]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/core/device.c
dm: core: Add a post_bind method for parents
[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 <fdtdec.h>
14 #include <malloc.h>
15 #include <dm/device.h>
16 #include <dm/device-internal.h>
17 #include <dm/lists.h>
18 #include <dm/platdata.h>
19 #include <dm/uclass.h>
20 #include <dm/uclass-internal.h>
21 #include <dm/util.h>
22 #include <linux/err.h>
23 #include <linux/list.h>
24
25 DECLARE_GLOBAL_DATA_PTR;
26
27 int device_bind(struct udevice *parent, struct driver *drv, const char *name,
28                 void *platdata, int of_offset, struct udevice **devp)
29 {
30         struct udevice *dev;
31         struct uclass *uc;
32         int ret = 0;
33
34         *devp = NULL;
35         if (!name)
36                 return -EINVAL;
37
38         ret = uclass_get(drv->id, &uc);
39         if (ret)
40                 return ret;
41
42         dev = calloc(1, sizeof(struct udevice));
43         if (!dev)
44                 return -ENOMEM;
45
46         INIT_LIST_HEAD(&dev->sibling_node);
47         INIT_LIST_HEAD(&dev->child_head);
48         INIT_LIST_HEAD(&dev->uclass_node);
49         dev->platdata = platdata;
50         dev->name = name;
51         dev->of_offset = of_offset;
52         dev->parent = parent;
53         dev->driver = drv;
54         dev->uclass = uc;
55
56         /*
57          * For some devices, such as a SPI or I2C bus, the 'reg' property
58          * is a reasonable indicator of the sequence number. But if there is
59          * an alias, we use that in preference. In any case, this is just
60          * a 'requested' sequence, and will be resolved (and ->seq updated)
61          * when the device is probed.
62          */
63         dev->seq = -1;
64 #ifdef CONFIG_OF_CONTROL
65         dev->req_seq = fdtdec_get_int(gd->fdt_blob, of_offset, "reg", -1);
66         if (!IS_ERR_VALUE(dev->req_seq))
67                 dev->req_seq &= INT_MAX;
68         if (uc->uc_drv->name && of_offset != -1) {
69                 fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, of_offset,
70                                      &dev->req_seq);
71         }
72 #else
73         dev->req_seq = -1;
74 #endif
75         if (!dev->platdata && drv->platdata_auto_alloc_size) {
76                 dev->flags |= DM_FLAG_ALLOC_PDATA;
77                 dev->platdata = calloc(1, drv->platdata_auto_alloc_size);
78                 if (!dev->platdata) {
79                         ret = -ENOMEM;
80                         goto fail_alloc1;
81                 }
82         }
83         if (parent) {
84                 int size = parent->driver->per_child_platdata_auto_alloc_size;
85
86                 if (!size) {
87                         size = parent->uclass->uc_drv->
88                                         per_child_platdata_auto_alloc_size;
89                 }
90                 if (size) {
91                         dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA;
92                         dev->parent_platdata = calloc(1, size);
93                         if (!dev->parent_platdata) {
94                                 ret = -ENOMEM;
95                                 goto fail_alloc2;
96                         }
97                 }
98         }
99
100         /* put dev into parent's successor list */
101         if (parent)
102                 list_add_tail(&dev->sibling_node, &parent->child_head);
103
104         ret = uclass_bind_device(dev);
105         if (ret)
106                 goto fail_uclass_bind;
107
108         /* if we fail to bind we remove device from successors and free it */
109         if (drv->bind) {
110                 ret = drv->bind(dev);
111                 if (ret)
112                         goto fail_bind;
113         }
114         if (parent && parent->driver->child_post_bind) {
115                 ret = parent->driver->child_post_bind(dev);
116                 if (ret)
117                         goto fail_child_post_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_child_post_bind:
127         if (drv->unbind && drv->unbind(dev)) {
128                 dm_warn("unbind() method failed on dev '%s' on error path\n",
129                         dev->name);
130         }
131
132 fail_bind:
133         if (uclass_unbind_device(dev)) {
134                 dm_warn("Failed to unbind dev '%s' on error path\n",
135                         dev->name);
136         }
137 fail_uclass_bind:
138         list_del(&dev->sibling_node);
139         if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
140                 free(dev->parent_platdata);
141                 dev->parent_platdata = NULL;
142         }
143 fail_alloc2:
144         if (dev->flags & DM_FLAG_ALLOC_PDATA) {
145                 free(dev->platdata);
146                 dev->platdata = NULL;
147         }
148 fail_alloc1:
149         free(dev);
150
151         return ret;
152 }
153
154 int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
155                         const struct driver_info *info, struct udevice **devp)
156 {
157         struct driver *drv;
158
159         drv = lists_driver_lookup_name(info->name);
160         if (!drv)
161                 return -ENOENT;
162         if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC))
163                 return -EPERM;
164
165         return device_bind(parent, drv, info->name, (void *)info->platdata,
166                            -1, devp);
167 }
168
169 int device_probe_child(struct udevice *dev, void *parent_priv)
170 {
171         struct driver *drv;
172         int size = 0;
173         int ret;
174         int seq;
175
176         if (!dev)
177                 return -EINVAL;
178
179         if (dev->flags & DM_FLAG_ACTIVATED)
180                 return 0;
181
182         drv = dev->driver;
183         assert(drv);
184
185         /* Allocate private data if requested */
186         if (drv->priv_auto_alloc_size) {
187                 dev->priv = calloc(1, drv->priv_auto_alloc_size);
188                 if (!dev->priv) {
189                         ret = -ENOMEM;
190                         goto fail;
191                 }
192         }
193         /* Allocate private data if requested */
194         size = dev->uclass->uc_drv->per_device_auto_alloc_size;
195         if (size) {
196                 dev->uclass_priv = calloc(1, size);
197                 if (!dev->uclass_priv) {
198                         ret = -ENOMEM;
199                         goto fail;
200                 }
201         }
202
203         /* Ensure all parents are probed */
204         if (dev->parent) {
205                 size = dev->parent->driver->per_child_auto_alloc_size;
206                 if (size) {
207                         dev->parent_priv = calloc(1, size);
208                         if (!dev->parent_priv) {
209                                 ret = -ENOMEM;
210                                 goto fail;
211                         }
212                         if (parent_priv)
213                                 memcpy(dev->parent_priv, parent_priv, size);
214                 }
215
216                 ret = device_probe(dev->parent);
217                 if (ret)
218                         goto fail;
219         }
220
221         seq = uclass_resolve_seq(dev);
222         if (seq < 0) {
223                 ret = seq;
224                 goto fail;
225         }
226         dev->seq = seq;
227
228         if (dev->parent && dev->parent->driver->child_pre_probe) {
229                 ret = dev->parent->driver->child_pre_probe(dev);
230                 if (ret)
231                         goto fail;
232         }
233
234         if (drv->ofdata_to_platdata && dev->of_offset >= 0) {
235                 ret = drv->ofdata_to_platdata(dev);
236                 if (ret)
237                         goto fail;
238         }
239
240         if (drv->probe) {
241                 ret = drv->probe(dev);
242                 if (ret)
243                         goto fail;
244         }
245
246         dev->flags |= DM_FLAG_ACTIVATED;
247
248         ret = uclass_post_probe_device(dev);
249         if (ret) {
250                 dev->flags &= ~DM_FLAG_ACTIVATED;
251                 goto fail_uclass;
252         }
253
254         return 0;
255 fail_uclass:
256         if (device_remove(dev)) {
257                 dm_warn("%s: Device '%s' failed to remove on error path\n",
258                         __func__, dev->name);
259         }
260 fail:
261         dev->seq = -1;
262         device_free(dev);
263
264         return ret;
265 }
266
267 int device_probe(struct udevice *dev)
268 {
269         return device_probe_child(dev, NULL);
270 }
271
272 void *dev_get_platdata(struct udevice *dev)
273 {
274         if (!dev) {
275                 dm_warn("%s: null device\n", __func__);
276                 return NULL;
277         }
278
279         return dev->platdata;
280 }
281
282 void *dev_get_parent_platdata(struct udevice *dev)
283 {
284         if (!dev) {
285                 dm_warn("%s: null device", __func__);
286                 return NULL;
287         }
288
289         return dev->parent_platdata;
290 }
291
292 void *dev_get_priv(struct udevice *dev)
293 {
294         if (!dev) {
295                 dm_warn("%s: null device\n", __func__);
296                 return NULL;
297         }
298
299         return dev->priv;
300 }
301
302 void *dev_get_parentdata(struct udevice *dev)
303 {
304         if (!dev) {
305                 dm_warn("%s: null device\n", __func__);
306                 return NULL;
307         }
308
309         return dev->parent_priv;
310 }
311
312 static int device_get_device_tail(struct udevice *dev, int ret,
313                                   struct udevice **devp)
314 {
315         if (ret)
316                 return ret;
317
318         ret = device_probe(dev);
319         if (ret)
320                 return ret;
321
322         *devp = dev;
323
324         return 0;
325 }
326
327 int device_get_child(struct udevice *parent, int index, struct udevice **devp)
328 {
329         struct udevice *dev;
330
331         list_for_each_entry(dev, &parent->child_head, sibling_node) {
332                 if (!index--)
333                         return device_get_device_tail(dev, 0, devp);
334         }
335
336         return -ENODEV;
337 }
338
339 int device_find_child_by_seq(struct udevice *parent, int seq_or_req_seq,
340                              bool find_req_seq, struct udevice **devp)
341 {
342         struct udevice *dev;
343
344         *devp = NULL;
345         if (seq_or_req_seq == -1)
346                 return -ENODEV;
347
348         list_for_each_entry(dev, &parent->child_head, sibling_node) {
349                 if ((find_req_seq ? dev->req_seq : dev->seq) ==
350                                 seq_or_req_seq) {
351                         *devp = dev;
352                         return 0;
353                 }
354         }
355
356         return -ENODEV;
357 }
358
359 int device_get_child_by_seq(struct udevice *parent, int seq,
360                             struct udevice **devp)
361 {
362         struct udevice *dev;
363         int ret;
364
365         *devp = NULL;
366         ret = device_find_child_by_seq(parent, seq, false, &dev);
367         if (ret == -ENODEV) {
368                 /*
369                  * We didn't find it in probed devices. See if there is one
370                  * that will request this seq if probed.
371                  */
372                 ret = device_find_child_by_seq(parent, seq, true, &dev);
373         }
374         return device_get_device_tail(dev, ret, devp);
375 }
376
377 int device_find_child_by_of_offset(struct udevice *parent, int of_offset,
378                                    struct udevice **devp)
379 {
380         struct udevice *dev;
381
382         *devp = NULL;
383
384         list_for_each_entry(dev, &parent->child_head, sibling_node) {
385                 if (dev->of_offset == of_offset) {
386                         *devp = dev;
387                         return 0;
388                 }
389         }
390
391         return -ENODEV;
392 }
393
394 int device_get_child_by_of_offset(struct udevice *parent, int seq,
395                                   struct udevice **devp)
396 {
397         struct udevice *dev;
398         int ret;
399
400         *devp = NULL;
401         ret = device_find_child_by_of_offset(parent, seq, &dev);
402         return device_get_device_tail(dev, ret, devp);
403 }
404
405 int device_find_first_child(struct udevice *parent, struct udevice **devp)
406 {
407         if (list_empty(&parent->child_head)) {
408                 *devp = NULL;
409         } else {
410                 *devp = list_first_entry(&parent->child_head, struct udevice,
411                                          sibling_node);
412         }
413
414         return 0;
415 }
416
417 int device_find_next_child(struct udevice **devp)
418 {
419         struct udevice *dev = *devp;
420         struct udevice *parent = dev->parent;
421
422         if (list_is_last(&dev->sibling_node, &parent->child_head)) {
423                 *devp = NULL;
424         } else {
425                 *devp = list_entry(dev->sibling_node.next, struct udevice,
426                                    sibling_node);
427         }
428
429         return 0;
430 }
431
432 struct udevice *dev_get_parent(struct udevice *child)
433 {
434         return child->parent;
435 }
436
437 ulong dev_get_of_data(struct udevice *dev)
438 {
439         return dev->of_id->data;
440 }