dm: Add functions to access a device's children
authorSimon Glass <sjg@chromium.org>
Wed, 23 Jul 2014 12:55:19 +0000 (06:55 -0600)
committerSimon Glass <sjg@chromium.org>
Wed, 23 Jul 2014 13:08:37 +0000 (14:08 +0100)
Devices can have childen that can be addressed by a simple index, the
sequence number or a device tree offset. Add functions to access a child
in each of these ways.

The index is typically used as a fallback when the sequence number is not
available. For example we may use a serial UART with sequence number 0 as
the console, but if no UART has sequence number 0, then we can fall back
to just using the first UART (index 0).

The device tree offset function is useful for buses, where they want to
locate one of their children. The device tree can be scanned to find the
offset of each child, and that offset can then find the device.

Signed-off-by: Simon Glass <sjg@chromium.org>
doc/driver-model/README.txt
drivers/core/device.c
include/dm/device.h
test/dm/bus.c

index a2b6122ebcb3ac7be8285a218d5ad61dd83c6798..59ef05cf2b5405f36de05c8e50f1e90ea34d7426 100644 (file)
@@ -95,13 +95,14 @@ are provided in test/dm. To run them, try:
 You should see something like this:
 
     <...U-Boot banner...>
-    Running 18 driver model tests
+    Running 19 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_bus_children_funcs
     Test: dm_test_children
     Test: dm_test_fdt
     Device 'd-test': seq 3 is in use by 'b-test'
index 848ce3b675efc72c2b9ea953c78bd8f81862f810..74bb5f0f8efb8b568885a2a10ba09f118f052a9f 100644 (file)
@@ -376,3 +376,96 @@ void *dev_get_priv(struct udevice *dev)
 
        return dev->priv;
 }
+
+static int device_get_device_tail(struct udevice *dev, int ret,
+                                 struct udevice **devp)
+{
+       if (ret)
+               return ret;
+
+       ret = device_probe(dev);
+       if (ret)
+               return ret;
+
+       *devp = dev;
+
+       return 0;
+}
+
+int device_get_child(struct udevice *parent, int index, struct udevice **devp)
+{
+       struct udevice *dev;
+
+       list_for_each_entry(dev, &parent->child_head, sibling_node) {
+               if (!index--)
+                       return device_get_device_tail(dev, 0, devp);
+       }
+
+       return -ENODEV;
+}
+
+int device_find_child_by_seq(struct udevice *parent, int seq_or_req_seq,
+                            bool find_req_seq, struct udevice **devp)
+{
+       struct udevice *dev;
+
+       *devp = NULL;
+       if (seq_or_req_seq == -1)
+               return -ENODEV;
+
+       list_for_each_entry(dev, &parent->child_head, sibling_node) {
+               if ((find_req_seq ? dev->req_seq : dev->seq) ==
+                               seq_or_req_seq) {
+                       *devp = dev;
+                       return 0;
+               }
+       }
+
+       return -ENODEV;
+}
+
+int device_get_child_by_seq(struct udevice *parent, int seq,
+                           struct udevice **devp)
+{
+       struct udevice *dev;
+       int ret;
+
+       *devp = NULL;
+       ret = device_find_child_by_seq(parent, seq, false, &dev);
+       if (ret == -ENODEV) {
+               /*
+                * We didn't find it in probed devices. See if there is one
+                * that will request this seq if probed.
+                */
+               ret = device_find_child_by_seq(parent, seq, true, &dev);
+       }
+       return device_get_device_tail(dev, ret, devp);
+}
+
+int device_find_child_by_of_offset(struct udevice *parent, int of_offset,
+                                  struct udevice **devp)
+{
+       struct udevice *dev;
+
+       *devp = NULL;
+
+       list_for_each_entry(dev, &parent->child_head, sibling_node) {
+               if (dev->of_offset == of_offset) {
+                       *devp = dev;
+                       return 0;
+               }
+       }
+
+       return -ENODEV;
+}
+
+int device_get_child_by_of_offset(struct udevice *parent, int seq,
+                                 struct udevice **devp)
+{
+       struct udevice *dev;
+       int ret;
+
+       *devp = NULL;
+       ret = device_find_child_by_of_offset(parent, seq, &dev);
+       return device_get_device_tail(dev, ret, devp);
+}
index 9077490901e6dffd745c5e964dde1c625457fb48..3f0f7111f7cd136b3b54527a334bfafe2a3229ed 100644 (file)
@@ -168,6 +168,18 @@ void *dev_get_platdata(struct udevice *dev);
  */
 void *dev_get_priv(struct udevice *dev);
 
