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 <asm/apic.h> 9#include <asm/mpspec.h> 10#include <asm/pci_x86.h> 11#include <asm/numaq.h> 12 13#define BUS2QUAD(global) (mp_bus_id_to_node[global]) 14 15#define BUS2LOCAL(global) (mp_bus_id_to_local[global]) 16 17#define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local]) 18 19#define PCI_CONF1_MQ_ADDRESS(bus, devfn, reg) \ 20 (0x80000000 | (BUS2LOCAL(bus) << 16) | (devfn << 8) | (reg & ~3)) 21 22static void write_cf8(unsigned bus, unsigned devfn, unsigned reg) 23{ 24 unsigned val = PCI_CONF1_MQ_ADDRESS(bus, devfn, reg); 25 if (xquad_portio) 26 writel(val, XQUAD_PORT_ADDR(0xcf8, BUS2QUAD(bus))); 27 else 28 outl(val, 0xCF8); 29} 30 31static int pci_conf1_mq_read(unsigned int seg, unsigned int bus, 32 unsigned int devfn, int reg, int len, u32 *value) 33{ 34 unsigned long flags; 35 void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); 36 37 if (!value || (bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) 38 return -EINVAL; 39 40 raw_spin_lock_irqsave(&pci_config_lock, flags); 41 42 write_cf8(bus, devfn, reg); 43 44 switch (len) { 45 case 1: 46 if (xquad_portio) 47 *value = readb(adr + (reg & 3)); 48 else 49 *value = inb(0xCFC + (reg & 3)); 50 break; 51 case 2: 52 if (xquad_portio) 53 *value = readw(adr + (reg & 2)); 54 else 55 *value = inw(0xCFC + (reg & 2)); 56 break; 57 case 4: 58 if (xquad_portio) 59 *value = readl(adr); 60 else 61 *value = inl(0xCFC); 62 break; 63 } 64 65 raw_spin_unlock_irqrestore(&pci_config_lock, flags); 66 67 return 0; 68} 69 70static int pci_conf1_mq_write(unsigned int seg, unsigned int bus, 71 unsigned int devfn, int reg, int len, u32 value) 72{ 73 unsigned long flags; 74 void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); 75 76 if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) 77 return -EINVAL; 78 79 raw_spin_lock_irqsave(&pci_config_lock, flags); 80 81 write_cf8(bus, devfn, reg); 82 83 switch (len) { 84 case 1: 85 if (xquad_portio) 86 writeb(value, adr + (reg & 3)); 87 else 88 outb((u8)value, 0xCFC + (reg & 3)); 89 break; 90 case 2: 91 if (xquad_portio) 92 writew(value, adr + (reg & 2)); 93 else 94 outw((u16)value, 0xCFC + (reg & 2)); 95 break; 96 case 4: 97 if (xquad_portio) 98 writel(value, adr + reg); 99 else 100 outl((u32)value, 0xCFC); 101 break; 102 } 103 104 raw_spin_unlock_irqrestore(&pci_config_lock, flags); 105 106 return 0; 107} 108 109#undef PCI_CONF1_MQ_ADDRESS 110 111static struct pci_raw_ops pci_direct_conf1_mq = { 112 .read = pci_conf1_mq_read, 113 .write = pci_conf1_mq_write 114}; 115 116 117static void __devinit pci_fixup_i450nx(struct pci_dev *d) 118{ 119 /* 120 * i450NX -- Find and scan all secondary buses on all PXB's. 121 */ 122 int pxb, reg; 123 u8 busno, suba, subb; 124 int quad = BUS2QUAD(d->bus->number); 125 126 dev_info(&d->dev, "searching for i450NX host bridges\n"); 127 reg = 0xd0; 128 for(pxb=0; pxb<2; pxb++) { 129 pci_read_config_byte(d, reg++, &busno); 130 pci_read_config_byte(d, reg++, &suba); 131 pci_read_config_byte(d, reg++, &subb); 132 dev_dbg(&d->dev, "i450NX PXB %d: %02x/%02x/%02x\n", 133 pxb, busno, suba, subb); 134 if (busno) { 135 /* Bus A */ 136 pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, busno)); 137 } 138 if (suba < subb) { 139 /* Bus B */ 140 pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, suba+1)); 141 } 142 } 143 pcibios_last_bus = -1; 144} 145DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx); 146 147int __init pci_numaq_init(void) 148{ 149 int quad; 150 151 raw_pci_ops = &pci_direct_conf1_mq; 152 153 pci_root_bus = pcibios_scan_root(0); 154 if (pci_root_bus) 155 pci_bus_add_devices(pci_root_bus); 156 if (num_online_nodes() > 1) 157 for_each_online_node(quad) { 158 if (quad == 0) 159 continue; 160 printk("Scanning PCI bus %d for quad %d\n", 161 QUADLOCAL2BUS(quad,0), quad); 162 pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, 0)); 163 } 164 return 0; 165} 166