13ac435d3SSimon Glass /*
23ac435d3SSimon Glass * Device manager
33ac435d3SSimon Glass *
43ac435d3SSimon Glass * Copyright (c) 2014 Google, Inc
53ac435d3SSimon Glass *
63ac435d3SSimon Glass * (C) Copyright 2012
73ac435d3SSimon Glass * Pavel Herrmann <morpheus.ibis@gmail.com>
83ac435d3SSimon Glass *
93ac435d3SSimon Glass * SPDX-License-Identifier: GPL-2.0+
103ac435d3SSimon Glass */
113ac435d3SSimon Glass
123ac435d3SSimon Glass #include <common.h>
133ac435d3SSimon Glass #include <errno.h>
143ac435d3SSimon Glass #include <malloc.h>
153ac435d3SSimon Glass #include <dm/device.h>
163ac435d3SSimon Glass #include <dm/device-internal.h>
173ac435d3SSimon Glass #include <dm/uclass.h>
183ac435d3SSimon Glass #include <dm/uclass-internal.h>
193ac435d3SSimon Glass #include <dm/util.h>
203ac435d3SSimon Glass
2179725ca4SSimon Glass /**
2279725ca4SSimon Glass * device_chld_unbind() - Unbind all device's children from the device
2379725ca4SSimon Glass *
2479725ca4SSimon Glass * On error, the function continues to unbind all children, and reports the
2579725ca4SSimon Glass * first error.
2679725ca4SSimon Glass *
2779725ca4SSimon Glass * @dev: The device that is to be stripped of its children
2879725ca4SSimon Glass * @return 0 on success, -ve on error
2979725ca4SSimon Glass */
device_chld_unbind(struct udevice * dev)3079725ca4SSimon Glass static int device_chld_unbind(struct udevice *dev)
313ac435d3SSimon Glass {
323ac435d3SSimon Glass struct udevice *pos, *n;
333ac435d3SSimon Glass int ret, saved_ret = 0;
343ac435d3SSimon Glass
353ac435d3SSimon Glass assert(dev);
363ac435d3SSimon Glass
373ac435d3SSimon Glass list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
383ac435d3SSimon Glass ret = device_unbind(pos);
393ac435d3SSimon Glass if (ret && !saved_ret)
403ac435d3SSimon Glass saved_ret = ret;
413ac435d3SSimon Glass }
423ac435d3SSimon Glass
433ac435d3SSimon Glass return saved_ret;
443ac435d3SSimon Glass }
453ac435d3SSimon Glass
4679725ca4SSimon Glass /**
4779725ca4SSimon Glass * device_chld_remove() - Stop all device's children
4879725ca4SSimon Glass * @dev: The device whose children are to be removed
49706865afSStefan Roese * @pre_os_remove: Flag, if this functions is called in the pre-OS stage
5079725ca4SSimon Glass * @return 0 on success, -ve on error
5179725ca4SSimon Glass */
device_chld_remove(struct udevice * dev,uint flags)52706865afSStefan Roese static int device_chld_remove(struct udevice *dev, uint flags)
533ac435d3SSimon Glass {
543ac435d3SSimon Glass struct udevice *pos, *n;
553ac435d3SSimon Glass int ret;
563ac435d3SSimon Glass
573ac435d3SSimon Glass assert(dev);
583ac435d3SSimon Glass
593ac435d3SSimon Glass list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
60706865afSStefan Roese ret = device_remove(pos, flags);
613ac435d3SSimon Glass if (ret)
623ac435d3SSimon Glass return ret;
633ac435d3SSimon Glass }
643ac435d3SSimon Glass
653ac435d3SSimon Glass return 0;
663ac435d3SSimon Glass }
673ac435d3SSimon Glass
device_unbind(struct udevice * dev)683ac435d3SSimon Glass int device_unbind(struct udevice *dev)
693ac435d3SSimon Glass {
703479253dSSimon Glass const struct driver *drv;
713ac435d3SSimon Glass int ret;
723ac435d3SSimon Glass
733ac435d3SSimon Glass if (!dev)
743ac435d3SSimon Glass return -EINVAL;
753ac435d3SSimon Glass
763ac435d3SSimon Glass if (dev->flags & DM_FLAG_ACTIVATED)
773ac435d3SSimon Glass return -EINVAL;
783ac435d3SSimon Glass
79aed1a4ddSMasahiro Yamada if (!(dev->flags & DM_FLAG_BOUND))
80aed1a4ddSMasahiro Yamada return -EINVAL;
81aed1a4ddSMasahiro Yamada
823ac435d3SSimon Glass drv = dev->driver;
833ac435d3SSimon Glass assert(drv);
843ac435d3SSimon Glass
853ac435d3SSimon Glass if (drv->unbind) {
863ac435d3SSimon Glass ret = drv->unbind(dev);
873ac435d3SSimon Glass if (ret)
883ac435d3SSimon Glass return ret;
893ac435d3SSimon Glass }
903ac435d3SSimon Glass
9179725ca4SSimon Glass ret = device_chld_unbind(dev);
923ac435d3SSimon Glass if (ret)
933ac435d3SSimon Glass return ret;
943ac435d3SSimon Glass
95f8a85449SSimon Glass if (dev->flags & DM_FLAG_ALLOC_PDATA) {
96f8a85449SSimon Glass free(dev->platdata);
97f8a85449SSimon Glass dev->platdata = NULL;
98f8a85449SSimon Glass }
995eaed880SPrzemyslaw Marczak if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) {
1005eaed880SPrzemyslaw Marczak free(dev->uclass_platdata);
1015eaed880SPrzemyslaw Marczak dev->uclass_platdata = NULL;
1025eaed880SPrzemyslaw Marczak }
103cdc133bdSSimon Glass if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
104cdc133bdSSimon Glass free(dev->parent_platdata);
105cdc133bdSSimon Glass dev->parent_platdata = NULL;
106cdc133bdSSimon Glass }
1073ac435d3SSimon Glass ret = uclass_unbind_device(dev);
1083ac435d3SSimon Glass if (ret)
1093ac435d3SSimon Glass return ret;
1103ac435d3SSimon Glass
1113ac435d3SSimon Glass if (dev->parent)
1123ac435d3SSimon Glass list_del(&dev->sibling_node);
113608f26c5SMasahiro Yamada
114608f26c5SMasahiro Yamada devres_release_all(dev);
115608f26c5SMasahiro Yamada
116fd1c2d9bSSimon Glass if (dev->flags & DM_FLAG_NAME_ALLOCED)
117a2040facSSimon Glass free((char *)dev->name);
1183ac435d3SSimon Glass free(dev);
1193ac435d3SSimon Glass
1203ac435d3SSimon Glass return 0;
1213ac435d3SSimon Glass }
1223ac435d3SSimon Glass
1233ac435d3SSimon Glass /**
1243ac435d3SSimon Glass * device_free() - Free memory buffers allocated by a device
1253ac435d3SSimon Glass * @dev: Device that is to be started
1263ac435d3SSimon Glass */
device_free(struct udevice * dev)1273ac435d3SSimon Glass void device_free(struct udevice *dev)
1283ac435d3SSimon Glass {
1293ac435d3SSimon Glass int size;
1303ac435d3SSimon Glass
1313ac435d3SSimon Glass if (dev->driver->priv_auto_alloc_size) {
1323ac435d3SSimon Glass free(dev->priv);
1333ac435d3SSimon Glass dev->priv = NULL;
1343ac435d3SSimon Glass }
1353ac435d3SSimon Glass size = dev->uclass->uc_drv->per_device_auto_alloc_size;
1363ac435d3SSimon Glass if (size) {
1373ac435d3SSimon Glass free(dev->uclass_priv);
1383ac435d3SSimon Glass dev->uclass_priv = NULL;
1393ac435d3SSimon Glass }
1403ac435d3SSimon Glass if (dev->parent) {
1413ac435d3SSimon Glass size = dev->parent->driver->per_child_auto_alloc_size;
142dac8db2cSSimon Glass if (!size) {
143dac8db2cSSimon Glass size = dev->parent->uclass->uc_drv->
144dac8db2cSSimon Glass per_child_auto_alloc_size;
145dac8db2cSSimon Glass }
1463ac435d3SSimon Glass if (size) {
1473ac435d3SSimon Glass free(dev->parent_priv);
1483ac435d3SSimon Glass dev->parent_priv = NULL;
1493ac435d3SSimon Glass }
1503ac435d3SSimon Glass }
151608f26c5SMasahiro Yamada
152608f26c5SMasahiro Yamada devres_release_probe(dev);
1533ac435d3SSimon Glass }
1543ac435d3SSimon Glass
flags_remove(uint flags,uint drv_flags)155*426f99faSStefan Roese static bool flags_remove(uint flags, uint drv_flags)
156*426f99faSStefan Roese {
157*426f99faSStefan Roese if ((flags & DM_REMOVE_NORMAL) ||
158*426f99faSStefan Roese (flags & (drv_flags & (DM_FLAG_ACTIVE_DMA | DM_FLAG_OS_PREPARE))))
159*426f99faSStefan Roese return true;
160*426f99faSStefan Roese
161*426f99faSStefan Roese return false;
162*426f99faSStefan Roese }
163*426f99faSStefan Roese
device_remove(struct udevice * dev,uint flags)164706865afSStefan Roese int device_remove(struct udevice *dev, uint flags)
1653ac435d3SSimon Glass {
1663479253dSSimon Glass const struct driver *drv;
1673ac435d3SSimon Glass int ret;
1683ac435d3SSimon Glass
1693ac435d3SSimon Glass if (!dev)
1703ac435d3SSimon Glass return -EINVAL;
1713ac435d3SSimon Glass
1723ac435d3SSimon Glass if (!(dev->flags & DM_FLAG_ACTIVATED))
1733ac435d3SSimon Glass return 0;
1743ac435d3SSimon Glass
1753ac435d3SSimon Glass drv = dev->driver;
1763ac435d3SSimon Glass assert(drv);
1773ac435d3SSimon Glass
1783ac435d3SSimon Glass ret = uclass_pre_remove_device(dev);
1793ac435d3SSimon Glass if (ret)
1803ac435d3SSimon Glass return ret;
1813ac435d3SSimon Glass
182706865afSStefan Roese ret = device_chld_remove(dev, flags);
1833ac435d3SSimon Glass if (ret)
1843ac435d3SSimon Glass goto err;
1853ac435d3SSimon Glass
186bc85aa40SStefan Roese /*
187bc85aa40SStefan Roese * Remove the device if called with the "normal" remove flag set,
188bc85aa40SStefan Roese * or if the remove flag matches any of the drivers remove flags
189bc85aa40SStefan Roese */
190*426f99faSStefan Roese if (drv->remove && flags_remove(flags, drv->flags)) {
1913ac435d3SSimon Glass ret = drv->remove(dev);
1923ac435d3SSimon Glass if (ret)
1933ac435d3SSimon Glass goto err_remove;
1943ac435d3SSimon Glass }
1953ac435d3SSimon Glass
1963ac435d3SSimon Glass if (dev->parent && dev->parent->driver->child_post_remove) {
1973ac435d3SSimon Glass ret = dev->parent->driver->child_post_remove(dev);
1983ac435d3SSimon Glass if (ret) {
1993ac435d3SSimon Glass dm_warn("%s: Device '%s' failed child_post_remove()",
2003ac435d3SSimon Glass __func__, dev->name);
2013ac435d3SSimon Glass }
2023ac435d3SSimon Glass }
2033ac435d3SSimon Glass
204*426f99faSStefan Roese if (flags_remove(flags, drv->flags)) {
2053ac435d3SSimon Glass device_free(dev);
2063ac435d3SSimon Glass
2073ac435d3SSimon Glass dev->seq = -1;
2083ac435d3SSimon Glass dev->flags &= ~DM_FLAG_ACTIVATED;
209bc85aa40SStefan Roese }
2103ac435d3SSimon Glass
2113ac435d3SSimon Glass return ret;
2123ac435d3SSimon Glass
2133ac435d3SSimon Glass err_remove:
2143ac435d3SSimon Glass /* We can't put the children back */
2153ac435d3SSimon Glass dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
2163ac435d3SSimon Glass __func__, dev->name);
2173ac435d3SSimon Glass err:
2183ac435d3SSimon Glass ret = uclass_post_probe_device(dev);
2193ac435d3SSimon Glass if (ret) {
2203ac435d3SSimon Glass dm_warn("%s: Device '%s' failed to post_probe on error path\n",
2213ac435d3SSimon Glass __func__, dev->name);
2223ac435d3SSimon Glass }
2233ac435d3SSimon Glass
2243ac435d3SSimon Glass return ret;
2253ac435d3SSimon Glass }
226