]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/core/device.c
Merge branch 'u-boot-sh/rmobile' into 'u-boot-arm/master'
[karo-tx-uboot.git] / drivers / core / device.c
1 /*
2  * Device manager
3  *
4  * Copyright (c) 2013 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 <malloc.h>
14 #include <dm/device.h>
15 #include <dm/device-internal.h>
16 #include <dm/lists.h>
17 #include <dm/platdata.h>
18 #include <dm/uclass.h>
19 #include <dm/uclass-internal.h>
20 #include <dm/util.h>
21 #include <linux/err.h>
22 #include <linux/list.h>
23
24 /**
25  * device_chld_unbind() - Unbind all device's children from the device
26  *
27  * On error, the function continues to unbind all children, and reports the
28  * first error.
29  *
30  * @dev:        The device that is to be stripped of its children
31  * @return 0 on success, -ve on error
32  */
33 static int device_chld_unbind(struct udevice *dev)
34 {
35         struct udevice *pos, *n;
36         int ret, saved_ret = 0;
37
38         assert(dev);
39
40         list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
41                 ret = device_unbind(pos);
42                 if (ret && !saved_ret)
43                         saved_ret = ret;
44         }
45
46         return saved_ret;
47 }
48
49 /**
50  * device_chld_remove() - Stop all device's children
51  * @dev:        The device whose children are to be removed
52  * @return 0 on success, -ve on error
53  */
54 static int device_chld_remove(struct udevice *dev)
55 {
56         struct udevice *pos, *n;
57         int ret;
58
59         assert(dev);
60
61         list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
62                 ret = device_remove(pos);
63                 if (ret)
64                         return ret;
65         }
66
67         return 0;
68 }
69
70 int device_bind(struct udevice *parent, struct driver *drv, const char *name,
71                 void *platdata, int of_offset, struct udevice **devp)
72 {
73         struct udevice *dev;
74         struct uclass *uc;
75         int ret = 0;
76
77         *devp = NULL;
78         if (!name)
79                 return -EINVAL;
80
81         ret = uclass_get(drv->id, &uc);
82         if (ret)
83                 return ret;
84
85         dev = calloc(1, sizeof(struct udevice));
86         if (!dev)
87                 return -ENOMEM;
88
89         INIT_LIST_HEAD(&dev->sibling_node);
90         INIT_LIST_HEAD(&dev->child_head);
91         INIT_LIST_HEAD(&dev->uclass_node);
92         dev->platdata = platdata;
93         dev->name = name;
94         dev->of_offset = of_offset;
95         dev->parent = parent;
96         dev->driver = drv;
97         dev->uclass = uc;
98         if (!dev->platdata && drv->platdata_auto_alloc_size)
99                 dev->flags |= DM_FLAG_ALLOC_PDATA;
100
101         /* put dev into parent's successor list */
102         if (parent)
103                 list_add_tail(&dev->sibling_node, &parent->child_head);
104
105         ret = uclass_bind_device(dev);
106         if (ret)
107                 goto fail_bind;
108
109         /* if we fail to bind we remove device from successors and free it */
110         if (drv->bind) {
111                 ret = drv->bind(dev);
112                 if (ret) {
113                         if (uclass_unbind_device(dev)) {
114                                 dm_warn("Failed to unbind dev '%s' on error path\n",
115                                         dev->name);
116                         }
117                         goto fail_bind;
118                 }
119         }
120         if (parent)
121                 dm_dbg("Bound device %s to %s\n", dev->name, parent->name);
122         *devp = dev;
123
124         return 0;
125
126 fail_bind:
127         list_del(&dev->sibling_node);
128         free(dev);
129         return ret;
130 }
131
132 int device_bind_by_name(struct udevice *parent, const struct driver_info *info,
133                         struct udevice **devp)
134 {
135         struct driver *drv;
136
137         drv = lists_driver_lookup_name(info->name);
138         if (!drv)
139                 return -ENOENT;
140
141         return device_bind(parent, drv, info->name, (void *)info->platdata,
142                            -1, devp);
143 }
144
145 int device_unbind(struct udevice *dev)
146 {
147         struct driver *drv;
148         int ret;
149
150         if (!dev)
151                 return -EINVAL;
152
153         if (dev->flags & DM_FLAG_ACTIVATED)
154                 return -EINVAL;
155
156         drv = dev->driver;
157         assert(drv);
158
159         if (drv->unbind) {
160                 ret = drv->unbind(dev);
161                 if (ret)
162                         return ret;
163         }
164
165         ret = device_chld_unbind(dev);
166         if (ret)
167                 return ret;
168
169         ret = uclass_unbind_device(dev);
170         if (ret)
171                 return ret;
172
173         if (dev->parent)
174                 list_del(&dev->sibling_node);
175         free(dev);
176
177         return 0;
178 }
179
180 /**
181  * device_free() - Free memory buffers allocated by a device
182  * @dev:        Device that is to be started
183  */
184 static void device_free(struct udevice *dev)
185 {
186         int size;
187
188         if (dev->driver->priv_auto_alloc_size) {
189                 free(dev->priv);
190                 dev->priv = NULL;
191         }
192         if (dev->flags & DM_FLAG_ALLOC_PDATA) {
193                 free(dev->platdata);
194                 dev->platdata = NULL;
195         }
196         size = dev->uclass->uc_drv->per_device_auto_alloc_size;
197         if (size) {
198                 free(dev->uclass_priv);
199                 dev->uclass_priv = NULL;
200         }
201 }
202
203 int device_probe(struct udevice *dev)
204 {
205         struct driver *drv;
206         int size = 0;
207         int ret;
208
209         if (!dev)
210                 return -EINVAL;
211
212         if (dev->flags & DM_FLAG_ACTIVATED)
213                 return 0;
214
215         drv = dev->driver;
216         assert(drv);
217
218         /* Allocate private data and platdata if requested */
219         if (drv->priv_auto_alloc_size) {
220                 dev->priv = calloc(1, drv->priv_auto_alloc_size);
221                 if (!dev->priv) {
222                         ret = -ENOMEM;
223                         goto fail;
224                 }
225         }
226         /* Allocate private data if requested */
227         if (dev->flags & DM_FLAG_ALLOC_PDATA) {
228                 dev->platdata = calloc(1, drv->platdata_auto_alloc_size);
229                 if (!dev->platdata) {
230                         ret = -ENOMEM;
231                         goto fail;
232                 }
233         }
234         size = dev->uclass->uc_drv->per_device_auto_alloc_size;
235         if (size) {
236                 dev->uclass_priv = calloc(1, size);
237                 if (!dev->uclass_priv) {
238                         ret = -ENOMEM;
239                         goto fail;
240                 }
241         }
242
243         /* Ensure all parents are probed */
244         if (dev->parent) {
245                 ret = device_probe(dev->parent);
246                 if (ret)
247                         goto fail;
248         }
249
250         if (drv->ofdata_to_platdata && dev->of_offset >= 0) {
251                 ret = drv->ofdata_to_platdata(dev);
252                 if (ret)
253                         goto fail;
254         }
255
256         if (drv->probe) {
257                 ret = drv->probe(dev);
258                 if (ret)
259                         goto fail;
260         }
261
262         dev->flags |= DM_FLAG_ACTIVATED;
263
264         ret = uclass_post_probe_device(dev);
265         if (ret) {
266                 dev->flags &= ~DM_FLAG_ACTIVATED;
267                 goto fail_uclass;
268         }
269
270         return 0;
271 fail_uclass:
272         if (device_remove(dev)) {
273                 dm_warn("%s: Device '%s' failed to remove on error path\n",
274                         __func__, dev->name);
275         }
276 fail:
277         device_free(dev);
278
279         return ret;
280 }
281
282 int device_remove(struct udevice *dev)
283 {
284         struct driver *drv;
285         int ret;
286
287         if (!dev)
288                 return -EINVAL;
289
290         if (!(dev->flags & DM_FLAG_ACTIVATED))
291                 return 0;
292
293         drv = dev->driver;
294         assert(drv);
295
296         ret = uclass_pre_remove_device(dev);
297         if (ret)
298                 return ret;
299
300         ret = device_chld_remove(dev);
301         if (ret)
302                 goto err;
303
304         if (drv->remove) {
305                 ret = drv->remove(dev);
306                 if (ret)
307                         goto err_remove;
308         }
309
310         device_free(dev);
311
312         dev->flags &= ~DM_FLAG_ACTIVATED;
313
314         return 0;
315
316 err_remove:
317         /* We can't put the children back */
318         dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
319                 __func__, dev->name);
320 err:
321         ret = uclass_post_probe_device(dev);
322         if (ret) {
323                 dm_warn("%s: Device '%s' failed to post_probe on error path\n",
324                         __func__, dev->name);
325         }
326
327         return ret;
328 }
329
330 void *dev_get_platdata(struct udevice *dev)
331 {
332         if (!dev) {
333                 dm_warn("%s: null device", __func__);
334                 return NULL;
335         }
336
337         return dev->platdata;
338 }
339
340 void *dev_get_priv(struct udevice *dev)
341 {
342         if (!dev) {
343                 dm_warn("%s: null device", __func__);
344                 return NULL;
345         }
346
347         return dev->priv;
348 }