"Das U-Boot" Source Tree
at master 274 lines 6.1 kB view raw
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#define LOG_CATEGORY LOGC_DM 12 13#include <errno.h> 14#include <log.h> 15#include <malloc.h> 16#include <dm/device.h> 17#include <dm/device-internal.h> 18#include <dm/uclass.h> 19#include <dm/uclass-internal.h> 20#include <dm/util.h> 21#include <power-domain.h> 22#include <asm/global_data.h> 23 24int device_chld_unbind(struct udevice *dev, struct driver *drv) 25{ 26 struct udevice *pos, *n; 27 int ret, saved_ret = 0; 28 29 assert(dev); 30 31 device_foreach_child_safe(pos, n, dev) { 32 if (drv && (pos->driver != drv)) 33 continue; 34 35 ret = device_unbind(pos); 36 if (ret && !saved_ret) { 37 log_warning("device '%s' failed to unbind\n", 38 pos->name); 39 saved_ret = ret; 40 } 41 } 42 43 return log_ret(saved_ret); 44} 45 46int device_chld_remove(struct udevice *dev, struct driver *drv, 47 uint flags) 48{ 49 struct udevice *pos, *n; 50 int result = 0; 51 52 assert(dev); 53 54 device_foreach_child_safe(pos, n, dev) { 55 int ret; 56 57 if (drv && (pos->driver != drv)) 58 continue; 59 60 ret = device_remove(pos, flags); 61 if (ret == -EPROBE_DEFER) 62 result = ret; 63 else if (ret && ret != -EKEYREJECTED) 64 return ret; 65 } 66 67 return result; 68} 69 70int device_unbind(struct udevice *dev) 71{ 72 const struct driver *drv; 73 int ret; 74 75 if (!dev) 76 return log_msg_ret("dev", -EINVAL); 77 78 if (dev_get_flags(dev) & DM_FLAG_ACTIVATED) 79 return log_msg_ret("active", -EINVAL); 80 81 if (!(dev_get_flags(dev) & DM_FLAG_BOUND)) 82 return log_msg_ret("not-bound", -EINVAL); 83 84 drv = dev->driver; 85 assert(drv); 86 87 if (drv->unbind) { 88 ret = drv->unbind(dev); 89 if (ret) 90 return log_msg_ret("unbind", ret); 91 } 92 93 ret = device_chld_unbind(dev, NULL); 94 if (ret) 95 return log_msg_ret("child unbind", ret); 96 97 ret = uclass_pre_unbind_device(dev); 98 if (ret) 99 return log_msg_ret("uc", ret); 100 if (dev_get_flags(dev) & DM_FLAG_ALLOC_PDATA) { 101 free(dev_get_plat(dev)); 102 dev_set_plat(dev, NULL); 103 } 104 if (dev_get_flags(dev) & DM_FLAG_ALLOC_UCLASS_PDATA) { 105 free(dev_get_uclass_plat(dev)); 106 dev_set_uclass_plat(dev, NULL); 107 } 108 if (dev_get_flags(dev) & DM_FLAG_ALLOC_PARENT_PDATA) { 109 free(dev_get_parent_plat(dev)); 110 dev_set_parent_plat(dev, NULL); 111 } 112 ret = uclass_unbind_device(dev); 113 if (ret) 114 return log_msg_ret("uc", ret); 115 116 if (dev->parent) 117 list_del(&dev->sibling_node); 118 119 devres_release_all(dev); 120 121 if (dev_get_flags(dev) & DM_FLAG_NAME_ALLOCED) 122 free((char *)dev->name); 123 free(dev); 124 125 return 0; 126} 127 128/** 129 * device_free() - Free memory buffers allocated by a device 130 * @dev: Device that is to be started 131 */ 132void device_free(struct udevice *dev) 133{ 134 int size; 135 136 if (dev->driver->priv_auto) { 137 free(dev_get_priv(dev)); 138 dev_set_priv(dev, NULL); 139 } 140 size = dev->uclass->uc_drv->per_device_auto; 141 if (size) { 142 free(dev_get_uclass_priv(dev)); 143 dev_set_uclass_priv(dev, NULL); 144 } 145 if (dev->parent) { 146 size = dev->parent->driver->per_child_auto; 147 if (!size) 148 size = dev->parent->uclass->uc_drv->per_child_auto; 149 if (size) { 150 free(dev_get_parent_priv(dev)); 151 dev_set_parent_priv(dev, NULL); 152 } 153 } 154 dev_bic_flags(dev, DM_FLAG_PLATDATA_VALID); 155 156 devres_release_probe(dev); 157} 158 159/** 160 * flags_remove() - Figure out whether to remove a device 161 * 162 * If this is called with @flags == DM_REMOVE_NON_VITAL | DM_REMOVE_ACTIVE_DMA, 163 * then it returns 0 (=go head and remove) if the device is not matked vital 164 * but is marked DM_REMOVE_ACTIVE_DMA. 165 * 166 * If this is called with @flags == DM_REMOVE_ACTIVE_DMA, 167 * then it returns 0 (=go head and remove) if the device is marked 168 * DM_REMOVE_ACTIVE_DMA, regardless of whether it is marked vital. 169 * 170 * @flags: Flags passed to device_remove() 171 * @drv_flags: Driver flags 172 * Return: 0 if the device should be removed, 173 * -EKEYREJECTED if @flags includes a flag in DM_REMOVE_ACTIVE_ALL but 174 * @drv_flags does not (indicates that this device has nothing to do for 175 * DMA shutdown or OS prepare) 176 * -EPROBE_DEFER if @flags is DM_REMOVE_NON_VITAL but @drv_flags contains 177 * DM_FLAG_VITAL (indicates the device is vital and should not be removed) 178 */ 179static int flags_remove(uint flags, uint drv_flags) 180{ 181 if (!(flags & DM_REMOVE_NORMAL)) { 182 bool vital_match; 183 bool active_match; 184 185 active_match = !(flags & DM_REMOVE_ACTIVE_ALL) || 186 (drv_flags & flags); 187 vital_match = !(flags & DM_REMOVE_NON_VITAL) || 188 !(drv_flags & DM_FLAG_VITAL); 189 if (!vital_match) 190 return -EPROBE_DEFER; 191 if (!active_match) 192 return -EKEYREJECTED; 193 } 194 195 return 0; 196} 197 198int device_remove(struct udevice *dev, uint flags) 199{ 200 const struct driver *drv; 201 int ret; 202 203 if (!dev) 204 return -EINVAL; 205 206 if (!(dev_get_flags(dev) & DM_FLAG_ACTIVATED)) 207 return 0; 208 209 ret = device_notify(dev, EVT_DM_PRE_REMOVE); 210 if (ret) 211 return ret; 212 213 /* 214 * If the child returns EKEYREJECTED, continue. It just means that it 215 * didn't match the flags. 216 */ 217 ret = device_chld_remove(dev, NULL, flags); 218 if (ret && ret != -EKEYREJECTED) 219 return ret; 220 221 /* 222 * Remove the device if called with the "normal" remove flag set, 223 * or if the remove flag matches any of the drivers remove flags 224 */ 225 drv = dev->driver; 226 assert(drv); 227 ret = flags_remove(flags, drv->flags); 228 if (ret) { 229 log_debug("%s: When removing: flags=%x, drv->flags=%x, err=%d\n", 230 dev->name, flags, drv->flags, ret); 231 return ret; 232 } 233 234 ret = uclass_pre_remove_device(dev); 235 if (ret) 236 return ret; 237 238 if (drv->remove) { 239 ret = drv->remove(dev); 240 if (ret) 241 goto err_remove; 242 } 243 244 if (dev->parent && dev->parent->driver->child_post_remove) { 245 ret = dev->parent->driver->child_post_remove(dev); 246 if (ret) { 247 dm_warn("%s: Device '%s' failed child_post_remove()", 248 __func__, dev->name); 249 } 250 } 251 252 if (!(flags & DM_REMOVE_NO_PD) && 253 !(drv->flags & 254 (DM_FLAG_DEFAULT_PD_CTRL_OFF | DM_FLAG_LEAVE_PD_ON)) && 255 dev != gd->cur_serial_dev) 256 dev_power_domain_off(dev); 257 258 device_free(dev); 259 260 dev_bic_flags(dev, DM_FLAG_ACTIVATED); 261 262 ret = device_notify(dev, EVT_DM_POST_REMOVE); 263 if (ret) 264 goto err_remove; 265 266 return 0; 267 268err_remove: 269 /* We can't put the children back */ 270 dm_warn("%s: Device '%s' failed to remove, but children are gone\n", 271 __func__, dev->name); 272 273 return ret; 274}