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(a)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
--
1.6.2.4