From c1d6f91952d0761f61b0f0f96e4c7aa32eee2788 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Wed, 15 Apr 2015 13:07:17 +0200 Subject: dm: core: add internal functions for getting the device without probe This commit extends the uclass-internal functions by: - uclass_find_first_device() - uclass_find_next_device() For both functions, the returned device is not probed. After some cleanup, the above functions are called by: - uclass_first_device() - uclass_next_device() for which, the returned device is probed. Signed-off-by: Przemyslaw Marczak Cc: Simon Glass Acked-by: Simon Glass --- drivers/core/uclass.c | 59 +++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 25 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 98c15e585df..21ab0d52452 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -156,6 +156,36 @@ 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_seq(enum uclass_id id, int seq_or_req_seq, bool find_req_seq, struct udevice **devp) { @@ -274,24 +304,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 +318,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) -- cgit v1.2.3 From 5eaed880282480a5a0a2b555c5f98a11252ed94e Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Wed, 15 Apr 2015 13:07:18 +0200 Subject: dm: core: Extend struct udevice by '.uclass_platdata' field. This commit adds 'uclass_platdata' field to 'struct udevice', which can be automatically allocated at bind. The allocation size is defined in 'struct uclass_driver' as 'per_device_platdata_auto_alloc_size'. New device's flag is added: DM_FLAG_ALLOC_UCLASS_PDATA, which is used for memory freeing at device unbind method. As for other udevice's fields, a complementary function is added: - dev_get_uclass_platdata() Signed-off-by: Przemyslaw Marczak Cc: Simon Glass Acked-by: Simon Glass --- drivers/core/device-remove.c | 4 ++++ drivers/core/device.c | 33 +++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 7fee1c001e4..6a16b4f690f 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 ccaa99ca63f..80eb55bb3b8 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) @@ -79,9 +79,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 +101,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; } } } @@ -139,6 +149,11 @@ fail_uclass_bind: 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) { free(dev->platdata); @@ -314,6 +329,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) { -- cgit v1.2.3 From e0735a4c60577fafdaed71c5ef046f04b0f53f09 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Wed, 15 Apr 2015 13:07:22 +0200 Subject: dm: core: uclass: add function: uclass_find_device_by_name() This commit extends the driver model uclass's API by function: - uclass_find_device_by_name() And this function returns the device if: - uclass with given ID, exists, - device with exactly given name(dev->name), exists. The returned device is not activated - need to be probed before use. Note: This function returns the first device, which name is equal to the given one. This means, that using this function you must assume, that the device name is unique in the given uclass's ID device list. uclass-internal.h: cleanup - move the uclass_find_device_by_seq() declaration and description, near the other uclass_find*() functions. Signed-off-by: Przemyslaw Marczak Cc: Simon Glass Acked-by: Simon Glass --- drivers/core/uclass.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/core') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 21ab0d52452..61e96e9a427 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -186,6 +186,30 @@ int uclass_find_next_device(struct udevice **devp) 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) { -- cgit v1.2.3 From b7af1a2da767c0dd283ffce3d50efd36af32df14 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Wed, 15 Apr 2015 13:07:23 +0200 Subject: dm: core: uclass: add function: uclass_get_device_by_name() This commit extends the driver model uclass's API by function: - uclass_get_device_by_name() And this function returns the device if: - uclass with given ID, exists, - device with exactly given name(dev->name), exists, - device probe, doesn't return an error. The returned device is activated and ready to use. Note: This function returns the first device, which name is equal to the given one. This means, that using this function you must assume, that the device name is unique in the given uclass's ID device list. Signed-off-by: Przemyslaw Marczak Cc: Simon Glass Acked-by: Simon Glass --- drivers/core/uclass.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/core') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 61e96e9a427..c1ebee77f01 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -298,6 +298,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; -- cgit v1.2.3 From cc73d37b7f1edbbf03e2abcf5815bdd122e8baed Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Wed, 15 Apr 2015 13:07:24 +0200 Subject: dm: core: device: add function: dev_get_driver_ops() This commit extends the driver model device's API by function: - dev_get_driver_ops() And this function returns the device's driver's operations if given: - dev pointer, is non-NULL - dev->driver->ops pointer, is non-NULL in other case the, the NULL pointer is returned. Signed-off-by: Przemyslaw Marczak Cc: Simon Glass Acked-by: Simon Glass --- drivers/core/device.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/core') diff --git a/drivers/core/device.c b/drivers/core/device.c index 80eb55bb3b8..d024abbd417 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -499,6 +499,14 @@ 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; -- cgit v1.2.3 From f9c370dcdf056f035f18bf77db8a706cea21f9ce Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Wed, 15 Apr 2015 13:07:25 +0200 Subject: dm: core: device: add function: dev_get_uclass_name() This commit extends the driver model device's API by function: - dev_get_uclass_name() And this function returns the device's uclass driver name if: - given dev pointer, is non_NULL otherwise, the NULL pointer is returned. Signed-off-by: Przemyslaw Marczak Cc: Simon Glass Acked-by: Simon Glass --- drivers/core/device.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/core') diff --git a/drivers/core/device.c b/drivers/core/device.c index d024abbd417..df81b8e63d2 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -512,6 +512,14 @@ 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) { -- cgit v1.2.3 From 794d521917eab07651248174a81ba51cd265d9dc Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Mon, 20 Apr 2015 13:32:32 +0200 Subject: dm: core: remove type 'static' of function uclass_get_device_tail() Uclass API provides a few functions for get/find the device. To provide a complete function set of uclass-internal functions, for use by the drivers, the function uclass_get_device_tail() should be non-static. Signed-off-by: Przemyslaw Marczak Cc: Simon Glass Acked-by: Simon Glass --- drivers/core/uclass.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index c1ebee77f01..7627ad141b5 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -263,17 +263,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) -- cgit v1.2.3 From 07d260e047680971d926bc9a573f9291f39fc988 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 19 Apr 2015 07:20:58 -0600 Subject: dm: core: Handle recursive unbinding of uclass devices Since a device can have children in the same uclass as itself, we need to handle unbinding carefully: we must allow that unbinding a device in a uclass may cause another device in the same uclass to be unbound. Adjust the code to cope. Signed-off-by: Simon Glass Reviewed-by: Joe Hershberger Tested-by: Joe Hershberger --- drivers/core/uclass.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 7627ad141b5..9fec8c9c7ad 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; -- cgit v1.2.3 From 36fa61dc612e363fb60840a06333fc37ec3fb68e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 27 Feb 2015 22:06:30 -0700 Subject: dm: core: Allow sequence alias support to be removed for SPL In many cases SPL only uses a single serial port and there is no need for alias sequence support. We will just use the serial port pointed to by stdout-path in the /chosen node. Signed-off-by: Simon Glass --- drivers/core/Kconfig | 9 +++++++++ drivers/core/device.c | 28 +++++++++++++++------------- 2 files changed, 24 insertions(+), 13 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 75d182d27f7..2861b430795 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.c b/drivers/core/device.c index df81b8e63d2..7f24243fd7f 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -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); -- cgit v1.2.3 From 7f9875e733b79556ade508b88f88ac1f8a2c7e3c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 27 Feb 2015 22:06:31 -0700 Subject: dm: core: Remove unbind operations when not required The CONFIG_DM_DEVICE_REMOVE option takes out code related to removing devices. It should also remove the 'unbind' code since if we cannot remove we probably don't need to unbind. Signed-off-by: Simon Glass --- drivers/core/uclass.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/core') diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 9fec8c9c7ad..04e939d6c13 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -386,6 +386,7 @@ err: return ret; } +#ifdef CONFIG_DM_DEVICE_REMOVE int uclass_unbind_device(struct udevice *dev) { struct uclass *uc; @@ -401,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) { @@ -464,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; @@ -485,3 +488,4 @@ int uclass_pre_remove_device(struct udevice *dev) return 0; } +#endif -- cgit v1.2.3 From 5a87c4174d18fe40dcc847ba36853a9f15cb3e1e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 27 Feb 2015 22:06:33 -0700 Subject: dm: core: Drop device removal error path when not supported When CONFIG_DM_DEVICE_REMOVE is not enabled, such as in SPL, we cannot remove or unbind devices and do not expect to get errors when binding and probing devices. So drop the error path to reduce code size. Signed-off-by: Simon Glass --- drivers/core/device.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/device.c b/drivers/core/device.c index 7f24243fd7f..3b77d231d34 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -135,21 +135,27 @@ 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) { -- cgit v1.2.3 From b2b0d3e7129d4e59be1a016ad4fb05db87b8c5b4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 27 Feb 2015 22:06:41 -0700 Subject: dm: core: Select device tree control correctly for SPL Some boards will not use device tree for SPL even with driver model. Add the logic to support this. Signed-off-by: Simon Glass --- drivers/core/root.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/core') diff --git a/drivers/core/root.c b/drivers/core/root.c index 9b5c6bb10cb..12d046051fd 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; -- cgit v1.2.3