+/**
+ * device_get_child() - Get the child of a device by index
+ *
+ * Returns the numbered child, 0 being the first. This does not use
+ * sequence numbers, only the natural order.
+ *
+ * @dev:       Parent device to check
+ * @index:     Child index
+ * @devp:      Returns pointer to device
+ */
+int device_get_child(struct udevice *parent, int index, struct udevice **devp);
+
 /**
  * device_find_child_by_seq() - Find a child device based on a sequence
  *
@@ -190,4 +202,50 @@ void *dev_get_priv(struct udevice *dev);
 int device_find_child_by_seq(struct udevice *parent, int seq_or_req_seq,
                             bool find_req_seq, struct udevice **devp);
 
+/**
+ * device_get_child_by_seq() - Get a child device based on a sequence
+ *
+ * If an active device has this sequence it will be returned. If there is no
+ * such device then this will check for a device that is requesting this
+ * sequence.
+ *
+ * The device is probed to activate it ready for use.
+ *
+ * @parent: Parent device
+ * @seq: Sequence number to find (0=first)
+ * @devp: Returns pointer to device (there is only one per for each seq)
+ * Set to NULL if none is found
+ * @return 0 if OK, -ve on error
+ */
+int device_get_child_by_seq(struct udevice *parent, int seq,
+                           struct udevice **devp);
+
+/**
+ * device_find_child_by_of_offset() - Find a child device based on FDT offset
+ *
+ * Locates a child device by its device tree offset.
+ *
+ * @parent: Parent device
+ * @of_offset: Device tree offset to find
+ * @devp: Returns pointer to device if found, otherwise this is set to NULL
+ * @return 0 if OK, -ve on error
+ */
+int device_find_child_by_of_offset(struct udevice *parent, int of_offset,
+                                  struct udevice **devp);
+
+/**
+ * device_get_child_by_of_offset() - Get a child device based on FDT offset
+ *
+ * Locates a child device by its device tree offset.
+ *
+ * The device is probed to activate it ready for use.
+ *
+ * @parent: Parent device
+ * @of_offset: Device tree offset to find
+ * @devp: Returns pointer to device if found, otherwise this is set to NULL
+ * @return 0 if OK, -ve on error
+ */
+int device_get_child_by_of_offset(struct udevice *parent, int seq,
+                                 struct udevice **devp);
+
 #endif
index cfb993440c73eaacd40e90262c737dca9801e218..08a4725648e1da7f3c2bf0d4f690652fb125f622 100644 (file)
@@ -61,3 +61,49 @@ static int dm_test_bus_children(struct dm_test_state *dms)
        return 0;
 }
 DM_TEST(dm_test_bus_children, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test our functions for accessing children */
+static int dm_test_bus_children_funcs(struct dm_test_state *dms)
+{
+       const void *blob = gd->fdt_blob;
+       struct udevice *bus, *dev;
+       int node;
+
+       ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+
+       /* device_get_child() */
+       ut_assertok(device_get_child(bus, 0, &dev));
+       ut_asserteq(-ENODEV, device_get_child(bus, 4, &dev));
+       ut_assertok(device_get_child_by_seq(bus, 5, &dev));
+       ut_assert(dev->flags & DM_FLAG_ACTIVATED);
+       ut_asserteq_str("c-test@5", dev->name);
+
+       /* Device with sequence number 0 should be accessible */
+       ut_asserteq(-ENODEV, device_find_child_by_seq(bus, -1, true, &dev));
+       ut_assertok(device_find_child_by_seq(bus, 0, true, &dev));
+       ut_assert(!(dev->flags & DM_FLAG_ACTIVATED));
+       ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 0, false, &dev));
+       ut_assertok(device_get_child_by_seq(bus, 0, &dev));
+       ut_assert(dev->flags & DM_FLAG_ACTIVATED);
+
+       /* There is no device with sequence number 2 */
+       ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 2, false, &dev));
+       ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 2, true, &dev));
+       ut_asserteq(-ENODEV, device_get_child_by_seq(bus, 2, &dev));
+
+       /* Looking for something that is not a child */
+       node = fdt_path_offset(blob, "/junk");
+       ut_asserteq(-ENODEV, device_find_child_by_of_offset(bus, node, &dev));
+       node = fdt_path_offset(blob, "/d-test");
+       ut_asserteq(-ENODEV, device_find_child_by_of_offset(bus, node, &dev));
+
+       /* Find a valid child */
+       node = fdt_path_offset(blob, "/some-bus/c-test@1");
+       ut_assertok(device_find_child_by_of_offset(bus, node, &dev));
+       ut_assert(!(dev->flags & DM_FLAG_ACTIVATED));
+       ut_assertok(device_get_child_by_of_offset(bus, node, &dev));
+       ut_assert(dev->flags & DM_FLAG_ACTIVATED);
+
+       return 0;
+}
+DM_TEST(dm_test_bus_children_funcs, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);