| 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> | 
| Ingo Molnar | 7b6aa33 | 2009-02-17 13:58:15 +0100 | [diff] [blame] | 8 | #include <asm/apic.h> | 
| Yinghai Lu | d49c428 | 2008-06-08 18:31:54 -0700 | [diff] [blame] | 9 | #include <asm/mpspec.h> | 
| Jaswinder Singh Rajput | 8248771 | 2008-12-27 18:32:28 +0530 | [diff] [blame] | 10 | #include <asm/pci_x86.h> | 
| Brian Gerst | 5c64c70 | 2010-02-05 09:37:03 -0500 | [diff] [blame] | 11 | #include <asm/numaq.h> | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 12 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 | #define BUS2QUAD(global) (mp_bus_id_to_node[global]) | 
| Alexey Starikovskiy | e129cb4 | 2008-03-11 22:55:42 +0300 | [diff] [blame] | 14 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 15 | #define BUS2LOCAL(global) (mp_bus_id_to_local[global]) | 
| Alexey Starikovskiy | 6079d2d | 2008-03-11 19:45:48 +0300 | [diff] [blame] | 16 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 22 | static 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 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | static 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; | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 35 | void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 |  | 
| Jan Beulich | db34a36 | 2011-07-22 08:13:05 +0100 | [diff] [blame] | 37 | WARN_ON(seg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 38 | if (!value || (bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) | 
|  | 39 | return -EINVAL; | 
|  | 40 |  | 
| Thomas Gleixner | d19f61f | 2010-02-17 14:35:25 +0000 | [diff] [blame] | 41 | raw_spin_lock_irqsave(&pci_config_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 42 |  | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 43 | write_cf8(bus, devfn, reg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 |  | 
|  | 45 | switch (len) { | 
|  | 46 | case 1: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 47 | if (xquad_portio) | 
|  | 48 | *value = readb(adr + (reg & 3)); | 
|  | 49 | else | 
|  | 50 | *value = inb(0xCFC + (reg & 3)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 | break; | 
|  | 52 | case 2: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 53 | if (xquad_portio) | 
|  | 54 | *value = readw(adr + (reg & 2)); | 
|  | 55 | else | 
|  | 56 | *value = inw(0xCFC + (reg & 2)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 57 | break; | 
|  | 58 | case 4: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 59 | if (xquad_portio) | 
|  | 60 | *value = readl(adr); | 
|  | 61 | else | 
|  | 62 | *value = inl(0xCFC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 63 | break; | 
|  | 64 | } | 
|  | 65 |  | 
| Thomas Gleixner | d19f61f | 2010-02-17 14:35:25 +0000 | [diff] [blame] | 66 | raw_spin_unlock_irqrestore(&pci_config_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 |  | 
|  | 68 | return 0; | 
|  | 69 | } | 
|  | 70 |  | 
|  | 71 | static int pci_conf1_mq_write(unsigned int seg, unsigned int bus, | 
|  | 72 | unsigned int devfn, int reg, int len, u32 value) | 
|  | 73 | { | 
|  | 74 | unsigned long flags; | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 75 | void *adr __iomem = XQUAD_PORT_ADDR(0xcfc, BUS2QUAD(bus)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 76 |  | 
| Jan Beulich | db34a36 | 2011-07-22 08:13:05 +0100 | [diff] [blame] | 77 | WARN_ON(seg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255)) | 
|  | 79 | return -EINVAL; | 
|  | 80 |  | 
| Thomas Gleixner | d19f61f | 2010-02-17 14:35:25 +0000 | [diff] [blame] | 81 | raw_spin_lock_irqsave(&pci_config_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 82 |  | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 83 | write_cf8(bus, devfn, reg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 |  | 
|  | 85 | switch (len) { | 
|  | 86 | case 1: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 87 | if (xquad_portio) | 
|  | 88 | writeb(value, adr + (reg & 3)); | 
|  | 89 | else | 
|  | 90 | outb((u8)value, 0xCFC + (reg & 3)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 91 | break; | 
|  | 92 | case 2: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 93 | if (xquad_portio) | 
|  | 94 | writew(value, adr + (reg & 2)); | 
|  | 95 | else | 
|  | 96 | outw((u16)value, 0xCFC + (reg & 2)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 97 | break; | 
|  | 98 | case 4: | 
| Andi Kleen | c7e844f | 2008-02-04 16:48:03 +0100 | [diff] [blame] | 99 | if (xquad_portio) | 
|  | 100 | writel(value, adr + reg); | 
|  | 101 | else | 
|  | 102 | outl((u32)value, 0xCFC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 | break; | 
|  | 104 | } | 
|  | 105 |  | 
| Thomas Gleixner | d19f61f | 2010-02-17 14:35:25 +0000 | [diff] [blame] | 106 | raw_spin_unlock_irqrestore(&pci_config_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 107 |  | 
|  | 108 | return 0; | 
|  | 109 | } | 
|  | 110 |  | 
|  | 111 | #undef PCI_CONF1_MQ_ADDRESS | 
|  | 112 |  | 
| Jan Beulich | 72da0b0 | 2011-09-15 08:58:51 +0100 | [diff] [blame] | 113 | static const struct pci_raw_ops pci_direct_conf1_mq = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 114 | .read	= pci_conf1_mq_read, | 
|  | 115 | .write	= pci_conf1_mq_write | 
|  | 116 | }; | 
|  | 117 |  | 
|  | 118 |  | 
|  | 119 | static void __devinit pci_fixup_i450nx(struct pci_dev *d) | 
|  | 120 | { | 
|  | 121 | /* | 
|  | 122 | * i450NX -- Find and scan all secondary buses on all PXB's. | 
|  | 123 | */ | 
|  | 124 | int pxb, reg; | 
|  | 125 | u8 busno, suba, subb; | 
|  | 126 | int quad = BUS2QUAD(d->bus->number); | 
|  | 127 |  | 
| Bjorn Helgaas | 12c0b20 | 2008-07-23 17:00:13 -0600 | [diff] [blame] | 128 | dev_info(&d->dev, "searching for i450NX host bridges\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 129 | reg = 0xd0; | 
|  | 130 | for(pxb=0; pxb<2; pxb++) { | 
|  | 131 | pci_read_config_byte(d, reg++, &busno); | 
|  | 132 | pci_read_config_byte(d, reg++, &suba); | 
|  | 133 | pci_read_config_byte(d, reg++, &subb); | 
| Bjorn Helgaas | 12c0b20 | 2008-07-23 17:00:13 -0600 | [diff] [blame] | 134 | dev_dbg(&d->dev, "i450NX PXB %d: %02x/%02x/%02x\n", | 
|  | 135 | pxb, busno, suba, subb); | 
| Muli Ben-Yehuda | 73c59af | 2007-08-10 13:01:19 -0700 | [diff] [blame] | 136 | if (busno) { | 
|  | 137 | /* Bus A */ | 
|  | 138 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, busno)); | 
|  | 139 | } | 
|  | 140 | if (suba < subb) { | 
|  | 141 | /* Bus B */ | 
|  | 142 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, suba+1)); | 
|  | 143 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 144 | } | 
|  | 145 | pcibios_last_bus = -1; | 
|  | 146 | } | 
|  | 147 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx); | 
|  | 148 |  | 
| Robert Richter | e27cf3a | 2008-07-11 12:18:41 +0200 | [diff] [blame] | 149 | int __init pci_numaq_init(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 150 | { | 
|  | 151 | int quad; | 
|  | 152 |  | 
|  | 153 | raw_pci_ops = &pci_direct_conf1_mq; | 
|  | 154 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 155 | pci_root_bus = pcibios_scan_root(0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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); | 
| Muli Ben-Yehuda | 73c59af | 2007-08-10 13:01:19 -0700 | [diff] [blame] | 162 | pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, 0)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 163 | } | 
|  | 164 | return 0; | 
|  | 165 | } |