diff options
Diffstat (limited to 'drivers/core')
-rw-r--r-- | drivers/core/Kconfig | 9 | ||||
-rw-r--r-- | drivers/core/device-remove.c | 4 | ||||
-rw-r--r-- | drivers/core/device.c | 103 | ||||
-rw-r--r-- | drivers/core/root.c | 14 | ||||
-rw-r--r-- | drivers/core/uclass.c | 122 |
5 files changed, 181 insertions, 71 deletions
diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 75d182d27f..2861b43079 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -46,3 +46,12 @@ config DM_STDIO Normally serial drivers register with stdio so that they can be used as normal output devices. In SPL we don't normally use stdio, so we can omit this feature. + +config DM_SEQ_ALIAS + bool "Support numbered aliases in device tree" + depends on DM + default y + help + Most boards will have a '/aliases' node containing the path to + numbered devices (e.g. serial0 = &serial0). This feature can be + disabled if it is not required, to save code space in SPL. diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 7fee1c001e..6a16b4f690 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -92,6 +92,10 @@ int device_unbind(struct udevice *dev) free(dev->platdata); dev->platdata = NULL; } + if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) { + free(dev->uclass_platdata); + dev->uclass_platdata = NULL; + } if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { free(dev->parent_platdata); dev->parent_platdata = NULL; diff --git a/drivers/core/device.c b/drivers/core/device.c index ccaa99ca63..3b77d231d3 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -30,7 +30,7 @@ int device_bind(struct udevice *parent, const struct driver *drv, { struct udevice *dev; struct uclass *uc; - int ret = 0; + int size, ret = 0; *devp = NULL; if (!name) @@ -56,21 +56,23 @@ int device_bind(struct udevice *parent, const struct driver *drv, dev->seq = -1; dev->req_seq = -1; -#ifdef CONFIG_OF_CONTROL - /* - * 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); + if (IS_ENABLED(CONFIG_OF_CONTROL) && IS_ENABLED(CONFIG_DM_SEQ_ALIAS)) { + /* + * 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); + } } } -#endif + if (!dev->platdata && drv->platdata_auto_alloc_size) { dev->flags |= DM_FLAG_ALLOC_PDATA; dev->platdata = calloc(1, drv->platdata_auto_alloc_size); @@ -79,9 +81,19 @@ int device_bind(struct udevice *parent, const struct driver *drv, goto fail_alloc1; } } - if (parent) { - int size = parent->driver->per_child_platdata_auto_alloc_size; + size = uc->uc_drv->per_device_platdata_auto_alloc_size; + if (size) { + dev->flags |= DM_FLAG_ALLOC_UCLASS_PDATA; + dev->uclass_platdata = calloc(1, size); + if (!dev->uclass_platdata) { + ret = -ENOMEM; + goto fail_alloc2; + } + } + + if (parent) { + size = parent->driver->per_child_platdata_auto_alloc_size; if (!size) { size = parent->uclass->uc_drv-> per_child_platdata_auto_alloc_size; @@ -91,7 +103,7 @@ int device_bind(struct udevice *parent, const struct driver *drv, dev->parent_platdata = calloc(1, size); if (!dev->parent_platdata) { ret = -ENOMEM; - goto fail_alloc2; + goto fail_alloc3; } } } @@ -123,21 +135,32 @@ int device_bind(struct udevice *parent, const struct driver *drv, 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); + if (IS_ENABLED(DM_DEVICE_REMOVE)) { + 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); + if (IS_ENABLED(DM_DEVICE_REMOVE)) { + 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; + if (IS_ENABLED(DM_DEVICE_REMOVE)) { + list_del(&dev->sibling_node); + if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { + free(dev->parent_platdata); + dev->parent_platdata = NULL; + } + } +fail_alloc3: + if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) { + free(dev->uclass_platdata); + dev->uclass_platdata = NULL; } fail_alloc2: if (dev->flags & DM_FLAG_ALLOC_PDATA) { @@ -314,6 +337,16 @@ void *dev_get_parent_platdata(struct udevice *dev) return dev->parent_platdata; } +void *dev_get_uclass_platdata(struct udevice *dev) +{ + if (!dev) { + dm_warn("%s: null device", __func__); + return NULL; + } + + return dev->uclass_platdata; +} + void *dev_get_priv(struct udevice *dev) { if (!dev) { @@ -474,11 +507,27 @@ ulong dev_get_driver_data(struct udevice *dev) return dev->driver_data; } +const void *dev_get_driver_ops(struct udevice *dev) +{ + if (!dev || !dev->driver->ops) + return NULL; + + return dev->driver->ops; +} + enum uclass_id device_get_uclass_id(struct udevice *dev) { return dev->uclass->uc_drv->id; } +const char *dev_get_uclass_name(struct udevice *dev) +{ + if (!dev) + return NULL; + + return dev->uclass->uc_drv->name; +} + #ifdef CONFIG_OF_CONTROL fdt_addr_t dev_get_addr(struct udevice *dev) { diff --git a/drivers/core/root.c b/drivers/core/root.c index 9b5c6bb10c..12d046051f 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -197,13 +197,15 @@ int dm_init_and_scan(bool pre_reloc_only) debug("dm_scan_platdata() failed: %d\n", ret); return ret; } -#ifdef CONFIG_OF_CONTROL - ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); - if (ret) { - debug("dm_scan_fdt() failed: %d\n", ret); - return ret; + + if (OF_CONTROL) { + ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); + if (ret) { + debug("dm_scan_fdt() failed: %d\n", ret); + return ret; + } } -#endif + ret = dm_scan_other(pre_reloc_only); if (ret) return ret; diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 98c15e585d..04e939d6c1 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -99,10 +99,18 @@ fail_mem: int uclass_destroy(struct uclass *uc) { struct uclass_driver *uc_drv; - struct udevice *dev, *tmp; + struct udevice *dev; int ret; - list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) { + /* + * We cannot use list_for_each_entry_safe() here. If a device in this + * uclass has a child device also in this uclass, it will be also be + * unbound (by the recursion in the call to device_unbind() below). + * We can loop until the list is empty. + */ + while (!list_empty(&uc->dev_head)) { + dev = list_first_entry(&uc->dev_head, struct udevice, + uclass_node); ret = device_remove(dev); if (ret) return ret; @@ -156,6 +164,60 @@ int uclass_find_device(enum uclass_id id, int index, struct udevice **devp) return -ENODEV; } +int uclass_find_first_device(enum uclass_id id, struct udevice **devp) +{ + struct uclass *uc; + int ret; + + *devp = NULL; + ret = uclass_get(id, &uc); + if (ret) + return ret; + if (list_empty(&uc->dev_head)) + return 0; + + *devp = list_first_entry(&uc->dev_head, struct udevice, uclass_node); + + return 0; +} + +int uclass_find_next_device(struct udevice **devp) +{ + struct udevice *dev = *devp; + + *devp = NULL; + if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head)) + return 0; + + *devp = list_entry(dev->uclass_node.next, struct udevice, uclass_node); + + return 0; +} + +int uclass_find_device_by_name(enum uclass_id id, const char *name, + struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + *devp = NULL; + if (!name) + return -EINVAL; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (!strncmp(dev->name, name, strlen(name))) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq, bool find_req_seq, struct udevice **devp) { @@ -209,17 +271,7 @@ static int uclass_find_device_by_of_offset(enum uclass_id id, int node, return -ENODEV; } -/** - * uclass_get_device_tail() - handle the end of a get_device call - * - * This handles returning an error or probing a device as needed. - * - * @dev: Device that needs to be probed - * @ret: Error to return. If non-zero then the device is not probed - * @devp: Returns the value of 'dev' if there is no error - * @return ret, if non-zero, else the result of the device_probe() call - */ -static int uclass_get_device_tail(struct udevice *dev, int ret, +int uclass_get_device_tail(struct udevice *dev, int ret, struct udevice **devp) { if (ret) @@ -244,6 +296,17 @@ int uclass_get_device(enum uclass_id id, int index, struct udevice **devp) return uclass_get_device_tail(dev, ret, devp); } +int uclass_get_device_by_name(enum uclass_id id, const char *name, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device_by_name(id, name, &dev); + return uclass_get_device_tail(dev, ret, devp); +} + int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp) { struct udevice *dev; @@ -274,24 +337,12 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node, int uclass_first_device(enum uclass_id id, struct udevice **devp) { - struct uclass *uc; struct udevice *dev; int ret; *devp = NULL; - ret = uclass_get(id, &uc); - if (ret) - return ret; - if (list_empty(&uc->dev_head)) - return 0; - - dev = list_first_entry(&uc->dev_head, struct udevice, uclass_node); - ret = device_probe(dev); - if (ret) - return ret; - *devp = dev; - - return 0; + ret = uclass_find_first_device(id, &dev); + return uclass_get_device_tail(dev, ret, devp); } int uclass_next_device(struct udevice **devp) @@ -300,17 +351,8 @@ int uclass_next_device(struct udevice **devp) int ret; *devp = NULL; - if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head)) - return 0; - - dev = list_entry(dev->uclass_node.next, struct udevice, - uclass_node); - ret = device_probe(dev); - if (ret) - return ret; - *devp = dev; - - return 0; + ret = uclass_find_next_device(&dev); + return uclass_get_device_tail(dev, ret, devp); } int uclass_bind_device(struct udevice *dev) @@ -344,6 +386,7 @@ err: return ret; } +#ifdef CONFIG_DM_DEVICE_REMOVE int uclass_unbind_device(struct udevice *dev) { struct uclass *uc; @@ -359,6 +402,7 @@ int uclass_unbind_device(struct udevice *dev) list_del(&dev->uclass_node); return 0; } +#endif int uclass_resolve_seq(struct udevice *dev) { @@ -422,6 +466,7 @@ int uclass_post_probe_device(struct udevice *dev) return 0; } +#ifdef CONFIG_DM_DEVICE_REMOVE int uclass_pre_remove_device(struct udevice *dev) { struct uclass_driver *uc_drv; @@ -443,3 +488,4 @@ int uclass_pre_remove_device(struct udevice *dev) return 0; } +#endif |