linux/arch/x86/pci/numaq_32.c
<<
>>
Prefs
   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