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 drv = dev->driver; 65 assert(drv); 66 67 if (drv->unbind) { 68 ret = drv->unbind(dev); 69 if (ret) 70 return ret; 71 } 72 73 ret = device_unbind_children(dev); 74 if (ret) 75 return ret; 76 77 if (dev->flags & DM_FLAG_ALLOC_PDATA) { 78 free(dev->platdata); 79 dev->platdata = NULL; 80 } 81 if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) { 82 free(dev->uclass_platdata); 83 dev->uclass_platdata = NULL; 84 } 85 if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) { 86 free(dev->parent_platdata); 87 dev->parent_platdata = NULL; 88 } 89 ret = uclass_unbind_device(dev); 90 if (ret) 91 return ret; 92 93 if (dev->parent) 94 list_del(&dev->sibling_node); 95 free(dev); 96 97 return 0; 98 } 99 100 /** 101 * device_free() - Free memory buffers allocated by a device 102 * @dev: Device that is to be started 103 */ 104 void device_free(struct udevice *dev) 105 { 106 int size; 107 108 if (dev->driver->priv_auto_alloc_size) { 109 free(dev->priv); 110 dev->priv = NULL; 111 } 112 size = dev->uclass->uc_drv->per_device_auto_alloc_size; 113 if (size) { 114 free(dev->uclass_priv); 115 dev->uclass_priv = NULL; 116 } 117 if (dev->parent) { 118 size = dev->parent->driver->per_child_auto_alloc_size; 119 if (!size) { 120 size = dev->parent->uclass->uc_drv-> 121 per_child_auto_alloc_size; 122 } 123 if (size) { 124 free(dev->parent_priv); 125 dev->parent_priv = NULL; 126 } 127 } 128 } 129 130 int device_remove(struct udevice *dev) 131 { 132 const struct driver *drv; 133 int ret; 134 135 if (!dev) 136 return -EINVAL; 137 138 if (!(dev->flags & DM_FLAG_ACTIVATED)) 139 return 0; 140 141 drv = dev->driver; 142 assert(drv); 143 144 ret = uclass_pre_remove_device(dev); 145 if (ret) 146 return ret; 147 148 ret = device_remove_children(dev); 149 if (ret) 150 goto err; 151 152 if (drv->remove) { 153 ret = drv->remove(dev); 154 if (ret) 155 goto err_remove; 156 } 157 158 if (dev->parent && dev->parent->driver->child_post_remove) { 159 ret = dev->parent->driver->child_post_remove(dev); 160 if (ret) { 161 dm_warn("%s: Device '%s' failed child_post_remove()", 162 __func__, dev->name); 163 } 164 } 165 166 device_free(dev); 167 168 dev->seq = -1; 169 dev->flags &= ~DM_FLAG_ACTIVATED; 170 171 return ret; 172 173 err_remove: 174 /* We can't put the children back */ 175 dm_warn("%s: Device '%s' failed to remove, but children are gone\n", 176 __func__, dev->name); 177 err: 178 ret = uclass_post_probe_device(dev); 179 if (ret) { 180 dm_warn("%s: Device '%s' failed to post_probe on error path\n", 181 __func__, dev->name); 182 } 183 184 return ret; 185 } 186