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@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