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