dm: Provide a function to scan child FDT nodes
authorSimon Glass <sjg@chromium.org>
Wed, 23 Jul 2014 12:55:18 +0000 (06:55 -0600)
committerSimon Glass <sjg@chromium.org>
Wed, 23 Jul 2014 13:08:36 +0000 (14:08 +0100)
At present only root nodes in the device tree are scanned for devices.
But some devices can have children. For example a SPI bus may have
several children for each of its chip selects.

Add a function which scans subnodes and binds devices for each one. This
can be used for the root node scan also, so change it.

A device can call this function in its bind() or probe() methods to bind
its children.

Signed-off-by: Simon Glass <sjg@chromium.org>
doc/driver-model/README.txt
drivers/core/root.c
include/dm/root.h
include/dm/test.h
include/dm/uclass-id.h
test/dm/Makefile
test/dm/bus.c [new file with mode: 0644]
test/dm/test-fdt.c
test/dm/test.dts

index 672497d4822d63db88e5162ca914bbb3b1067668..a2b6122ebcb3ac7be8285a218d5ad61dd83c6798 100644 (file)
@@ -95,9 +95,13 @@ are provided in test/dm. To run them, try:
 You should see something like this:
 
     <...U-Boot banner...>
-    Running 17 driver model tests
+    Running 18 driver model tests
     Test: dm_test_autobind
     Test: dm_test_autoprobe
+    Test: dm_test_bus_children
+    Device 'd-test': seq 3 is in use by 'b-test'
+    Device 'c-test@0': seq 0 is in use by 'a-test'
+    Device 'c-test@1': seq 1 is in use by 'd-test'
     Test: dm_test_children
     Test: dm_test_fdt
     Device 'd-test': seq 3 is in use by 'b-test'
index 60597563d6c42b0f1945363ed647ca981e6d9ace..4f9c7e708ae4153c0f4f323ea6d40029bd7ba02b 100644 (file)
@@ -80,29 +80,32 @@ int dm_scan_platdata(bool pre_reloc_only)
 }
 
 #ifdef CONFIG_OF_CONTROL
-int dm_scan_fdt(const void *blob, bool pre_reloc_only)
+int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset,
+                    bool pre_reloc_only)
 {
-       int offset = 0;
        int ret = 0, err;
-       int depth = 0;
-
-       do {
-               offset = fdt_next_node(blob, offset, &depth);
-               if (offset > 0 && depth == 1) {
-                       if (pre_reloc_only &&
-                           !fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL))
-                               continue;
-                       err = lists_bind_fdt(gd->dm_root, blob, offset);
-                       if (err && !ret)
-                               ret = err;
-               }
-       } while (offset > 0);
+
+       for (offset = fdt_first_subnode(blob, offset);
+            offset > 0;
+            offset = fdt_next_subnode(blob, offset)) {
+               if (pre_reloc_only &&
+                   !fdt_getprop(blob, offset, "u-boot,dm-pre-reloc", NULL))
+                       continue;
+               err = lists_bind_fdt(parent, blob, offset);
+               if (err && !ret)
+                       ret = err;
+       }
 
        if (ret)
                dm_warn("Some drivers failed to bind\n");
 
        return ret;
 }
+
+int dm_scan_fdt(const void *blob, bool pre_reloc_only)
+{
+       return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only);
+}
 #endif
 
 int dm_init_and_scan(bool pre_reloc_only)
index 02c7788da0508077b8c720d13836ea4c476cc54b..33f951b0ccfbd79a9a80943676a7548475f1fce7 100644 (file)
@@ -45,6 +45,22 @@ int dm_scan_platdata(bool pre_reloc_only);
  */
 int dm_scan_fdt(const void *blob, bool pre_reloc_only);
 
