| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
| Robert Richter | e27cf3a | 2008-07-11 12:18:41 +0200 | [diff] [blame] | 2 | * numaq_32.c - Low-level PCI access for NUMA-Q machines | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3 | */ | 
|  | 4 |  | 
|  | 5 | #include <linux/pci.h> | 
|  | 6 | #include <linux/init.h> | 
|  | 7 | #include <linux/nodemask.h> | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 8 | #include <mach_apic.h> | 
| Yinghai Lu | d49c428 | 2008-06-08 18:31:54 -0700 | [diff] [blame] | 9 | #include <asm/mpspec.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 10 | #include "pci.h" | 
|  | 11 |  | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 12 | #define XQUAD_PORTIO_BASE 0xfe400000 | 
|  | 13 | #define XQUAD_PORTIO_QUAD 0x40000  /* 256k per quad. */ | 
|  | 14 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 15 | #define BUS2QUAD(global) (mp_bus_id_to_node[global]) | 
| Alexey Starikovskiy | e129cb4 | 2008-03-11 22:55:42 +0300 | [diff] [blame] | 16 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 17 | #define BUS2LOCAL(global) (mp_bus_id_to_local[global]) | 
| Alexey Starikovskiy | 6079d2d | 2008-03-11 19:45:48 +0300 | [diff] [blame] | 18 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 19 | #define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local]) | 
|  | 20 |  | 
| Glauber de Oliveira Costa | 4cedb33 | 2008-03-19 14:26:14 -0300 | [diff] [blame] | 21 | /* Where the IO area was mapped on multiquad, always 0 otherwise */ | 
|  | 22 | void *xquad_portio; | 
| Glauber de Oliveira Costa | 4cedb33 | 2008-03-19 14:26:14 -0300 | [diff] [blame] | 23 | EXPORT_SYMBOL(xquad_portio); | 
| Glauber de Oliveira Costa | 4cedb33 | 2008-03-19 14:26:14 -0300 | [diff] [blame] | 24 |  | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 25 | #define XQUAD_PORT_ADDR(port, quad) (xquad_portio + (XQUAD_PORTIO_QUAD*quad) + port) | 
|  | 26 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 27 | #define PCI_CONF1_MQ_ADDRESS(bus, devfn, reg) \ | 
|  | 28 | (0x80000000 | (BUS2LOCAL(bus) << 16) | (devfn << 8) | (reg & ~3)) | 
|  | 29 |  | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 30 | static void write_cf8(unsigned bus, unsigned devfn, unsigned reg) | 
|  | 31 | { | 
|  | 32 | unsigned val = PCI_CONF1_MQ_ADDRESS(bus, devfn, reg); | 
|  | 33 | if (xquad_portio) | 
|  | 34 | writel(val, XQUAD_PORT_ADDR(0xcf8, BUS2QUAD(bus))); | 
|  | 35 | else | 
|  | 36 | outl(val, 0xCF8); | 
|  | 37 | } | 
|  | 38 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 39 | static int pci_conf1_mq_read(unsigned int seg, unsigned int bus, | 
|  | 40 | unsigned int devfn, int reg, int len, u32 *value) | 
|  | 41 | { | 
|  | 42 | unsigned long flags; | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 43 | void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 |  | 
|  | 45 | if (!value || (bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) | 
|  | 46 | return -EINVAL; | 
|  | 47 |  | 
|  | 48 | spin_lock_irqsave(&pci_config_lock, flags); | 
|  | 49 |  | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 50 | write_cf8(bus, devfn, reg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 |  | 
|  | 52 | switch (len) { | 
|  | 53 | case 1: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 54 | if (xquad_portio) | 
|  | 55 | *value = readb(adr + (reg & 3)); | 
|  | 56 | else | 
|  | 57 | *value = inb(0xCFC + (reg & 3)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 58 | break; | 
|  | 59 | case 2: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 60 | if (xquad_portio) | 
|  | 61 | *value = readw(adr + (reg & 2)); | 
|  | 62 | else | 
|  | 63 | *value = inw(0xCFC + (reg & 2)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 64 | break; | 
|  | 65 | case 4: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 66 | if (xquad_portio) | 
|  | 67 | *value = readl(adr); | 
|  | 68 | else | 
|  | 69 | *value = inl(0xCFC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 70 | break; | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | spin_unlock_irqrestore(&pci_config_lock, flags); | 
|  | 74 |  | 
|  | 75 | return 0; | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | static int pci_conf1_mq_write(unsigned int seg, unsigned int bus, | 
|  | 79 | unsigned int devfn, int reg, int len, u32 value) | 
|  | 80 | { | 
|  | 81 | unsigned long flags; | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 82 | void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 |  | 
|  | 84 | if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) | 
|  | 85 | return -EINVAL; | 
|  | 86 |  | 
|  | 87 | spin_lock_irqsave(&pci_config_lock, flags); | 
|  | 88 |  | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 89 | write_cf8(bus, devfn, reg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 |  | 
|  | 91 | switch (len) { | 
|  | 92 | case 1: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 93 | if (xquad_portio) | 
|  | 94 | writeb(value, adr + (reg & 3)); | 
|  | 95 | else | 
|  | 96 | outb((u8)value, 0xCFC + (reg & 3)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 97 | break; | 
|  | 98 | case 2: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 99 | if (xquad_portio) | 
|  | 100 | writew(value, adr + (reg & 2)); | 
|  | 101 | else | 
|  | 102 | outw((u16)value, 0xCFC + (reg & 2)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 | break; | 
|  | 104 | case 4: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 105 | if (xquad_portio) | 
|  | 106 | writel(value, adr + reg); | 
|  | 107 | else | 
|  | 108 | outl((u32)value, 0xCFC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 109 | break; | 
|  | 110 | } | 
|  | 111 |  | 
|  | 112 | spin_unlock_irqrestore(&pci_config_lock, flags); | 
|  | 113 |  | 
|  | 114 | return 0; | 
|  | 115 | } | 
|  | 116 |  | 
|  | 117 | #undef PCI_CONF1_MQ_ADDRESS | 
|  | 118 |  | 
|  | 119 | static struct pci_raw_ops pci_direct_conf1_mq = { | 
|  | 120 | .read	= pci_conf1_mq_read, | 
|  | 121 | .write	= pci_conf1_mq_write | 
|  | 122 | }; | 
|  | 123 |  | 
|  | 124 |  | 
|  | 125 | static void __devinit pci_fixup_i450nx(struct pci_dev *d) | 
|  | 126 | { | 
|  | 127 | /* | 
|  | 128 | * i450NX -- Find and scan all secondary buses on all PXB's. | 
|  | 129 | */ | 
|  | 130 | int pxb, reg; | 
|  | 131 | u8 busno, suba, subb; | 
|  | 132 | int quad = BUS2QUAD(d->bus->number); | 
|  | 133 |  | 
| Bjorn Helgaas | 12c0b20 | 2008-07-23 17:00:13 -0600 | [diff] [blame] | 134 | dev_info(&d->dev, "searching for i450NX host bridges\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 135 | reg = 0xd0; | 
|  | 136 | for(pxb=0; pxb<2; pxb++) { | 
|  | 137 | pci_read_config_byte(d, reg++, &busno); | 
|  | 138 | pci_read_config_byte(d, reg++, &suba); | 
|  | 139 | pci_read_config_byte(d, reg++, &subb); | 
| Bjorn Helgaas | 12c0b20 | 2008-07-23 17:00:13 -0600 | [diff] [blame] | 140 | dev_dbg(&d->dev, "i450NX PXB %d: %02x/%02x/%02x\n", | 
|  | 141 | pxb, busno, suba, subb); | 
| Muli Ben-Yehuda | 73c59af | 2007-08-10 13:01:19 -0700 | [diff] [blame] | 142 | if (busno) { | 
|  | 143 | /* Bus A */ | 
|  | 144 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, busno)); | 
|  | 145 | } | 
|  | 146 | if (suba < subb) { | 
|  | 147 | /* Bus B */ | 
|  | 148 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, suba+1)); | 
|  | 149 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 150 | } | 
|  | 151 | pcibios_last_bus = -1; | 
|  | 152 | } | 
|  | 153 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx); | 
|  | 154 |  | 
| Robert Richter | e27cf3a | 2008-07-11 12:18:41 +0200 | [diff] [blame] | 155 | int __init pci_numaq_init(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 156 | { | 
|  | 157 | int quad; | 
|  | 158 |  | 
| Yinghai Lu | d49c428 | 2008-06-08 18:31:54 -0700 | [diff] [blame] | 159 | if (!found_numaq) | 
|  | 160 | return 0; | 
|  | 161 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 162 | raw_pci_ops = &pci_direct_conf1_mq; | 
|  | 163 |  | 
|  | 164 | if (pcibios_scanned++) | 
|  | 165 | return 0; | 
|  | 166 |  | 
|  | 167 | pci_root_bus = pcibios_scan_root(0); | 
| Rajesh Shah | c431ada | 2005-04-28 00:25:45 -0700 | [diff] [blame] | 168 | if (pci_root_bus) | 
|  | 169 | pci_bus_add_devices(pci_root_bus); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 170 | if (num_online_nodes() > 1) | 
|  | 171 | for_each_online_node(quad) { | 
|  | 172 | if (quad == 0) | 
|  | 173 | continue; | 
|  | 174 | printk("Scanning PCI bus %d for quad %d\n", | 
|  | 175 | QUADLOCAL2BUS(quad,0), quad); | 
| Muli Ben-Yehuda | 73c59af | 2007-08-10 13:01:19 -0700 | [diff] [blame] | 176 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, 0)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 177 | } | 
|  | 178 | return 0; | 
|  | 179 | } |