diff options
Diffstat (limited to 'arch/x86/pci/numaq_32.c')
-rw-r--r-- | arch/x86/pci/numaq_32.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/arch/x86/pci/numaq_32.c b/arch/x86/pci/numaq_32.c new file mode 100644 index 000000000000..f4b16dc11dad --- /dev/null +++ b/arch/x86/pci/numaq_32.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | * numaq_32.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> | ||
8 | #include <mach_apic.h> | ||
9 | #include <asm/mpspec.h> | ||
10 | #include "pci.h" | ||
11 | |||
12 | #define XQUAD_PORTIO_BASE 0xfe400000 | ||
13 | #define XQUAD_PORTIO_QUAD 0x40000 /* 256k per quad. */ | ||
14 | |||
15 | #define BUS2QUAD(global) (mp_bus_id_to_node[global]) | ||
16 | |||
17 | #define BUS2LOCAL(global) (mp_bus_id_to_local[global]) | ||
18 | |||
19 | #define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local]) | ||
20 | |||
21 | /* Where the IO area was mapped on multiquad, always 0 otherwise */ | ||
22 | void *xquad_portio; | ||
23 | EXPORT_SYMBOL(xquad_portio); | ||
24 | |||
25 | #define XQUAD_PORT_ADDR(port, quad) (xquad_portio + (XQUAD_PORTIO_QUAD*quad) + port) | ||
26 | |||
27 | #define PCI_CONF1_MQ_ADDRESS(bus, devfn, reg) \ | ||
28 | (0x80000000 | (BUS2LOCAL(bus) << 16) | (devfn << 8) | (reg & ~3)) | ||
29 | |||
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 | |||
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; | ||
43 | void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); | ||
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 | |||
50 | write_cf8(bus, devfn, reg); | ||
51 | |||
52 | switch (len) { | ||
53 | case 1: | ||
54 | if (xquad_portio) | ||
55 | *value = readb(adr + (reg & 3)); | ||
56 | else | ||
57 | *value = inb(0xCFC + (reg & 3)); | ||
58 | break; | ||
59 | case 2: | ||
60 | if (xquad_portio) | ||
61 | *value = readw(adr + (reg & 2)); | ||
62 | else | ||
63 | *value = inw(0xCFC + (reg & 2)); | ||
64 | break; | ||
65 | case 4: | ||
66 | if (xquad_portio) | ||
67 | *value = readl(adr); | ||
68 | else | ||
69 | *value = inl(0xCFC); | ||
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; | ||
82 | void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); | ||
83 | |||
84 | if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) | ||
85 | return -EINVAL; | ||
86 | |||
87 | spin_lock_irqsave(&pci_config_lock, flags); | ||
88 | |||
89 | write_cf8(bus, devfn, reg); | ||
90 | |||
91 | switch (len) { | ||
92 | case 1: | ||
93 | if (xquad_portio) | ||
94 | writeb(value, adr + (reg & 3)); | ||
95 | else | ||
96 | outb((u8)value, 0xCFC + (reg & 3)); | ||
97 | break; | ||
98 | case 2: | ||
99 | if (xquad_portio) | ||
100 | writew(value, adr + (reg & 2)); | ||
101 | else | ||
102 | outw((u16)value, 0xCFC + (reg & 2)); | ||
103 | break; | ||
104 | case 4: | ||
105 | if (xquad_portio) | ||
106 | writel(value, adr + reg); | ||
107 | else | ||
108 | outl((u32)value, 0xCFC); | ||
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); | ||
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 | } | ||
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 | int __init pci_numaq_init(void) | ||
155 | { | ||
156 | int quad; | ||
157 | |||
158 | if (!found_numaq) | ||
159 | return 0; | ||
160 | |||
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); | ||
167 | if (pci_root_bus) | ||
168 | pci_bus_add_devices(pci_root_bus); | ||
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); | ||
175 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, 0)); | ||
176 | } | ||
177 | return 0; | ||
178 | } | ||