X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fcore%2Fdevice-remove.c;h=3c6ab42f7d620a2aac9763f07cdbdb909072030d;hb=24bf59d0243f0ee496b20aee985968729b8d0386;hp=6b87f865e40377fc1a2e179fa4f4d394d8124abf;hpb=7a1af7a79bd79ded6a78d0c1afdbc3353669e313;p=u-boot diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 6b87f865e4..3c6ab42f7d 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -18,7 +18,16 @@ #include #include -int device_unbind_children(struct udevice *dev) +/** + * device_chld_unbind() - Unbind all device's children from the device + * + * On error, the function continues to unbind all children, and reports the + * first error. + * + * @dev: The device that is to be stripped of its children + * @return 0 on success, -ve on error + */ +static int device_chld_unbind(struct udevice *dev) { struct udevice *pos, *n; int ret, saved_ret = 0; @@ -34,7 +43,13 @@ int device_unbind_children(struct udevice *dev) return saved_ret; } -int device_remove_children(struct udevice *dev) +/** + * device_chld_remove() - Stop all device's children + * @dev: The device whose children are to be removed + * @pre_os_remove: Flag, if this functions is called in the pre-OS stage + * @return 0 on success, -ve on error + */ +static int device_chld_remove(struct udevice *dev, uint flags) { struct udevice *pos, *n; int ret; @@ -42,7 +57,7 @@ int device_remove_children(struct udevice *dev) assert(dev); list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { - ret = device_remove(pos); + ret = device_remove(pos, flags); if (ret) return ret; } @@ -61,6 +76,9 @@ int device_unbind(struct udevice *dev) if (dev->flags & DM_FLAG_ACTIVATED) return -EINVAL; + if (!(dev->flags & DM_FLAG_BOUND)) + return -EINVAL; + drv = dev->driver; assert(drv); @@ -70,7 +88,7 @@ int device_unbind(struct udevice *dev) return ret; } - ret = device_unbind_children(dev); + ret = device_chld_unbind(dev); if (ret) return ret; @@ -92,6 +110,11 @@ int device_unbind(struct udevice *dev) if (dev->parent) list_del(&dev->sibling_node); + + devres_release_all(dev); + + if (dev->flags & DM_FLAG_NAME_ALLOCED) + free((char *)dev->name); free(dev); return 0; @@ -125,9 +148,20 @@ void device_free(struct udevice *dev) dev->parent_priv = NULL; } } + + devres_release_probe(dev); +} + +static bool flags_remove(uint flags, uint drv_flags) +{ + if ((flags & DM_REMOVE_NORMAL) || + (flags & (drv_flags & (DM_FLAG_ACTIVE_DMA | DM_FLAG_OS_PREPARE)))) + return true; + + return false; } -int device_remove(struct udevice *dev) +int device_remove(struct udevice *dev, uint flags) { const struct driver *drv; int ret; @@ -145,11 +179,15 @@ int device_remove(struct udevice *dev) if (ret) return ret; - ret = device_remove_children(dev); + ret = device_chld_remove(dev, flags); if (ret) goto err; - if (drv->remove) { + /* + * Remove the device if called with the "normal" remove flag set, + * or if the remove flag matches any of the drivers remove flags + */ + if (drv->remove && flags_remove(flags, drv->flags)) { ret = drv->remove(dev); if (ret) goto err_remove; @@ -163,10 +201,12 @@ int device_remove(struct udevice *dev) } } - device_free(dev); + if (flags_remove(flags, drv->flags)) { + device_free(dev); - dev->seq = -1; - dev->flags &= ~DM_FLAG_ACTIVATED; + dev->seq = -1; + dev->flags &= ~DM_FLAG_ACTIVATED; + } return ret;