dm: Add Kconfig options for driver model SPL support
[platform/kernel/u-boot.git] / drivers / core / device.c
index 81ddeca..b73d3b8 100644 (file)
@@ -53,27 +53,47 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name,
        dev->driver = drv;
        dev->uclass = uc;
 
-       /*
-        * For some devices, such as a SPI or I2C bus, the 'reg' property
-        * is a reasonable indicator of the sequence number. But if there is
-        * an alias, we use that in preference. In any case, this is just
-        * a 'requested' sequence, and will be resolved (and ->seq updated)
-        * when the device is probed.
-        */
        dev->seq = -1;
+       dev->req_seq = -1;
 #ifdef CONFIG_OF_CONTROL
-       dev->req_seq = fdtdec_get_int(gd->fdt_blob, of_offset, "reg", -1);
-       if (!IS_ERR_VALUE(dev->req_seq))
-               dev->req_seq &= INT_MAX;
-       if (uc->uc_drv->name && of_offset != -1) {
-               fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, of_offset,
-                                    &dev->req_seq);
+       /*
+        * Some devices, such as a SPI bus, I2C bus and serial ports are
+        * numbered using aliases.
+        *
+        * This is just a 'requested' sequence, and will be
+        * resolved (and ->seq updated) when the device is probed.
+        */
+       if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) {
+               if (uc->uc_drv->name && of_offset != -1) {
+                       fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name,
+                                            of_offset, &dev->req_seq);
+               }
        }
-#else
-       dev->req_seq = -1;
 #endif
-       if (!dev->platdata && drv->platdata_auto_alloc_size)
+       if (!dev->platdata && drv->platdata_auto_alloc_size) {
                dev->flags |= DM_FLAG_ALLOC_PDATA;
+               dev->platdata = calloc(1, drv->platdata_auto_alloc_size);
+               if (!dev->platdata) {
+                       ret = -ENOMEM;
+                       goto fail_alloc1;
+               }
+       }
+       if (parent) {
+               int size = parent->driver->per_child_platdata_auto_alloc_size;
+
+               if (!size) {
+                       size = parent->uclass->uc_drv->
+                                       per_child_platdata_auto_alloc_size;
+               }
+               if (size) {
+                       dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA;
+                       dev->parent_platdata = calloc(1, size);
+                       if (!dev->parent_platdata) {
+                               ret = -ENOMEM;
+                               goto fail_alloc2;
+                       }
+               }
+       }
 
        /* put dev into parent's successor list */
        if (parent)
@@ -81,28 +101,51 @@ int device_bind(struct udevice *parent, struct driver *drv, const char *name,
 
        ret = uclass_bind_device(dev);
        if (ret)
-               goto fail_bind;
+               goto fail_uclass_bind;
 
        /* if we fail to bind we remove device from successors and free it */
        if (drv->bind) {
                ret = drv->bind(dev);
-               if (ret) {
-                       if (uclass_unbind_device(dev)) {
-                               dm_warn("Failed to unbind dev '%s' on error path\n",
-                                       dev->name);
-                       }
+               if (ret)
                        goto fail_bind;
-               }
        }
+       if (parent && parent->driver->child_post_bind) {
+               ret = parent->driver->child_post_bind(dev);
+               if (ret)
+                       goto fail_child_post_bind;
+       }
+
        if (parent)
                dm_dbg("Bound device %s to %s\n", dev->name, parent->name);
        *devp = dev;
 
        return 0;
 
+fail_child_post_bind:
+       if (drv->unbind && drv->unbind(dev)) {
+               dm_warn("unbind() method failed on dev '%s' on error path\n",
+                       dev->name);
+       }
+
 fail_bind:
+       if (uclass_unbind_device(dev)) {
+               dm_warn("Failed to unbind dev '%s' on error path\n",
+                       dev->name);
+       }
+fail_uclass_bind:
        list_del(&dev->sibling_node);
+       if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
+               free(dev->parent_platdata);
+               dev->parent_platdata = NULL;
+       }
+fail_alloc2:
+       if (dev->flags & DM_FLAG_ALLOC_PDATA) {
+               free(dev->platdata);
+               dev->platdata = NULL;
+       }
+fail_alloc1:
        free(dev);
+
        return ret;
 }
 
@@ -137,7 +180,7 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
        drv = dev->driver;
        assert(drv);
 
-       /* Allocate private data and platdata if requested */
+       /* Allocate private data if requested */
        if (drv->priv_auto_alloc_size) {
                dev->priv = calloc(1, drv->priv_auto_alloc_size);
                if (!dev->priv) {
@@ -146,13 +189,6 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
                }
        }
        /* Allocate private data if requested */
-       if (dev->flags & DM_FLAG_ALLOC_PDATA) {
-               dev->platdata = calloc(1, drv->platdata_auto_alloc_size);
-               if (!dev->platdata) {
-                       ret = -ENOMEM;
-                       goto fail;
-               }
-       }
        size = dev->uclass->uc_drv->per_device_auto_alloc_size;
        if (size) {
                dev->uclass_priv = calloc(1, size);
@@ -165,6 +201,10 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
        /* Ensure all parents are probed */
        if (dev->parent) {
                size = dev->parent->driver->per_child_auto_alloc_size;
+               if (!size) {
+                       size = dev->parent->uclass->uc_drv->
+                                       per_child_auto_alloc_size;
+               }
                if (size) {
                        dev->parent_priv = calloc(1, size);
                        if (!dev->parent_priv) {
@@ -187,6 +227,10 @@ int device_probe_child(struct udevice *dev, void *parent_priv)
        }
        dev->seq = seq;
 
+       ret = uclass_pre_probe_child(dev);
+       if (ret)
+               goto fail;
+
        if (dev->parent && dev->parent->driver->child_pre_probe) {
                ret = dev->parent->driver->child_pre_probe(dev);
                if (ret)
@@ -234,27 +278,37 @@ int device_probe(struct udevice *dev)
 void *dev_get_platdata(struct udevice *dev)
 {
        if (!dev) {
-               dm_warn("%s: null device", __func__);
+               dm_warn("%s: null device\n", __func__);
                return NULL;
        }
 
        return dev->platdata;
 }
 
-void *dev_get_priv(struct udevice *dev)
+void *dev_get_parent_platdata(struct udevice *dev)
 {
        if (!dev) {
                dm_warn("%s: null device", __func__);
                return NULL;
        }
 
+       return dev->parent_platdata;
+}
+
+void *dev_get_priv(struct udevice *dev)
+{
+       if (!dev) {
+               dm_warn("%s: null device\n", __func__);
+               return NULL;
+       }
+
        return dev->priv;
 }
 
 void *dev_get_parentdata(struct udevice *dev)
 {
        if (!dev) {
-               dm_warn("%s: null device", __func__);
+               dm_warn("%s: null device\n", __func__);
                return NULL;
        }
 
@@ -381,7 +435,17 @@ int device_find_next_child(struct udevice **devp)
        return 0;
 }
 
+struct udevice *dev_get_parent(struct udevice *child)
+{
+       return child->parent;
+}
+
 ulong dev_get_of_data(struct udevice *dev)
 {
        return dev->of_id->data;
 }
+
+enum uclass_id device_get_uclass_id(struct udevice *dev)
+{
+       return dev->uclass->uc_drv->id;
+}