I tried to get PCI probing work in cases where the devices are behind multiple bridges. This patch works in many cases (especially current QEMU PPC/Sparc64 setup), but not if I make some arbitrary changes to the bridging setup.
The mysterious "?active-package get-package-path" piece resolves the simple /pci/pci path (which would be identical to any other bridge found) to /pci@1fe0,0/pci@1,1. Is there a better way?
Other comments?
Signed-off-by: Blue Swirl blauwirbel@gmail.com --- drivers/pci.c | 72 +++++++++++++++++++++++++++++++++++++++++++------------- drivers/pci.h | 2 + 2 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/drivers/pci.c b/drivers/pci.c index 7d681f1..293509b 100644 --- a/drivers/pci.c +++ b/drivers/pci.c @@ -877,8 +877,15 @@ ob_pci_configure(pci_addr addr, pci_config_t *config, int num_regs, int rom_bar, pci_config_write16(addr, PCI_COMMAND, cmd); }
-static void ob_scan_pci_bus(int bus, unsigned long *mem_base, - unsigned long *io_base, char **path) +static struct ob_buses{ + char *path; + pci_addr addr; +} ob_buses[256]; + +static int buses, found_host; + +static int ob_scan_pci_bus(int bus, unsigned long *mem_base, + unsigned long *io_base) { int devnum, fn, is_multi, vid, did; unsigned int htype; @@ -887,8 +894,9 @@ static void ob_scan_pci_bus(int bus, unsigned long *mem_base, const pci_dev_t *pci_dev; uint32_t ccode; uint8_t class, subclass, iface, rev; - int num_bars, rom_bar; + int num_bars, rom_bar, child_buses;
+ child_buses = 0; activate_device("/"); for (devnum = 0; devnum < 32; devnum++) { is_multi = 0; @@ -923,10 +931,10 @@ static void ob_scan_pci_bus(int bus, unsigned long *mem_base,
if (pci_dev == NULL || pci_dev->name == NULL) snprintf(config.path, sizeof(config.path), - "%s/pci%x,%x", *path, vid, did); + "%s/pci%x,%x", ob_buses[bus].path, vid, did); else snprintf(config.path, sizeof(config.path), - "%s/%s", *path, pci_dev->name); + "%s/%s", ob_buses[bus].path, pci_dev->name); #ifdef CONFIG_DEBUG_PCI printk("%s - ", config.path); #endif @@ -957,37 +965,67 @@ static void ob_scan_pci_bus(int bus, unsigned long *mem_base, (subclass == PCI_SUBCLASS_BRIDGE_HOST || subclass == PCI_SUBCLASS_BRIDGE_PCI)) { /* host or bridge */ - free(*path); - *path = strdup(config.path); + fword("?active-package"); + fword("get-package-path"); + ob_buses[buses].path = pop_fstr_copy(); + ob_buses[buses].addr = addr; + pci_config_write8(addr, PCI_PRIMARY_BUS, bus); + pci_config_write8(addr, PCI_SUBORDINATE_BUS, + 255); + /* we assume the first device found is + the host */ + if (!found_host) { + pci_config_write8(addr, + PCI_SECONDARY_BUS, + buses + 1); + } else { + pci_config_write8(addr, + PCI_SECONDARY_BUS, + buses); + } + buses++; + child_buses++; + if (!found_host) { + found_host = 1; + } else { + int ret; + + ret = ob_scan_pci_bus(buses - 1, + mem_base, + io_base); + pci_config_write8(addr, + PCI_SUBORDINATE_BUS, + buses - 1 + ret); + child_buses += ret; + } }
} } device_end(); + return child_buses; }
+/* Recursively scan PCI bus, starting from host bus */ int ob_pci_init(void) { int bus; unsigned long mem_base, io_base; - char *path;
#ifdef CONFIG_DEBUG_PCI printk("Initializing PCI devices...\n"); #endif
- /* brute force bus scan */ - - /* Find all PCI bridges */ - mem_base = arch->mem_base; /* I/O ports under 0x400 are used by devices mapped at fixed location. */ io_base = arch->io_base + 0x400; - path = strdup(""); - for (bus = 0; bus<0x100; bus++) { - ob_scan_pci_bus(bus, &mem_base, &io_base, &path); - } - free(path); + + ob_buses[0].path = strdup(""); + ob_scan_pci_bus(0, &mem_base, &io_base); + + for (bus = 0; bus < buses; bus++) { + free(ob_buses[bus].path); + } return 0; } diff --git a/drivers/pci.h b/drivers/pci.h index 3ca71f3..fc8968f 100644 --- a/drivers/pci.h +++ b/drivers/pci.h @@ -34,7 +34,9 @@ #define PCI_HEADER_TYPE_NORMAL 0x00 #define PCI_HEADER_TYPE_BRIDGE 0x01 #define PCI_HEADER_TYPE_CARDBUS 0x02 +#define PCI_PRIMARY_BUS 0x18 #define PCI_SECONDARY_BUS 0x19 +#define PCI_SUBORDINATE_BUS 0x1a #define PCI_BASE_ADDR_0 0x10 #define PCI_BASE_ADDR_1 0x14 #define PCI_BASE_ADDR_2 0x18