+/**
+ * dm_scan_fdt_node() - Scan the device tree and bind drivers for a node
+ *
+ * This scans the subnodes of a device tree node and and creates a driver
+ * for each one.
+ *
+ * @parent: Parent device for the devices that will be created
+ * @blob: Pointer to device tree blob
+ * @offset: Offset of node to scan
+ * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC
+ * flag. If false bind all drivers.
+ * @return 0 if OK, -ve on error
+ */
+int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset,
+                    bool pre_reloc_only);
+
 /**
  * dm_init_and_scan() - Initialise Driver Model structures and scan for devices
  *
index 409f1a3667fe3954b6164238b6ab2acccad2aa86..e8e1c0b24a38ffd8e4ca92e41d3a47d7cdacd7c8 100644 (file)
@@ -155,6 +155,15 @@ int testfdt_ping(struct udevice *dev, int pingval, int *pingret);
 int dm_check_operations(struct dm_test_state *dms, struct udevice *dev,
                        uint32_t base, struct dm_test_priv *priv);
 
+/**
+ * dm_check_devices() - check the devices respond to operations correctly
+ *
+ * @dms: Overall test state
+ * @num_devices: Number of test devices to check
+ * @return 0 if OK, -ve on error
+ */
+int dm_check_devices(struct dm_test_state *dms, int num_devices);
+
 /**
  * dm_test_main() - Run all the tests
  *
index 77ff9eae904ab817aaa18206edd77606ae84ff50..dd95fca428dc54d54c1d706431f7ef6e512c78e1 100644 (file)
@@ -17,6 +17,7 @@ enum uclass_id {
        UCLASS_DEMO,
        UCLASS_TEST,
        UCLASS_TEST_FDT,
+       UCLASS_TEST_BUS,
 
        /* U-Boot uclasses start here */
        UCLASS_GPIO,            /* Bank of general-purpose I/O pins */
index c0f21351d7435cf9399baf6c0ad5a6b59dcdff85..5c2415e3d2a93b9307e27cbefa48d6dab9eb40c9 100644 (file)
@@ -5,6 +5,7 @@
 #
 
 obj-$(CONFIG_CMD_DM) += cmd_dm.o
+obj-$(CONFIG_DM_TEST) += bus.o
 obj-$(CONFIG_DM_TEST) += test-driver.o
 obj-$(CONFIG_DM_TEST) += test-fdt.o
 obj-$(CONFIG_DM_TEST) += test-main.o
