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