123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Device manager
- *
- * Copyright (c) 2014 Google, Inc
- *
- * (C) Copyright 2012
- * Pavel Herrmann <morpheus.ibis@gmail.com>
- */
- #include <common.h>
- #include <errno.h>
- #include <malloc.h>
- #include <dm/device.h>
- #include <dm/device-internal.h>
- #include <dm/uclass.h>
- #include <dm/uclass-internal.h>
- #include <dm/util.h>
- int device_chld_unbind(struct udevice *dev, struct driver *drv)
- {
- struct udevice *pos, *n;
- int ret, saved_ret = 0;
- assert(dev);
- list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
- if (drv && (pos->driver != drv))
- continue;
- ret = device_unbind(pos);
- if (ret && !saved_ret)
- saved_ret = ret;
- }
- return saved_ret;
- }
- int device_chld_remove(struct udevice *dev, struct driver *drv,
- uint flags)
- {
- struct udevice *pos, *n;
- int ret;
- assert(dev);
- list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
- if (drv && (pos->driver != drv))
- continue;
- ret = device_remove(pos, flags);
- if (ret)
- return ret;
- }
- return 0;
- }
- int device_unbind(struct udevice *dev)
- {
- const struct driver *drv;
- int ret;
- if (!dev)
- return -EINVAL;
- if (dev->flags & DM_FLAG_ACTIVATED)
- return -EINVAL;
- if (!(dev->flags & DM_FLAG_BOUND))
- return -EINVAL;
- drv = dev->driver;
- assert(drv);
- if (drv->unbind) {
- ret = drv->unbind(dev);
- if (ret)
- return ret;
- }
- ret = device_chld_unbind(dev, NULL);
- if (ret)
- return ret;
- if (dev->flags & DM_FLAG_ALLOC_PDATA) {
- 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;
- }
- ret = uclass_unbind_device(dev);
- if (ret)
- return ret;
- 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;
- }
- /**
- * device_free() - Free memory buffers allocated by a device
- * @dev: Device that is to be started
- */
- void device_free(struct udevice *dev)
- {
- int size;
- if (dev->driver->priv_auto_alloc_size) {
- free(dev->priv);
- dev->priv = NULL;
- }
- size = dev->uclass->uc_drv->per_device_auto_alloc_size;
- if (size) {
- free(dev->uclass_priv);
- dev->uclass_priv = NULL;
- }
- 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) {
- free(dev->parent_priv);
- 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, uint flags)
- {
- const struct driver *drv;
- int ret;
- if (!dev)
- return -EINVAL;
- if (!(dev->flags & DM_FLAG_ACTIVATED))
- return 0;
- drv = dev->driver;
- assert(drv);
- ret = uclass_pre_remove_device(dev);
- if (ret)
- return ret;
- ret = device_chld_remove(dev, NULL, flags);
- if (ret)
- goto err;
- /*
- * 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;
- }
- if (dev->parent && dev->parent->driver->child_post_remove) {
- ret = dev->parent->driver->child_post_remove(dev);
- if (ret) {
- dm_warn("%s: Device '%s' failed child_post_remove()",
- __func__, dev->name);
- }
- }
- if (flags_remove(flags, drv->flags)) {
- device_free(dev);
- dev->seq = -1;
- dev->flags &= ~DM_FLAG_ACTIVATED;
- }
- return ret;
- err_remove:
- /* We can't put the children back */
- dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
- __func__, dev->name);
- err:
- ret = uclass_post_probe_device(dev);
- if (ret) {
- dm_warn("%s: Device '%s' failed to post_probe on error path\n",
- __func__, dev->name);
- }
- return ret;
- }
|