From e3e857bd42ff4d767849b2539a0c244d8cb52f60 Mon Sep 17 00:00:00 2001
From: Blue Swirl <blauwirbel@gmail.com>
Date: Sun, 7 Feb 2010 16:06:41 +0000
Subject: [PATCH] Probe PCI buses recursively

Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
---
 drivers/pci.c |   75 ++++++++++++++++++++++++++++++++++++++++++--------------
 drivers/pci.h |    2 +
 2 files changed, 58 insertions(+), 19 deletions(-)

diff --git a/drivers/pci.c b/drivers/pci.c
index fefb1d8..222aec4 100644
--- a/drivers/pci.c
+++ b/drivers/pci.c
@@ -32,6 +32,7 @@
 #include "macio.h"
 #endif
 
+#define CONFIG_DEBUG_PCI
 #define set_bool_property(ph, name) set_property(ph, name, NULL, 0);
 
 /* DECLARE data structures for the nodes.  */
@@ -876,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{
+        phandle_t ph;
+        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;
@@ -886,9 +894,11 @@ 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;
         phandle_t ph;
+        const char *parent_path;
 
+        child_buses = 0;
 	activate_device("/");
 	for (devnum = 0; devnum < 32; devnum++) {
 		is_multi = 0;
@@ -921,17 +931,20 @@ static void ob_scan_pci_bus(int bus, unsigned long *mem_base,
 			if (fn == 0)
 				is_multi = htype & 0x80;
 
+                        PUSH_ph(ob_buses[bus].ph);
+                        fword("get-package-path");
+                        parent_path = pop_fstr_copy();
+
 			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", parent_path, vid, did);
 			else
                             snprintf(config.path, sizeof(config.path),
-				     "%s/%s", *path, pci_dev->name);
+                                     "%s/%s", parent_path, pci_dev->name);
 #ifdef CONFIG_DEBUG_PCI
 			printk("%s - ", config.path);
 #endif
 			config.dev = addr & 0x00FFFFFF;
-
                         if (class == PCI_BASE_CLASS_BRIDGE &&
                             (subclass == PCI_SUBCLASS_BRIDGE_HOST ||
                              subclass == PCI_SUBCLASS_BRIDGE_PCI))
@@ -959,37 +972,61 @@ 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);
+                                ob_buses[buses].ph = ph;
+                                ob_buses[buses].addr = addr;
+                                pci_config_write8(addr, PCI_PRIMARY_BUS, bus);
+                                pci_config_write8(addr, PCI_SUBORDINATE_BUS,
+                                                  0xff);
+                                /* 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].ph = find_dev("/");
+        ob_scan_pci_bus(0, &mem_base, &io_base);
+
 	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.5.6.5

