dm: core: Allow the uclass to set up a device's child after binding
authorSimon Glass <sjg@chromium.org>
Sun, 25 Jan 2015 15:27:08 +0000 (08:27 -0700)
committerSimon Glass <sjg@chromium.org>
Fri, 30 Jan 2015 00:09:56 +0000 (17:09 -0700)
For buses, after a child is bound, allow the uclass to perform some
processing. This can be used to figure out the address of the child (e.g.
the chip select for SPI slaves) so that it is ready to be probed.

This avoids bus drivers having to repeat the same process, which really
should be done by the uclass, since it is common.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
drivers/core/uclass.c
include/dm/uclass.h
test/dm/bus.c

index 901b06ed2baaf5f4b0cbdb71d3ca40170de33991..a5445518f570d72ff11fcdfa6465f82eab188435 100644 (file)
@@ -319,18 +319,29 @@ int uclass_bind_device(struct udevice *dev)
        int ret;
 
        uc = dev->uclass;
-
        list_add_tail(&dev->uclass_node, &uc->dev_head);
 
+       if (dev->parent) {
+               struct uclass_driver *uc_drv = dev->parent->uclass->uc_drv;
+
+               if (uc_drv->child_post_bind) {
+                       ret = uc_drv->child_post_bind(dev);
+                       if (ret)
+                               goto err;
+               }
+       }
        if (uc->uc_drv->post_bind) {
                ret = uc->uc_drv->post_bind(dev);
-               if (ret) {
-                       list_del(&dev->uclass_node);
-                       return ret;
-               }
+               if (ret)
+                       goto err;
        }
 
        return 0;
+err:
+       /* There is no need to undo the parent's post_bind call */
+       list_del(&dev->uclass_node);
+
+       return ret;
 }
 
 int uclass_unbind_device(struct udevice *dev)
index ac6c85072c9517d640ec6c8ceb2e50bac826f243..5c5b8f4208387b2db92a9ce681b3e0588a1e0947 100644 (file)
@@ -55,6 +55,7 @@ struct udevice;
  * @pre_unbind: Called before a device is unbound from this uclass
  * @post_probe: Called after a new device is probed
  * @pre_remove: Called before a device is removed
+ * @child_post_bind: Called after a child is bound to a device in this uclass
  * @init: Called to set up the uclass
  * @destroy: Called to destroy the uclass
  * @priv_auto_alloc_size: If non-zero this is the size of the private data
@@ -81,6 +82,7 @@ struct uclass_driver {
        int (*pre_unbind)(struct udevice *dev);
        int (*post_probe)(struct udevice *dev);
        int (*pre_remove)(struct udevice *dev);
+       int (*child_post_bind)(struct udevice *dev);
        int (*init)(struct uclass *class);
        int (*destroy)(struct uclass *class);
        int priv_auto_alloc_size;
index e9096970628e70ec0f0e6ce9f99c4eebef25b902..c123ed7931092e77fc7470767d53a953c4341080 100644 (file)
@@ -18,6 +18,7 @@ DECLARE_GLOBAL_DATA_PTR;
 struct dm_test_parent_platdata {
        int count;
        int bind_flag;
+       int uclass_bind_flag;
 };
 
 enum {
@@ -38,6 +39,7 @@ static int testbus_child_post_bind(struct udevice *dev)
 
        plat = dev_get_parent_platdata(dev);
        plat->bind_flag = 1;
+       plat->uclass_bind_flag = 2;
 
        return 0;
 }
@@ -443,3 +445,27 @@ static int dm_test_bus_child_post_bind(struct dm_test_state *dms)
        return 0;
 }
 DM_TEST(dm_test_bus_child_post_bind, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test that the child post_bind method is called */
+static int dm_test_bus_child_post_bind_uclass(struct dm_test_state *dms)
+{
+       struct dm_test_parent_platdata *plat;
+       struct udevice *bus, *dev;
+       int child_count;
+
+       ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+       for (device_find_first_child(bus, &dev), child_count = 0;
+            dev;
+            device_find_next_child(&dev)) {
+               /* Check that platform data is allocated */
+               plat = dev_get_parent_platdata(dev);
+               ut_assert(plat != NULL);
+               ut_asserteq(2, plat->uclass_bind_flag);
+               child_count++;
+       }
+       ut_asserteq(3, child_count);
+
+       return 0;
+}
+DM_TEST(dm_test_bus_child_post_bind_uclass,
+       DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);