Home | History | Annotate | Download | only in core
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Device manager
      4  *
      5  * Copyright (c) 2014 Google, Inc
      6  *
      7  * (C) Copyright 2012
      8  * Pavel Herrmann <morpheus.ibis (at) gmail.com>
      9  */
     10 
     11 #include <common.h>
     12 #include <errno.h>
     13 #include <malloc.h>
     14 #include <dm/device.h>
     15 #include <dm/device-internal.h>
     16 #include <dm/uclass.h>
     17 #include <dm/uclass-internal.h>
     18 #include <dm/util.h>
     19 
     20 /**
     21  * device_chld_unbind() - Unbind all device's children from the device
     22  *
     23  * On error, the function continues to unbind all children, and reports the
     24  * first error.
     25  *
     26  * @dev:	The device that is to be stripped of its children
     27  * @return 0 on success, -ve on error
     28  */
     29 static int device_chld_unbind(struct udevice *dev)
     30 {
     31 	struct udevice *pos, *n;
     32 	int ret, saved_ret = 0;
     33 
     34 	assert(dev);
     35 
     36 	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
     37 		ret = device_unbind(pos);
     38 		if (ret && !saved_ret)
     39 			saved_ret = ret;
     40 	}
     41 
     42 	return saved_ret;
     43 }
     44 
     45 /**
     46  * device_chld_remove() - Stop all device's children
     47  * @dev:	The device whose children are to be removed
     48  * @pre_os_remove: Flag, if this functions is called in the pre-OS stage
     49  * @return 0 on success, -ve on error
     50  */
     51 static int device_chld_remove(struct udevice *dev, uint flags)
     52 {
     53 	struct udevice *pos, *n;
     54 	int ret;
     55 
     56 	assert(dev);
     57 
     58 	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
     59 		ret = device_remove(pos, flags);
     60 		if (ret)
     61 			return ret;
     62 	}
     63 
     64 	return 0;
     65 }
     66 
     67 int device_unbind(struct udevice *dev)
     68 {
     69 	const struct driver *drv;
     70 	int ret;
     71 
     72 	if (!dev)
     73 		return -EINVAL;
     74 
     75 	if (dev->flags & DM_FLAG_ACTIVATED)
     76 		return -EINVAL;
     77 
     78 	if (!(dev->flags & DM_FLAG_BOUND))
     79 		return -EINVAL;
     80 
     81 	drv = dev->driver;
     82 	assert(drv);
     83 
     84 	if (drv->unbind) {
     85 		ret = drv->unbind(dev);
     86 		if (ret)
     87 			return ret;
     88 	}
     89 
     90 	ret = device_chld_unbind(dev);
     91 	if (ret)
     92 		return ret;
     93 
     94 	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
     95 		free(dev->platdata);
     96 		dev->platdata = NULL;
     97 	}
     98 	if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) {
     99 		free(dev->uclass_platdata);
    100 		dev->uclass_platdata = NULL;
    101 	}
    102 	if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
    103 		free(dev->parent_platdata);
    104 		dev->parent_platdata = NULL;
    105 	}
    106 	ret = uclass_unbind_device(dev);
    107 	if (ret)
    108 		return ret;
    109 
    110 	if (dev->parent)
    111 		list_del(&dev->sibling_node);
    112 
    113 	devres_release_all(dev);
    114 
    115 	if (dev->flags & DM_FLAG_NAME_ALLOCED)
    116 		free((char *)dev->name);
    117 	free(dev);
    118 
    119 	return 0;
    120 }
    121 
    122 /**
    123  * device_free() - Free memory buffers allocated by a device
    124  * @dev:	Device that is to be started
    125  */
    126 void device_free(struct udevice *dev)
    127 {
    128 	int size;
    129 
    130 	if (dev->driver->priv_auto_alloc_size) {
    131 		free(dev->priv);
    132 		dev->priv = NULL;
    133 	}
    134 	size = dev->uclass->uc_drv->per_device_auto_alloc_size;
    135 	if (size) {
    136 		free(dev->uclass_priv);
    137 		dev->uclass_priv = NULL;
    138 	}
    139 	if (dev->parent) {
    140 		size = dev->parent->driver->per_child_auto_alloc_size;
    141 		if (!size) {
    142 			size = dev->parent->uclass->uc_drv->
    143 					per_child_auto_alloc_size;
    144 		}
    145 		if (size) {
    146 			free(dev->parent_priv);
    147 			dev->parent_priv = NULL;
    148 		}
    149 	}
    150 
    151 	devres_release_probe(dev);
    152 }
    153 
    154 static bool flags_remove(uint flags, uint drv_flags)
    155 {
    156 	if ((flags & DM_REMOVE_NORMAL) ||
    157 	    (flags & (drv_flags & (DM_FLAG_ACTIVE_DMA | DM_FLAG_OS_PREPARE))))
    158 		return true;
    159 
    160 	return false;
    161 }
    162 
    163 int device_remove(struct udevice *dev, uint flags)
    164 {
    165 	const struct driver *drv;
    166 	int ret;
    167 
    168 	if (!dev)
    169 		return -EINVAL;
    170 
    171 	if (!(dev->flags & DM_FLAG_ACTIVATED))
    172 		return 0;
    173 
    174 	drv = dev->driver;
    175 	assert(drv);
    176 
    177 	ret = uclass_pre_remove_device(dev);
    178 	if (ret)
    179 		return ret;
    180 
    181 	ret = device_chld_remove(dev, flags);
    182 	if (ret)
    183 		goto err;
    184 
    185 	/*
    186 	 * Remove the device if called with the "normal" remove flag set,
    187 	 * or if the remove flag matches any of the drivers remove flags
    188 	 */
    189 	if (drv->remove && flags_remove(flags, drv->flags)) {
    190 		ret = drv->remove(dev);
    191 		if (ret)
    192 			goto err_remove;
    193 	}
    194 
    195 	if (dev->parent && dev->parent->driver->child_post_remove) {
    196 		ret = dev->parent->driver->child_post_remove(dev);
    197 		if (ret) {
    198 			dm_warn("%s: Device '%s' failed child_post_remove()",
    199 				__func__, dev->name);
    200 		}
    201 	}
    202 
    203 	if (flags_remove(flags, drv->flags)) {
    204 		device_free(dev);
    205 
    206 		dev->seq = -1;
    207 		dev->flags &= ~DM_FLAG_ACTIVATED;
    208 	}
    209 
    210 	return ret;
    211 
    212 err_remove:
    213 	/* We can't put the children back */
    214 	dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
    215 		__func__, dev->name);
    216 err:
    217 	ret = uclass_post_probe_device(dev);
    218 	if (ret) {
    219 		dm_warn("%s: Device '%s' failed to post_probe on error path\n",
    220 			__func__, dev->name);
    221 	}
    222 
    223 	return ret;
    224 }
    225