xref: /openbmc/u-boot/drivers/core/device-remove.c (revision 5750e5e2)
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