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