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 int device_chld_unbind(struct udevice *dev, struct driver *drv) 21 { 22 struct udevice *pos, *n; 23 int ret, saved_ret = 0; 24 25 assert(dev); 26 27 list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { 28 if (drv && (pos->driver != drv)) 29 continue; 30 31 ret = device_unbind(pos); 32 if (ret && !saved_ret) 33 saved_ret = ret; 34 } 35 36 return saved_ret; 37 } 38 39 int device_chld_remove(struct udevice *dev, struct driver *drv, 40 uint flags) 41 { 42 struct udevice *pos, *n; 43 int ret; 44 45 assert(dev); 46 47 list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { 48 if (drv && (pos->driver != drv)) 49 continue; 50 51 ret = device_remove(pos, flags); 52 if (ret) 53 return ret; 54 } 55 56 return 0; 57 } 58 59 int device_unbind(struct udevice *dev) 60 { 61 const struct driver *drv; 62 int ret; 63 64 if (!dev) 65 return -EINVAL; 66 67 if (dev->flags & DM_FLAG_ACTIVATED) 68 return -EINVAL; 69 70 if (!(dev->flags & DM_FLAG_BOUND)) 71 return -EINVAL; 72 73 drv = dev->driver; 74 assert(drv); 75 76 if (drv->unbind) { 77 ret = drv->unbind(dev); 78 if (ret) 79 return ret; 80 } 81 82 ret = device_chld_unbind(dev, NULL); 83 if (ret) 84 return ret; 85 86 if (dev->flags & DM_FLAG_ALLOC_PDATA) { 87 free(dev->platdata); 88 dev->platdata = NULL; 89 } 90 if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) { 91 free(dev->uclass_platdata); 92 dev->uclass_platdata = NULL; 93 } 94 if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { 95 free(dev->parent_platdata); 96 dev->parent_platdata = NULL; 97 } 98 ret = uclass_unbind_device(dev); 99 if (ret) 100 return ret; 101 102 if (dev->parent) 103 list_del(&dev->sibling_node); 104 105 devres_release_all(dev); 106 107 if (dev->flags & DM_FLAG_NAME_ALLOCED) 108 free((char *)dev->name); 109 free(dev); 110 111 return 0; 112 } 113 114 /** 115 * device_free() - Free memory buffers allocated by a device 116 * @dev: Device that is to be started 117 */ 118 void device_free(struct udevice *dev) 119 { 120 int size; 121 122 if (dev->driver->priv_auto_alloc_size) { 123 free(dev->priv); 124 dev->priv = NULL; 125 } 126 size = dev->uclass->uc_drv->per_device_auto_alloc_size; 127 if (size) { 128 free(dev->uclass_priv); 129 dev->uclass_priv = NULL; 130 } 131 if (dev->parent) { 132 size = dev->parent->driver->per_child_auto_alloc_size; 133 if (!size) { 134 size = dev->parent->uclass->uc_drv-> 135 per_child_auto_alloc_size; 136 } 137 if (size) { 138 free(dev->parent_priv); 139 dev->parent_priv = NULL; 140 } 141 } 142 143 devres_release_probe(dev); 144 } 145 146 static bool flags_remove(uint flags, uint drv_flags) 147 { 148 if ((flags & DM_REMOVE_NORMAL) || 149 (flags & (drv_flags & (DM_FLAG_ACTIVE_DMA | DM_FLAG_OS_PREPARE)))) 150 return true; 151 152 return false; 153 } 154 155 int device_remove(struct udevice *dev, uint flags) 156 { 157 const struct driver *drv; 158 int ret; 159 160 if (!dev) 161 return -EINVAL; 162 163 if (!(dev->flags & DM_FLAG_ACTIVATED)) 164 return 0; 165 166 drv = dev->driver; 167 assert(drv); 168 169 ret = uclass_pre_remove_device(dev); 170 if (ret) 171 return ret; 172 173 ret = device_chld_remove(dev, NULL, flags); 174 if (ret) 175 goto err; 176 177 /* 178 * Remove the device if called with the "normal" remove flag set, 179 * or if the remove flag matches any of the drivers remove flags 180 */ 181 if (drv->remove && flags_remove(flags, drv->flags)) { 182 ret = drv->remove(dev); 183 if (ret) 184 goto err_remove; 185 } 186 187 if (dev->parent && dev->parent->driver->child_post_remove) { 188 ret = dev->parent->driver->child_post_remove(dev); 189 if (ret) { 190 dm_warn("%s: Device '%s' failed child_post_remove()", 191 __func__, dev->name); 192 } 193 } 194 195 if (flags_remove(flags, drv->flags)) { 196 device_free(dev); 197 198 dev->seq = -1; 199 dev->flags &= ~DM_FLAG_ACTIVATED; 200 } 201 202 return ret; 203 204 err_remove: 205 /* We can't put the children back */ 206 dm_warn("%s: Device '%s' failed to remove, but children are gone\n", 207 __func__, dev->name); 208 err: 209 ret = uclass_post_probe_device(dev); 210 if (ret) { 211 dm_warn("%s: Device '%s' failed to post_probe on error path\n", 212 __func__, dev->name); 213 } 214 215 return ret; 216 } 217