Merge git://git.kernel.org/pub/scm/linux/kernel/git/jk/spufs
[linux-2.6] / arch / x86 / pci / numa.c
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>
8 #include <mach_apic.h>
9 #include "pci.h"
10
11 #define XQUAD_PORTIO_BASE 0xfe400000
12 #define XQUAD_PORTIO_QUAD 0x40000  /* 256k per quad. */
13
14 int mp_bus_id_to_node[MAX_MP_BUSSES];
15 #define BUS2QUAD(global) (mp_bus_id_to_node[global])
16
17 int mp_bus_id_to_local[MAX_MP_BUSSES];
18 #define BUS2LOCAL(global) (mp_bus_id_to_local[global])
19
20 void mpc_oem_bus_info(struct mpc_config_bus *m, char *name,
21         struct mpc_config_translation *translation)
22 {
23         int quad = translation->trans_quad;
24         int local = translation->trans_local;
25
26         mp_bus_id_to_node[m->mpc_busid] = quad;
27         mp_bus_id_to_local[m->mpc_busid] = local;
28         printk(KERN_INFO "Bus #%d is %s (node %d)\n",
29                m->mpc_busid, name, quad);
30 }
31
32 int quad_local_to_mp_bus_id [NR_CPUS/4][4];
33 #define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local])
34 void mpc_oem_pci_bus(struct mpc_config_bus *m,
35         struct mpc_config_translation *translation)
36 {
37         int quad = translation->trans_quad;
38         int local = translation->trans_local;
39
40         quad_local_to_mp_bus_id[quad][local] = m->mpc_busid;
41 }
42
43 /* Where the IO area was mapped on multiquad, always 0 otherwise */
44 void *xquad_portio;
45 #ifdef CONFIG_X86_NUMAQ
46 EXPORT_SYMBOL(xquad_portio);
47 #endif
48
49 #define XQUAD_PORT_ADDR(port, quad) (xquad_portio + (XQUAD_PORTIO_QUAD*quad) + port)
50
51 #define PCI_CONF1_MQ_ADDRESS(bus, devfn, reg) \
52         (0x80000000 | (BUS2LOCAL(bus) << 16) | (devfn << 8) | (reg & ~3))
53
54 static void write_cf8(unsigned bus, unsigned devfn, unsigned reg)
55 {
56         unsigned val = PCI_CONF1_MQ_ADDRESS(bus, devfn, reg);
57         if (xquad_portio)
58                 writel(val, XQUAD_PORT_ADDR(0xcf8, BUS2QUAD(bus)));
59         else
60                 outl(val, 0xCF8);
61 }
62
63 static int pci_conf1_mq_read(unsigned int seg, unsigned int bus,
64                              unsigned int devfn, int reg, int len, u32 *value)
65 {
66         unsigned long flags;
67         void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus));
68
69         if (!value || (bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255))
70                 return -EINVAL;
71
72         spin_lock_irqsave(&pci_config_lock, flags);
73
74         write_cf8(bus, devfn, reg);
75
76         switch (len) {
77         case 1:
78                 if (xquad_portio)
79                         *value = readb(adr + (reg & 3));
80                 else
81                         *value = inb(0xCFC + (reg & 3));
82                 break;
83         case 2:
84                 if (xquad_portio)
85                         *value = readw(adr + (reg & 2));
86                 else
87                         *value = inw(0xCFC + (reg & 2));
88                 break;
89         case 4:
90                 if (xquad_portio)
91                         *value = readl(adr);
92                 else
93                         *value = inl(0xCFC);
94                 break;
95         }
96
97         spin_unlock_irqrestore(&pci_config_lock, flags);
98
99         return 0;
100 }
101
102 static int pci_conf1_mq_write(unsigned int seg, unsigned int bus,
103                               unsigned int devfn, int reg, int len, u32 value)
104 {
105         unsigned long flags;
106         void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus));
107
108         if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) 
109                 return -EINVAL;
110
111         spin_lock_irqsave(&pci_config_lock, flags);
112
113         write_cf8(bus, devfn, reg);
114
115         switch (len) {
116         case 1:
117                 if (xquad_portio)
118                         writeb(value, adr + (reg & 3));
119                 else
120                         outb((u8)value, 0xCFC + (reg & 3));
121                 break;
122         case 2:
123                 if (xquad_portio)
124                         writew(value, adr + (reg & 2));
125                 else
126                         outw((u16)value, 0xCFC + (reg & 2));
127                 break;
128         case 4:
129                 if (xquad_portio)
130                         writel(value, adr + reg);
131                 else
132                         outl((u32)value, 0xCFC);
133                 break;
134         }
135
136         spin_unlock_irqrestore(&pci_config_lock, flags);
137
138         return 0;
139 }
140
141 #undef PCI_CONF1_MQ_ADDRESS
142
143 static struct pci_raw_ops pci_direct_conf1_mq = {
144         .read   = pci_conf1_mq_read,
145         .write  = pci_conf1_mq_write
146 };
147
148
149 static void __devinit pci_fixup_i450nx(struct pci_dev *d)
150 {
151         /*
152          * i450NX -- Find and scan all secondary buses on all PXB's.
153          */
154         int pxb, reg;
155         u8 busno, suba, subb;
156         int quad = BUS2QUAD(d->bus->number);
157
158         printk("PCI: Searching for i450NX host bridges on %s\n", pci_name(d));
159         reg = 0xd0;
160         for(pxb=0; pxb<2; pxb++) {
161                 pci_read_config_byte(d, reg++, &busno);
162                 pci_read_config_byte(d, reg++, &suba);
163                 pci_read_config_byte(d, reg++, &subb);
164                 DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb);
165                 if (busno) {
166                         /* Bus A */
167                         pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, busno));
168                 }
169                 if (suba < subb) {
170                         /* Bus B */
171                         pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, suba+1));
172                 }
173         }
174         pcibios_last_bus = -1;
175 }
176 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx);
177
178 static int __init pci_numa_init(void)
179 {
180         int quad;
181
182         raw_pci_ops = &pci_direct_conf1_mq;
183
184         if (pcibios_scanned++)
185                 return 0;
186
187         pci_root_bus = pcibios_scan_root(0);
188         if (pci_root_bus)
189                 pci_bus_add_devices(pci_root_bus);
190         if (num_online_nodes() > 1)
191                 for_each_online_node(quad) {
192                         if (quad == 0)
193                                 continue;
194                         printk("Scanning PCI bus %d for quad %d\n", 
195                                 QUADLOCAL2BUS(quad,0), quad);
196                         pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, 0));
197                 }
198         return 0;
199 }
200
201 subsys_initcall(pci_numa_init);