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