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 free(dev); 99 100 return 0; 101 } 102 103 /** 104 * device_free() - Free memory buffers allocated by a device 105 * @dev: Device that is to be started 106 */ 107 void device_free(struct udevice *dev) 108 { 109 int size; 110 111 if (dev->driver->priv_auto_alloc_size) { 112 free(dev->priv); 113 dev->priv = NULL; 114 } 115 size = dev->uclass->uc_drv->per_device_auto_alloc_size; 116 if (size) { 117 free(dev->uclass_priv); 118 dev->uclass_priv = NULL; 119 } 120 if (dev->parent) { 121 size = dev->parent->driver->per_child_auto_alloc_size; 122 if (!size) { 123 size = dev->parent->uclass->uc_drv-> 124 per_child_auto_alloc_size; 125 } 126 if (size) { 127 free(dev->parent_priv); 128 dev->parent_priv = NULL; 129 } 130 } 131 } 132 133 int device_remove(struct udevice *dev) 134 { 135 const struct driver *drv; 136 int ret; 137 138 if (!dev) 139 return -EINVAL; 140 141 if (!(dev->flags & DM_FLAG_ACTIVATED)) 142 return 0; 143 144 drv = dev->driver; 145 assert(drv); 146 147 ret = uclass_pre_remove_device(dev); 148 if (ret) 149 return ret; 150 151 ret = device_remove_children(dev); 152 if (ret) 153 goto err; 154 155 if (drv->remove) { 156 ret = drv->remove(dev); 157 if (ret) 158 goto err_remove; 159 } 160 161 if (dev->parent && dev->parent->driver->child_post_remove) { 162 ret = dev->parent->driver->child_post_remove(dev); 163 if (ret) { 164 dm_warn("%s: Device '%s' failed child_post_remove()", 165 __func__, dev->name); 166 } 167 } 168 169 device_free(dev); 170 171 dev->seq = -1; 172 dev->flags &= ~DM_FLAG_ACTIVATED; 173 174 return ret; 175 176 err_remove: 177 /* We can't put the children back */ 178 dm_warn("%s: Device '%s' failed to remove, but children are gone\n", 179 __func__, dev->name); 180 err: 181 ret = uclass_post_probe_device(dev); 182 if (ret) { 183 dm_warn("%s: Device '%s' failed to post_probe on error path\n", 184 __func__, dev->name); 185 } 186 187 return ret; 188 } 189