Subject: Re: [PATCH] Add of_platform_device_scan().

From: Arnd Bergmann <[email-masked]>
Date: Wed, 4 Oct 2006 01:18:09 +0200

On Wednesday 04 October 2006 00:56, Scott Wood wrote:
> This patch adds of_platform_device_scan(), which adds matching device
> nodes to the of_platform bus.  The goal is to let drivers reuse the match
> struct to do this, rather than have special code in fsl_soc.c (or
> equivalent) that knows which nodes to add.
> 
> This will be used by the ucc_geth.c driver; in the current QE patchset,
> it registers an of_platform driver for devices that never get registered.
> 

I think it would be much more helpful to have working probing of all
SOC buses during bootup. With your patch, we don't have the ability
to autoload modules based on the available devices, but can only
see devices that have been explicitly registered. This fundamentally
violates the principles of the linux device model.

Incidentally, I've experimented last week with doing exactly what
I think we should have. It doesn't yet do the thing I want it for
(allow PCI buses below SOC buses), but it should be able to scan
nested soc buses fine and thereby add all your devices to the
tree so you can attach drivers to them.

	Arnd <><

Index: linux-2.6/arch/powerpc/kernel/Makefile
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/Makefile
+++ linux-2.6/arch/powerpc/kernel/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_TAU)		+= tau_6xx.o
 obj32-$(CONFIG_SOFTWARE_SUSPEND) += swsusp_32.o
 obj32-$(CONFIG_MODULES)		+= module_32.o
 obj-$(CONFIG_E500)		+= perfmon_fsl_booke.o
+obj-$(CONFIG_SOC)		+= soc.o
 
 ifeq ($(CONFIG_PPC_MERGE),y)
 
Index: linux-2.6/arch/powerpc/kernel/soc.c
===================================================================
--- /dev/null
+++ linux-2.6/arch/powerpc/kernel/soc.c
@@ -0,0 +1,152 @@
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/prom.h>
+#include <asm/of_device.h>
+#include <asm/ppc-pci.h>
+#include <asm-powerpc/axon_dcr.h>
+
+static int of_soc_create_device_name(struct device_node *node, char *name)
+{
+	const u32 *reg;
+	u64 addr;
+
+	reg = get_property(node, "dcr-reg", NULL);
+	if (reg) {
+		addr = of_translate_dcr_address(node, reg);
+		if (addr != OF_BAD_ADDR)
+			goto found_dcr;
+	}
+	reg = get_property(node, "reg", NULL);
+	if (reg) {
+		addr = of_translate_address(node, reg);
+		if (addr != OF_BAD_ADDR)
+			goto found;
+	}
+
+	return -EINVAL;
+
+found_dcr:
+	sprintf(name, "D%lx.%s", addr, node->name);
+	return 0;
+
+found:
+	sprintf(name, "%lx.%s", addr, node->name);
+	return 0;
+}
+
+static int of_soc_device_create(struct device_node *node, struct device *parent)
+{
+	char name[BUS_ID_SIZE];
+	int ret;
+
+	printk("found node %s\n", node->name);
+	ret = of_soc_create_device_name(node, name);
+	if (ret)
+		return ret;
+
+	of_platform_device_create(node, name, parent);
+	return 0;
+}
+
+static int of_soc_bus_probe(struct of_device *dev,
+			const struct of_device_id *match)
+{
+	struct device_node *child;
+	int ret = -ENODEV;
+	for (child = NULL; (child = of_get_next_child(dev->node, child)); ) {
+		ret = of_soc_device_create(child, &dev->dev);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
+static struct of_device_id of_soc_ids[] = {
+	{ .type = "soc", },
+	{ .compatible = "soc", },
+	{ .type = "spider", },
+	{ .type = "axon", },
+	{ .type = "plb5", },
+	{ .type = "plb4", },
+	{ .type = "opb", },
+	{},
+};
+
+static struct of_platform_driver of_soc_driver = {
+	.name = "soc",
+	.match_table = of_soc_ids,
+	.probe = of_soc_bus_probe,
+};
+
+#if 0
+static int of_pci_bus_probe(struct of_device *dev,
+			const struct of_device_id *match)
+{
+	struct pci_controller *phb;
+
+	phb = pcibios_alloc_controller(dev->node);
+	if (!phb)
+		return -ENODEV;
+	setup_phb(dev->node, phb);
+	pci_process_bridge_OF_ranges(phb, dev->node, 0);
+	pci_setup_phb_io(phb, 0);
+	return 0;
+}
+
+static struct of_device_id of_pci_ids[] = {
+	{ .type = "pci", },
+	{ .type = "pcie", },
+	{ .type = "pciex", },
+	{ .type = "ht", },
+	{ .compatible = "pci", },
+	{ .compatible = "pcie", },
+	{ .compatible = "pciex", },
+	{ .compatible = "ht", },
+};
+
+static struct of_platform_driver of_pci_driver = {
+	.name = "pci",
+	.match_table = of_pci_ids,
+	.probe = of_pci_bus_probe,
+};
+#endif
+
+static int of_soc_probe(void)
+{
+	struct device_node *root, *child;
+	int ret;
+
+	ret = 0;
+	root = of_find_node_by_path("/");
+	if (!root)
+		goto out;
+	ret = of_register_driver(&of_soc_driver);
+	if (ret)
+		goto out;
+
+#if 0
+	ret = of_register_driver(&of_pci_driver);
+	if (ret)
+		goto out2;
+#endif
+
+	for (child = NULL; (child = of_get_next_child(root, child)); ) {
+		if (strcmp(child->type, "spider") == 0) {
+			ret = of_soc_device_create(child, NULL);
+			if (ret)
+				goto out3;
+		}
+	}
+
+	return 0;
+out3:
+	of_unregister_driver(&of_soc_driver);
+out2:
+	of_unregister_driver(&of_pci_driver);
+out:
+	return ret;
+}
+__initcall(of_soc_probe);
Index: linux-2.6/arch/powerpc/Kconfig
===================================================================
--- linux-2.6.orig/arch/powerpc/Kconfig
+++ linux-2.6/arch/powerpc/Kconfig
@@ -450,6 +450,7 @@ config PPC_IBM_CELL_BLADE
 	select MMIO_NVRAM
 	select PPC_UDBG_16550
 	select UDBG_RTAS_CONSOLE
+	select SOC
 
 config UDBG_RTAS_CONSOLE
 	bool "RTAS based debug console"
@@ -544,6 +545,13 @@ config PPC_970_NAP
 	bool
 	default n
 
+config SOC
+	bool
+	default n
+	help
+	  Support for probing of SOC (system-on-a-chip) buses from
+	  the device tree
+
 source "drivers/cpufreq/Kconfig"
 
 config CPU_FREQ_PMAC