X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fcore%2Fuclass.c;h=f5e406792209686a897c7395dc80d616f082036f;hb=e83e2b390038c9075642cb243a6292241beb8d73;hp=98c15e585dfa81d36313899ffacdcaee9ea57ca6;hpb=02c07b3741f1b825934b1a6eb8f23530532dc426;p=u-boot diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 98c15e585d..f5e4067922 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -56,9 +57,14 @@ static int uclass_add(enum uclass_id id, struct uclass **ucp) *ucp = NULL; uc_drv = lists_uclass_lookup(id); if (!uc_drv) { - dm_warn("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", - id); - return -ENOENT; + debug("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", + id); + /* + * Use a strange error to make this case easier to find. When + * a uclass is not available it can prevent driver model from + * starting up and this failure is otherwise hard to debug. + */ + return -EPFNOSUPPORT; } uc = calloc(1, sizeof(*uc)); if (!uc) @@ -99,11 +105,19 @@ 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) { - ret = device_remove(dev); + /* + * 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, DM_REMOVE_NORMAL); if (ret) return ret; ret = device_unbind(dev); @@ -135,6 +149,15 @@ int uclass_get(enum uclass_id id, struct uclass **ucp) return 0; } +const char *uclass_get_name(enum uclass_id id) +{ + struct uclass *uc; + + if (uclass_get(id, &uc)) + return NULL; + return uc->uc_drv->name; +} + int uclass_find_device(enum uclass_id id, int index, struct udevice **devp) { struct uclass *uc; @@ -145,6 +168,8 @@ int uclass_find_device(enum uclass_id id, int index, struct udevice **devp) ret = uclass_get(id, &uc); if (ret) return ret; + if (list_empty(&uc->dev_head)) + return -ENODEV; list_for_each_entry(dev, &uc->dev_head, uclass_node) { if (!index--) { @@ -156,6 +181,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) { @@ -172,7 +251,7 @@ int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq, return ret; list_for_each_entry(dev, &uc->dev_head, uclass_node) { - debug(" - %d %d\n", dev->req_seq, dev->seq); + debug(" - %d %d '%s'\n", dev->req_seq, dev->seq, dev->name); if ((find_req_seq ? dev->req_seq : dev->seq) == seq_or_req_seq) { *devp = dev; @@ -185,8 +264,8 @@ int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq, return -ENODEV; } -static int uclass_find_device_by_of_offset(enum uclass_id id, int node, - struct udevice **devp) +int uclass_find_device_by_of_offset(enum uclass_id id, int node, + struct udevice **devp) { struct uclass *uc; struct udevice *dev; @@ -200,7 +279,7 @@ static int uclass_find_device_by_of_offset(enum uclass_id id, int node, return ret; list_for_each_entry(dev, &uc->dev_head, uclass_node) { - if (dev->of_offset == node) { + if (dev_of_offset(dev) == node) { *devp = dev; return 0; } @@ -209,22 +288,90 @@ 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, - struct udevice **devp) +int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, + struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + *devp = NULL; + if (!ofnode_valid(node)) + return -ENODEV; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (ofnode_equal(dev_ofnode(dev), node)) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) +static int uclass_find_device_by_phandle(enum uclass_id id, + struct udevice *parent, + const char *name, + struct udevice **devp) { + struct udevice *dev; + struct uclass *uc; + int find_phandle; + int ret; + + *devp = NULL; + find_phandle = dev_read_u32_default(parent, name, -1); + if (find_phandle <= 0) + return -ENOENT; + ret = uclass_get(id, &uc); if (ret) return ret; + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + uint phandle; + + phandle = dev_read_phandle(dev); + + if (phandle == find_phandle) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} +#endif + +int uclass_get_device_by_driver(enum uclass_id id, + const struct driver *find_drv, + struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (dev->driver == find_drv) + return uclass_get_device_tail(dev, 0, devp); + } + + return -ENODEV; +} + +int uclass_get_device_tail(struct udevice *dev, int ret, struct udevice **devp) +{ + if (ret) + return ret; + + assert(dev); ret = device_probe(dev); if (ret) return ret; @@ -244,6 +391,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; @@ -272,24 +430,52 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node, return uclass_get_device_tail(dev, ret, devp); } +int uclass_get_device_by_ofnode(enum uclass_id id, ofnode node, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device_by_ofnode(id, node, &dev); + + return uclass_get_device_tail(dev, ret, devp); +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) +int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, + const char *name, struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device_by_phandle(id, parent, name, &dev); + return uclass_get_device_tail(dev, ret, devp); +} +#endif + 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)) + ret = uclass_find_first_device(id, &dev); + if (!dev) return 0; + return uclass_get_device_tail(dev, ret, devp); +} - dev = list_first_entry(&uc->dev_head, struct udevice, uclass_node); - ret = device_probe(dev); +int uclass_first_device_err(enum uclass_id id, struct udevice **devp) +{ + int ret; + + ret = uclass_first_device(id, devp); if (ret) return ret; - *devp = dev; + else if (!*devp) + return -ENODEV; return 0; } @@ -300,17 +486,37 @@ int uclass_next_device(struct udevice **devp) int ret; *devp = NULL; - if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head)) + ret = uclass_find_next_device(&dev); + if (!dev) return 0; + return uclass_get_device_tail(dev, ret, devp); +} - dev = list_entry(dev->uclass_node.next, struct udevice, - uclass_node); - ret = device_probe(dev); +int uclass_first_device_check(enum uclass_id id, struct udevice **devp) +{ + int ret; + + *devp = NULL; + ret = uclass_find_first_device(id, devp); if (ret) return ret; - *devp = dev; + if (!*devp) + return 0; - return 0; + return device_probe(*devp); +} + +int uclass_next_device_check(struct udevice **devp) +{ + int ret; + + ret = uclass_find_next_device(devp); + if (ret) + return ret; + if (!*devp) + return 0; + + return device_probe(*devp); } int uclass_bind_device(struct udevice *dev) @@ -330,11 +536,6 @@ int uclass_bind_device(struct udevice *dev) goto err; } } - if (uc->uc_drv->post_bind) { - ret = uc->uc_drv->post_bind(dev); - if (ret) - goto err; - } return 0; err: @@ -344,6 +545,7 @@ err: return ret; } +#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE) int uclass_unbind_device(struct udevice *dev) { struct uclass *uc; @@ -359,6 +561,7 @@ int uclass_unbind_device(struct udevice *dev) list_del(&dev->uclass_node); return 0; } +#endif int uclass_resolve_seq(struct udevice *dev) { @@ -422,24 +625,19 @@ int uclass_post_probe_device(struct udevice *dev) return 0; } +#if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE) int uclass_pre_remove_device(struct udevice *dev) { - struct uclass_driver *uc_drv; struct uclass *uc; int ret; uc = dev->uclass; - uc_drv = uc->uc_drv; if (uc->uc_drv->pre_remove) { ret = uc->uc_drv->pre_remove(dev); if (ret) return ret; } - if (uc_drv->per_device_auto_alloc_size) { - free(dev->uclass_priv); - dev->uclass_priv = NULL; - } - dev->seq = -1; return 0; } +#endif