diff --git a/test/dm/bus.c b/test/dm/bus.c
new file mode 100644 (file)
index 0000000..cfb9934
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/ut.h>
+#include <dm/util.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int testbus_drv_probe(struct udevice *dev)
+{
+       return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
+static const struct udevice_id testbus_ids[] = {
+       {
+               .compatible = "denx,u-boot-test-bus",
+               .data = DM_TEST_TYPE_FIRST },
+       { }
+};
+
+U_BOOT_DRIVER(testbus_drv) = {
+       .name   = "testbus_drv",
+       .of_match       = testbus_ids,
+       .id     = UCLASS_TEST_BUS,
+       .probe  = testbus_drv_probe,
+       .priv_auto_alloc_size = sizeof(struct dm_test_priv),
+       .platdata_auto_alloc_size = sizeof(struct dm_test_pdata),
+};
+
+UCLASS_DRIVER(testbus) = {
+       .name           = "testbus",
+       .id             = UCLASS_TEST_BUS,
+};
+
+/* Test that we can probe for children */
+static int dm_test_bus_children(struct dm_test_state *dms)
+{
+       int num_devices = 4;
+       struct udevice *bus;
+       struct uclass *uc;
+
+       ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
+       ut_asserteq(num_devices, list_count_items(&uc->dev_head));
+
+       /* Probe the bus, which should yield 3 more devices */
+       ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+       num_devices += 3;
+
+       ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
+       ut_asserteq(num_devices, list_count_items(&uc->dev_head));
+
+       ut_assert(!dm_check_devices(dms, num_devices));
+
+       return 0;
+}
+DM_TEST(dm_test_bus_children, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
index 7980a683b5445bfc3835ee7a5f316b999ef9f185..cd2c38995e936246a5b8643f04c91b218d454ff0 100644 (file)
@@ -91,37 +91,17 @@ UCLASS_DRIVER(testfdt) = {
        .id             = UCLASS_TEST_FDT,
 };
 
-/* Test that FDT-based binding works correctly */
-static int dm_test_fdt(struct dm_test_state *dms)
+int dm_check_devices(struct dm_test_state *dms, int num_devices)
 {
-       const int num_drivers = 4;
        struct udevice *dev;
-       struct uclass *uc;
        int ret;
        int i;
 
-       ret = dm_scan_fdt(gd->fdt_blob, false);
-       ut_assert(!ret);
-
-       ret = uclass_get(UCLASS_TEST_FDT, &uc);
-       ut_assert(!ret);
-
-       /* These are num_drivers compatible root-level device tree nodes */
-       ut_asserteq(num_drivers, list_count_items(&uc->dev_head));
-
-       /* Each should have no platdata / priv */
-       for (i = 0; i < num_drivers; i++) {
-               ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev);
-               ut_assert(!ret);
-               ut_assert(!dev_get_priv(dev));
-               ut_assert(!dev->platdata);
-       }
-
        /*
         * Now check that the ping adds are what we expect. This is using the
         * ping-add property in each node.
         */
-       for (i = 0; i < num_drivers; i++) {
+       for (i = 0; i < num_devices; i++) {
                uint32_t base;
 
                ret = uclass_get_device(UCLASS_TEST_FDT, i, &dev);
@@ -144,6 +124,37 @@ static int dm_test_fdt(struct dm_test_state *dms)
 
        return 0;
 }
+
+/* Test that FDT-based binding works correctly */
+static int dm_test_fdt(struct dm_test_state *dms)
+{
+       const int num_devices = 4;
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret;
+       int i;
+
+       ret = dm_scan_fdt(gd->fdt_blob, false);
+       ut_assert(!ret);
+
+       ret = uclass_get(UCLASS_TEST_FDT, &uc);
+       ut_assert(!ret);
+
+       /* These are num_devices compatible root-level device tree nodes */
+       ut_asserteq(num_devices, list_count_items(&uc->dev_head));
+
+       /* Each should have no platdata / priv */
+       for (i = 0; i < num_devices; i++) {
+               ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev);
+               ut_assert(!ret);
+               ut_assert(!dev_get_priv(dev));
+               ut_assert(!dev->platdata);
+       }
+
+       ut_assertok(dm_check_devices(dms, num_devices));
+
+       return 0;
+}
 DM_TEST(dm_test_fdt, 0);
 
 static int dm_test_fdt_pre_reloc(struct dm_test_state *dms)
@@ -187,7 +198,10 @@ static int dm_test_fdt_uclass_seq(struct dm_test_state *dms)
        ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 7,
                                                       true, &dev));
 
-       /* Note that c-test is not probed since it is not a top-level node */
+       /*
+        * Note that c-test nodes are not probed since it is not a top-level
+        * node
+        */
        ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 3, &dev));
        ut_asserteq_str("b-test", dev->name);
 
@@ -236,12 +250,11 @@ static int dm_test_fdt_offset(struct dm_test_state *dms)
                                                            node, &dev));
 
        /* This is not a top level node so should not be probed */
-       node = fdt_path_offset(blob, "/some-bus/c-test");
+       node = fdt_path_offset(blob, "/some-bus/c-test@5");
        ut_assert(node > 0);
        ut_asserteq(-ENODEV, uclass_get_device_by_of_offset(UCLASS_TEST_FDT,
                                                            node, &dev));
 
        return 0;
 }
-
 DM_TEST(dm_test_fdt_offset, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
index 60503f1da34fe9b015b62b786089c5a3939bd43a..84895951550ffec7b1df26c4f2e782398f589ce5 100644 (file)
        some-bus {
                #address-cells = <1>;
                #size-cells = <0>;
+               compatible = "denx,u-boot-test-bus";
                reg = <3>;
                ping-expect = <4>;
                ping-add = <4>;
-               c-test {
+               c-test@5 {
                        compatible = "denx,u-boot-fdt-test";
                        reg = <5>;
+                       ping-expect = <5>;
                        ping-add = <5>;
                };
+               c-test@0 {
+                       compatible = "denx,u-boot-fdt-test";
+                       reg = <0>;
+                       ping-expect = <6>;
+                       ping-add = <6>;
+               };
+               c-test@1 {
+                       compatible = "denx,u-boot-fdt-test";
+                       reg = <1>;
+                       ping-expect = <7>;
+                       ping-add = <7>;
+               };
        };
 
        d-test {