Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * numa.c - Low-level PCI access for NUMA-Q machines | |
3 | */ | |
4 | ||
5 | #include <linux/pci.h> | |
6 | #include <linux/init.h> | |
7 | #include <linux/nodemask.h> | |
c7e844f0 | 8 | #include <mach_apic.h> |
d49c4288 | 9 | #include <asm/mpspec.h> |
1da177e4 LT |
10 | #include "pci.h" |
11 | ||
c7e844f0 AK |
12 | #define XQUAD_PORTIO_BASE 0xfe400000 |
13 | #define XQUAD_PORTIO_QUAD 0x40000 /* 256k per quad. */ | |
14 | ||
1da177e4 | 15 | #define BUS2QUAD(global) (mp_bus_id_to_node[global]) |
e129cb49 | 16 | |
1da177e4 | 17 | #define BUS2LOCAL(global) (mp_bus_id_to_local[global]) |
6079d2d5 | 18 | |
1da177e4 LT |
19 | #define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local]) |
20 | ||
4cedb334 GOC |
21 | /* Where the IO area was mapped on multiquad, always 0 otherwise */ |
22 | void *xquad_portio; | |
4cedb334 | 23 | EXPORT_SYMBOL(xquad_portio); |
4cedb334 | 24 | |
c7e844f0 AK |
25 | #define XQUAD_PORT_ADDR(port, quad) (xquad_portio + (XQUAD_PORTIO_QUAD*quad) + port) |
26 | ||
1da177e4 LT |
27 | #define PCI_CONF1_MQ_ADDRESS(bus, devfn, reg) \ |
28 | (0x80000000 | (BUS2LOCAL(bus) << 16) | (devfn << 8) | (reg & ~3)) | |
29 | ||
c7e844f0 AK |
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 | ||
1da177e4 LT |
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; | |
c7e844f0 | 43 | void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); |
1da177e4 LT |
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 | ||
c7e844f0 | 50 | write_cf8(bus, devfn, reg); |
1da177e4 LT |
51 | |
52 | switch (len) { | |
53 | case 1: | |
c7e844f0 AK |
54 | if (xquad_portio) |
55 | *value = readb(adr + (reg & 3)); | |
56 | else | |
57 | *value = inb(0xCFC + (reg & 3)); | |
1da177e4 LT |
58 | break; |
59 | case 2: | |
c7e844f0 AK |
60 | if (xquad_portio) |
61 | *value = readw(adr + (reg & 2)); | |
62 | else | |
63 | *value = inw(0xCFC + (reg & 2)); | |
1da177e4 LT |
64 | break; |
65 | case 4: | |
c7e844f0 AK |
66 | if (xquad_portio) |
67 | *value = readl(adr); | |
68 | else | |
69 | *value = inl(0xCFC); | |
1da177e4 LT |
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; | |
c7e844f0 | 82 | void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); |
1da177e4 LT |
83 | |
84 | if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) | |
85 | return -EINVAL; | |
86 | ||
87 | spin_lock_irqsave(&pci_config_lock, flags); | |
88 | ||
c7e844f0 | 89 | write_cf8(bus, devfn, reg); |
1da177e4 LT |
90 | |
91 | switch (len) { | |
92 | case 1: | |
c7e844f0 AK |
93 | if (xquad_portio) |
94 | writeb(value, adr + (reg & 3)); | |
95 | else | |
96 | outb((u8)value, 0xCFC + (reg & 3)); | |
1da177e4 LT |
97 | break; |
98 | case 2: | |
c7e844f0 AK |
99 | if (xquad_portio) |
100 | writew(value, adr + (reg & 2)); | |
101 | else | |
102 | outw((u16)value, 0xCFC + (reg & 2)); | |
1da177e4 LT |
103 | break; |
104 | case 4: | |
c7e844f0 AK |
105 | if (xquad_portio) |
106 | writel(value, adr + reg); | |
107 | else | |
108 | outl((u32)value, 0xCFC); | |
1da177e4 LT |
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 | ||
134 | printk("PCI: Searching for i450NX host bridges on %s\n", pci_name(d)); | |
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); | |
140 | DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); | |
73c59afc MBY |
141 | if (busno) { |
142 | /* Bus A */ | |
143 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, busno)); | |
144 | } | |
145 | if (suba < subb) { | |
146 | /* Bus B */ | |
147 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, suba+1)); | |
148 | } | |
1da177e4 LT |
149 | } |
150 | pcibios_last_bus = -1; | |
151 | } | |
152 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx); | |
153 | ||
154 | static int __init pci_numa_init(void) | |
155 | { | |
156 | int quad; | |
157 | ||
d49c4288 YL |
158 | if (!found_numaq) |
159 | return 0; | |
160 | ||
1da177e4 LT |
161 | raw_pci_ops = &pci_direct_conf1_mq; |
162 | ||
163 | if (pcibios_scanned++) | |
164 | return 0; | |
165 | ||
166 | pci_root_bus = pcibios_scan_root(0); | |
c431ada4 RS |
167 | if (pci_root_bus) |
168 | pci_bus_add_devices(pci_root_bus); | |
1da177e4 LT |
169 | if (num_online_nodes() > 1) |
170 | for_each_online_node(quad) { | |
171 | if (quad == 0) | |
172 | continue; | |
173 | printk("Scanning PCI bus %d for quad %d\n", | |
174 | QUADLOCAL2BUS(quad,0), quad); | |
73c59afc | 175 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, 0)); |
1da177e4 LT |
176 | } |
177 | return 0; | |
178 | } | |
179 | ||
180 | subsys_initcall(pci_numa_init); |