diff options
Diffstat (limited to 'drivers/core/device.c')
-rw-r--r-- | drivers/core/device.c | 133 |
1 files changed, 102 insertions, 31 deletions
diff --git a/drivers/core/device.c b/drivers/core/device.c index 963b16f26f..73c3e07c28 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -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) @@ -241,6 +285,16 @@ void *dev_get_platdata(struct udevice *dev) return dev->platdata; } +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) { @@ -390,3 +444,20 @@ 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; +} + +#ifdef CONFIG_OF_CONTROL +fdt_addr_t dev_get_addr(struct udevice *dev) +{ + return fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); +} +#else +fdt_addr_t dev_get_addr(struct udevice *dev) +{ + return FDT_ADDR_T_NONE; +} +#endif |