]> Git Repo - J-u-boot.git/blob - drivers/core/device-remove.c
Merge patch series "Endian Kconfig improvements"
[J-u-boot.git] / drivers / core / device-remove.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Device manager
4  *
5  * Copyright (c) 2014 Google, Inc
6  *
7  * (C) Copyright 2012
8  * Pavel Herrmann <[email protected]>
9  */
10
11 #define LOG_CATEGORY    LOGC_DM
12
13 #include <errno.h>
14 #include <log.h>
15 #include <malloc.h>
16 #include <dm/device.h>
17 #include <dm/device-internal.h>
18 #include <dm/uclass.h>
19 #include <dm/uclass-internal.h>
20 #include <dm/util.h>
21 #include <power-domain.h>
22 #include <asm/global_data.h>
23
24 int device_chld_unbind(struct udevice *dev, struct driver *drv)
25 {
26         struct udevice *pos, *n;
27         int ret, saved_ret = 0;
28
29         assert(dev);
30
31         device_foreach_child_safe(pos, n, dev) {
32                 if (drv && (pos->driver != drv))
33                         continue;
34
35                 ret = device_unbind(pos);
36                 if (ret && !saved_ret) {
37                         log_warning("device '%s' failed to unbind\n",
38                                     pos->name);
39                         saved_ret = ret;
40                 }
41         }
42
43         return log_ret(saved_ret);
44 }
45
46 int device_chld_remove(struct udevice *dev, struct driver *drv,
47                        uint flags)
48 {
49         struct udevice *pos, *n;
50         int result = 0;
51
52         assert(dev);
53
54         device_foreach_child_safe(pos, n, dev) {
55                 int ret;
56
57                 if (drv && (pos->driver != drv))
58                         continue;
59
60                 ret = device_remove(pos, flags);
61                 if (ret == -EPROBE_DEFER)
62                         result = ret;
63                 else if (ret && ret != -EKEYREJECTED)
64                         return ret;
65         }
66
67         return result;
68 }
69
70 int device_unbind(struct udevice *dev)
71 {
72         const struct driver *drv;
73         int ret;
74
75         if (!dev)
76                 return log_msg_ret("dev", -EINVAL);
77
78         if (dev_get_flags(dev) & DM_FLAG_ACTIVATED)
79                 return log_msg_ret("active", -EINVAL);
80
81         if (!(dev_get_flags(dev) & DM_FLAG_BOUND))
82                 return log_msg_ret("not-bound", -EINVAL);
83
84         drv = dev->driver;
85         assert(drv);
86
87         if (drv->unbind) {
88                 ret = drv->unbind(dev);
89                 if (ret)
90                         return log_msg_ret("unbind", ret);
91         }
92
93         ret = device_chld_unbind(dev, NULL);
94         if (ret)
95                 return log_msg_ret("child unbind", ret);
96
97         ret = uclass_pre_unbind_device(dev);
98         if (ret)
99                 return log_msg_ret("uc", ret);
100         if (dev_get_flags(dev) & DM_FLAG_ALLOC_PDATA) {
101                 free(dev_get_plat(dev));
102                 dev_set_plat(dev, NULL);
103         }
104         if (dev_get_flags(dev) & DM_FLAG_ALLOC_UCLASS_PDATA) {
105                 free(dev_get_uclass_plat(dev));
106                 dev_set_uclass_plat(dev, NULL);
107         }
108         if (dev_get_flags(dev) & DM_FLAG_ALLOC_PARENT_PDATA) {
109                 free(dev_get_parent_plat(dev));
110                 dev_set_parent_plat(dev, NULL);
111         }
112         ret = uclass_unbind_device(dev);
113         if (ret)
114                 return log_msg_ret("uc", ret);
115
116         if (dev->parent)
117                 list_del(&dev->sibling_node);
118
119         devres_release_all(dev);
120
121         if (dev_get_flags(dev) & DM_FLAG_NAME_ALLOCED)
122                 free((char *)dev->name);
123         free(dev);
124
125         return 0;
126 }
127
128 /**
129  * device_free() - Free memory buffers allocated by a device
130  * @dev:        Device that is to be started
131  */
132 void device_free(struct udevice *dev)
133 {
134         int size;
135
136         if (dev->driver->priv_auto) {
137                 free(dev_get_priv(dev));
138                 dev_set_priv(dev, NULL);
139         }
140         size = dev->uclass->uc_drv->per_device_auto;
141         if (size) {
142                 free(dev_get_uclass_priv(dev));
143                 dev_set_uclass_priv(dev, NULL);
144         }
145         if (dev->parent) {
146                 size = dev->parent->driver->per_child_auto;
147                 if (!size)
148                         size = dev->parent->uclass->uc_drv->per_child_auto;
149                 if (size) {
150                         free(dev_get_parent_priv(dev));
151                         dev_set_parent_priv(dev, NULL);
152                 }
153         }
154         dev_bic_flags(dev, DM_FLAG_PLATDATA_VALID);
155
156         devres_release_probe(dev);
157 }
158
159 /**
160  * flags_remove() - Figure out whether to remove a device
161  *
162  * If this is called with @flags == DM_REMOVE_NON_VITAL | DM_REMOVE_ACTIVE_DMA,
163  * then it returns 0 (=go head and remove) if the device is not matked vital
164  * but is marked DM_REMOVE_ACTIVE_DMA.
165  *
166  * If this is called with @flags == DM_REMOVE_ACTIVE_DMA,
167  * then it returns 0 (=go head and remove) if the device is marked
168  * DM_REMOVE_ACTIVE_DMA, regardless of whether it is marked vital.
169  *
170  * @flags: Flags passed to device_remove()
171  * @drv_flags: Driver flags
172  * Return: 0 if the device should be removed,
173  * -EKEYREJECTED if @flags includes a flag in DM_REMOVE_ACTIVE_ALL but
174  *      @drv_flags does not (indicates that this device has nothing to do for
175  *      DMA shutdown or OS prepare)
176  * -EPROBE_DEFER if @flags is DM_REMOVE_NON_VITAL but @drv_flags contains
177  *      DM_FLAG_VITAL (indicates the device is vital and should not be removed)
178  */
179 static int flags_remove(uint flags, uint drv_flags)
180 {
181         if (!(flags & DM_REMOVE_NORMAL)) {
182                 bool vital_match;
183                 bool active_match;
184
185                 active_match = !(flags & DM_REMOVE_ACTIVE_ALL) ||
186                         (drv_flags & flags);
187                 vital_match = !(flags & DM_REMOVE_NON_VITAL) ||
188                         !(drv_flags & DM_FLAG_VITAL);
189                 if (!vital_match)
190                         return -EPROBE_DEFER;
191                 if (!active_match)
192                         return -EKEYREJECTED;
193         }
194
195         return 0;
196 }
197
198 int device_remove(struct udevice *dev, uint flags)
199 {
200         const struct driver *drv;
201         int ret;
202
203         if (!dev)
204                 return -EINVAL;
205
206         if (!(dev_get_flags(dev) & DM_FLAG_ACTIVATED))
207                 return 0;
208
209         ret = device_notify(dev, EVT_DM_PRE_REMOVE);
210         if (ret)
211                 return ret;
212
213         /*
214          * If the child returns EKEYREJECTED, continue. It just means that it
215          * didn't match the flags.
216          */
217         ret = device_chld_remove(dev, NULL, flags);
218         if (ret && ret != -EKEYREJECTED)
219                 return ret;
220
221         /*
222          * Remove the device if called with the "normal" remove flag set,
223          * or if the remove flag matches any of the drivers remove flags
224          */
225         drv = dev->driver;
226         assert(drv);
227         ret = flags_remove(flags, drv->flags);
228         if (ret) {
229                 log_debug("%s: When removing: flags=%x, drv->flags=%x, err=%d\n",
230                           dev->name, flags, drv->flags, ret);
231                 return ret;
232         }
233
234         ret = uclass_pre_remove_device(dev);
235         if (ret)
236                 return ret;
237
238         if (drv->remove) {
239                 ret = drv->remove(dev);
240                 if (ret)
241                         goto err_remove;
242         }
243
244         if (dev->parent && dev->parent->driver->child_post_remove) {
245                 ret = dev->parent->driver->child_post_remove(dev);
246                 if (ret) {
247                         dm_warn("%s: Device '%s' failed child_post_remove()",
248                                 __func__, dev->name);
249                 }
250         }
251
252         if (!(flags & DM_REMOVE_NO_PD) &&
253             !(drv->flags &
254               (DM_FLAG_DEFAULT_PD_CTRL_OFF | DM_FLAG_LEAVE_PD_ON)) &&
255             dev != gd->cur_serial_dev)
256                 dev_power_domain_off(dev);
257
258         device_free(dev);
259
260         dev_bic_flags(dev, DM_FLAG_ACTIVATED);
261
262         ret = device_notify(dev, EVT_DM_POST_REMOVE);
263         if (ret)
264                 goto err_remove;
265
266         return 0;
267
268 err_remove:
269         /* We can't put the children back */
270         dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
271                 __func__, dev->name);
272
273         return ret;
274 }
This page took 0.042457 seconds and 4 git commands to generate.