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 * @return 0 on success, -ve on error 50 */ 51 static int device_chld_remove(struct udevice *dev) 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); 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 free(dev); 116 117 return 0; 118 } 119 120 /** 121 * device_free() - Free memory buffers allocated by a device 122 * @dev: Device that is to be started 123 */ 124 void device_free(struct udevice *dev) 125 { 126 int size; 127 128 if (dev->driver->priv_auto_alloc_size) { 129 free(dev->priv); 130 dev->priv = NULL; 131 } 132 size = dev->uclass->uc_drv->per_device_auto_alloc_size; 133 if (size) { 134 free(dev->uclass_priv); 135 dev->uclass_priv = NULL; 136 } 137 if (dev->parent) { 138 size = dev->parent->driver->per_child_auto_alloc_size; 139 if (!size) { 140 size = dev->parent->uclass->uc_drv-> 141 per_child_auto_alloc_size; 142 } 143 if (size) { 144 free(dev->parent_priv); 145 dev->parent_priv = NULL; 146 } 147 } 148 149 devres_release_probe(dev); 150 } 151 152 int device_remove(struct udevice *dev) 153 { 154 const struct driver *drv; 155 int ret; 156 157 if (!dev) 158 return -EINVAL; 159 160 if (!(dev->flags & DM_FLAG_ACTIVATED)) 161 return 0; 162 163 drv = dev->driver; 164 assert(drv); 165 166 ret = uclass_pre_remove_device(dev); 167 if (ret) 168 return ret; 169 170 ret = device_chld_remove(dev); 171 if (ret) 172 goto err; 173 174 if (drv->remove) { 175 ret = drv->remove(dev); 176 if (ret) 177 goto err_remove; 178 } 179 180 if (dev->parent && dev->parent->driver->child_post_remove) { 181 ret = dev->parent->driver->child_post_remove(dev); 182 if (ret) { 183 dm_warn("%s: Device '%s' failed child_post_remove()", 184 __func__, dev->name); 185 } 186 } 187 188 device_free(dev); 189 190 dev->seq = -1; 191 dev->flags &= ~DM_FLAG_ACTIVATED; 192 193 return ret; 194 195 err_remove: 196 /* We can't put the children back */ 197 dm_warn("%s: Device '%s' failed to remove, but children are gone\n", 198 __func__, dev->name); 199 err: 200 ret = uclass_post_probe_device(dev); 201 if (ret) { 202 dm_warn("%s: Device '%s' failed to post_probe on error path\n", 203 __func__, dev->name); 204 } 205 206 return ret; 207 } 208