mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 00:48:50 +00:00
c431ada45d
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>
132 lines
3.2 KiB
C
132 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);
|