1
linux/arch/i386/pci/numa.c
Rajesh Shah c431ada45d [PATCH] acpi bridge hotadd: ACPI based root bridge hot-add
When you hot-plug a (root) bridge hierarchy, it may have p2p bridges and
devices attached to it that have not been configured by firmware.  In this
case, we need to configure the devices before starting them.  This patch
separates device start from device scan so that we can introduce the
configuration step in the middle.

I kept the existing semantics for pci_scan_bus() since there are a huge number
of callers to that function.

Also, I have no way of testing the changes I made to the parisc files, so this
needs review by those folks.  Sorry for the massive cross-post, this touches
files in many different places.

Signed-off-by: Rajesh Shah <rajesh.shah@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-06-27 21:52:39 -07:00

133 lines
3.2 KiB
C

/*
* numa.c - Low-level PCI access for NUMA-Q machines
*/
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/nodemask.h>
#include "pci.h"
#define BUS2QUAD(global) (mp_bus_id_to_node[global])
#define BUS2LOCAL(global) (mp_bus_id_to_local[global])
#define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local])
#define PCI_CONF1_MQ_ADDRESS(bus, devfn, reg) \
(0x80000000 | (BUS2LOCAL(bus) << 16) | (devfn << 8) | (reg & ~3))
static int pci_conf1_mq_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value)
{
unsigned long flags;
if (!value || (bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
outl_quad(PCI_CONF1_MQ_ADDRESS(bus, devfn, reg), 0xCF8, BUS2QUAD(bus));
switch (len) {
case 1:
*value = inb_quad(0xCFC + (reg & 3), BUS2QUAD(bus));
break;
case 2:
*value = inw_quad(0xCFC + (reg & 2), BUS2QUAD(bus));
break;
case 4:
*value = inl_quad(0xCFC, BUS2QUAD(bus));
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
static int pci_conf1_mq_write(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 value)
{
unsigned long flags;
if ((bus >= MAX_MP_BUSSES) || (devfn > 255) || (reg > 255))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
outl_quad(PCI_CONF1_MQ_ADDRESS(bus, devfn, reg), 0xCF8, BUS2QUAD(bus));
switch (len) {
case 1:
outb_quad((u8)value, 0xCFC + (reg & 3), BUS2QUAD(bus));
break;
case 2:
outw_quad((u16)value, 0xCFC + (reg & 2), BUS2QUAD(bus));
break;
case 4:
outl_quad((u32)value, 0xCFC, BUS2QUAD(bus));
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
#undef PCI_CONF1_MQ_ADDRESS
static struct pci_raw_ops pci_direct_conf1_mq = {
.read = pci_conf1_mq_read,
.write = pci_conf1_mq_write
};
static void __devinit pci_fixup_i450nx(struct pci_dev *d)
{
/*
* i450NX -- Find and scan all secondary buses on all PXB's.
*/
int pxb, reg;
u8 busno, suba, subb;
int quad = BUS2QUAD(d->bus->number);
printk("PCI: Searching for i450NX host bridges on %s\n", pci_name(d));
reg = 0xd0;
for(pxb=0; pxb<2; pxb++) {
pci_read_config_byte(d, reg++, &busno);
pci_read_config_byte(d, reg++, &suba);
pci_read_config_byte(d, reg++, &subb);
DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb);
if (busno)
pci_scan_bus(QUADLOCAL2BUS(quad,busno), &pci_root_ops, NULL); /* Bus A */
if (suba < subb)
pci_scan_bus(QUADLOCAL2BUS(quad,suba+1), &pci_root_ops, NULL); /* Bus B */
}
pcibios_last_bus = -1;
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx);
static int __init pci_numa_init(void)
{
int quad;
raw_pci_ops = &pci_direct_conf1_mq;
if (pcibios_scanned++)
return 0;
pci_root_bus = pcibios_scan_root(0);
if (pci_root_bus)
pci_bus_add_devices(pci_root_bus);
if (num_online_nodes() > 1)
for_each_online_node(quad) {
if (quad == 0)
continue;
printk("Scanning PCI bus %d for quad %d\n",
QUADLOCAL2BUS(quad,0), quad);
pci_scan_bus(QUADLOCAL2BUS(quad,0),
&pci_root_ops, NULL);
}
return 0;
}
subsys_initcall(pci_numa_init);