]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - test/dm/bus.c
dm: core: Allow parents to have platform data for their children
[karo-tx-uboot.git] / test / dm / bus.c
1 /*
2  * Copyright (c) 2014 Google, Inc
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6
7 #include <common.h>
8 #include <dm.h>
9 #include <dm/device-internal.h>
10 #include <dm/root.h>
11 #include <dm/test.h>
12 #include <dm/uclass-internal.h>
13 #include <dm/ut.h>
14 #include <dm/util.h>
15
16 DECLARE_GLOBAL_DATA_PTR;
17
18 struct dm_test_parent_platdata {
19         int count;
20 };
21
22 enum {
23         FLAG_CHILD_PROBED       = 10,
24         FLAG_CHILD_REMOVED      = -7,
25 };
26
27 static struct dm_test_state *test_state;
28
29 static int testbus_drv_probe(struct udevice *dev)
30 {
31         return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
32 }
33
34 static int testbus_child_pre_probe(struct udevice *dev)
35 {
36         struct dm_test_parent_data *parent_data = dev_get_parentdata(dev);
37
38         parent_data->flag += FLAG_CHILD_PROBED;
39
40         return 0;
41 }
42
43 static int testbus_child_post_remove(struct udevice *dev)
44 {
45         struct dm_test_parent_data *parent_data = dev_get_parentdata(dev);
46         struct dm_test_state *dms = test_state;
47
48         parent_data->flag += FLAG_CHILD_REMOVED;
49         if (dms)
50                 dms->removed = dev;
51
52         return 0;
53 }
54
55 static const struct udevice_id testbus_ids[] = {
56         {
57                 .compatible = "denx,u-boot-test-bus",
58                 .data = DM_TEST_TYPE_FIRST },
59         { }
60 };
61
62 U_BOOT_DRIVER(testbus_drv) = {
63         .name   = "testbus_drv",
64         .of_match       = testbus_ids,
65         .id     = UCLASS_TEST_BUS,
66         .probe  = testbus_drv_probe,
67         .priv_auto_alloc_size = sizeof(struct dm_test_priv),
68         .platdata_auto_alloc_size = sizeof(struct dm_test_pdata),
69         .per_child_auto_alloc_size = sizeof(struct dm_test_parent_data),
70         .per_child_platdata_auto_alloc_size =
71                         sizeof(struct dm_test_parent_platdata),
72         .child_pre_probe = testbus_child_pre_probe,
73         .child_post_remove = testbus_child_post_remove,
74 };
75
76 UCLASS_DRIVER(testbus) = {
77         .name           = "testbus",
78         .id             = UCLASS_TEST_BUS,
79 };
80
81 /* Test that we can probe for children */
82 static int dm_test_bus_children(struct dm_test_state *dms)
83 {
84         int num_devices = 4;
85         struct udevice *bus;
86         struct uclass *uc;
87
88         ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
89         ut_asserteq(num_devices, list_count_items(&uc->dev_head));
90
91         /* Probe the bus, which should yield 3 more devices */
92         ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
93         num_devices += 3;
94
95         ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
96         ut_asserteq(num_devices, list_count_items(&uc->dev_head));
97
98         ut_assert(!dm_check_devices(dms, num_devices));
99
100         return 0;
101 }
102 DM_TEST(dm_test_bus_children, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
103
104 /* Test our functions for accessing children */
105 static int dm_test_bus_children_funcs(struct dm_test_state *dms)
106 {
107         const void *blob = gd->fdt_blob;
108         struct udevice *bus, *dev;
109         int node;
110
111         ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
112
113         /* device_get_child() */
114         ut_assertok(device_get_child(bus, 0, &dev));
115         ut_asserteq(-ENODEV, device_get_child(bus, 4, &dev));
116         ut_assertok(device_get_child_by_seq(bus, 5, &dev));
117         ut_assert(dev->flags & DM_FLAG_ACTIVATED);
118         ut_asserteq_str("c-test@5", dev->name);
119
120         /* Device with sequence number 0 should be accessible */
121         ut_asserteq(-ENODEV, device_find_child_by_seq(bus, -1, true, &dev));
122         ut_assertok(device_find_child_by_seq(bus, 0, true, &dev));
123         ut_assert(!(dev->flags & DM_FLAG_ACTIVATED));
124         ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 0, false, &dev));
125         ut_assertok(device_get_child_by_seq(bus, 0, &dev));
126         ut_assert(dev->flags & DM_FLAG_ACTIVATED);
127
128         /* There is no device with sequence number 2 */
129         ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 2, false, &dev));
130         ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 2, true, &dev));
131         ut_asserteq(-ENODEV, device_get_child_by_seq(bus, 2, &dev));
132
133         /* Looking for something that is not a child */
134         node = fdt_path_offset(blob, "/junk");
135         ut_asserteq(-ENODEV, device_find_child_by_of_offset(bus, node, &dev));
136         node = fdt_path_offset(blob, "/d-test");
137         ut_asserteq(-ENODEV, device_find_child_by_of_offset(bus, node, &dev));
138
139         /* Find a valid child */
140         node = fdt_path_offset(blob, "/some-bus/c-test@1");
141         ut_assertok(device_find_child_by_of_offset(bus, node, &dev));
142         ut_assert(!(dev->flags & DM_FLAG_ACTIVATED));
143         ut_assertok(device_get_child_by_of_offset(bus, node, &dev));
144         ut_assert(dev->flags & DM_FLAG_ACTIVATED);
145
146         return 0;
147 }
148 DM_TEST(dm_test_bus_children_funcs, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
149
150 /* Test that we can iterate through children */
151 static int dm_test_bus_children_iterators(struct dm_test_state *dms)
152 {
153         struct udevice *bus, *dev, *child;
154
155         /* Walk through the children one by one */
156         ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
157         ut_assertok(device_find_first_child(bus, &dev));
158         ut_asserteq_str("c-test@5", dev->name);
159         ut_assertok(device_find_next_child(&dev));
160         ut_asserteq_str("c-test@0", dev->name);
161         ut_assertok(device_find_next_child(&dev));
162         ut_asserteq_str("c-test@1", dev->name);
163         ut_assertok(device_find_next_child(&dev));
164         ut_asserteq_ptr(dev, NULL);
165
166         /* Move to the next child without using device_find_first_child() */
167         ut_assertok(device_find_child_by_seq(bus, 5, true, &dev));
168         ut_asserteq_str("c-test@5", dev->name);
169         ut_assertok(device_find_next_child(&dev));
170         ut_asserteq_str("c-test@0", dev->name);
171
172         /* Try a device with no children */
173         ut_assertok(device_find_first_child(dev, &child));
174         ut_asserteq_ptr(child, NULL);
175
176         return 0;
177 }
178 DM_TEST(dm_test_bus_children_iterators,
179         DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
180
181 /* Test that the bus can store data about each child */
182 static int dm_test_bus_parent_data(struct dm_test_state *dms)
183 {
184         struct dm_test_parent_data *parent_data;
185         struct udevice *bus, *dev;
186         struct uclass *uc;
187         int value;
188
189         ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
190
191         /* Check that parent data is allocated */
192         ut_assertok(device_find_child_by_seq(bus, 0, true, &dev));
193         ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
194         ut_assertok(device_get_child_by_seq(bus, 0, &dev));
195         parent_data = dev_get_parentdata(dev);
196         ut_assert(NULL != parent_data);
197
198         /* Check that it starts at 0 and goes away when device is removed */
199         parent_data->sum += 5;
200         ut_asserteq(5, parent_data->sum);
201         device_remove(dev);
202         ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
203
204         /* Check that we can do this twice */
205         ut_assertok(device_get_child_by_seq(bus, 0, &dev));
206         parent_data = dev_get_parentdata(dev);
207         ut_assert(NULL != parent_data);
208         parent_data->sum += 5;
209         ut_asserteq(5, parent_data->sum);
210
211         /* Add parent data to all children */
212         ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
213         value = 5;
214         uclass_foreach_dev(dev, uc) {
215                 /* Ignore these if they are not on this bus */
216                 if (dev->parent != bus) {
217                         ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
218                         continue;
219                 }
220                 ut_assertok(device_probe(dev));
221                 parent_data = dev_get_parentdata(dev);
222
223                 parent_data->sum = value;
224                 value += 5;
225         }
226
227         /* Check it is still there */
228         value = 5;
229         uclass_foreach_dev(dev, uc) {
230                 /* Ignore these if they are not on this bus */
231                 if (dev->parent != bus)
232                         continue;
233                 parent_data = dev_get_parentdata(dev);
234
235                 ut_asserteq(value, parent_data->sum);
236                 value += 5;
237         }
238
239         return 0;
240 }
241
242 DM_TEST(dm_test_bus_parent_data, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
243
244 /* Test that the bus ops are called when a child is probed/removed */
245 static int dm_test_bus_parent_ops(struct dm_test_state *dms)
246 {
247         struct dm_test_parent_data *parent_data;
248         struct udevice *bus, *dev;
249         struct uclass *uc;
250
251         test_state = dms;
252         ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
253         ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
254
255         uclass_foreach_dev(dev, uc) {
256                 /* Ignore these if they are not on this bus */
257                 if (dev->parent != bus)
258                         continue;
259                 ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
260
261                 ut_assertok(device_probe(dev));
262                 parent_data = dev_get_parentdata(dev);
263                 ut_asserteq(FLAG_CHILD_PROBED, parent_data->flag);
264         }
265
266         uclass_foreach_dev(dev, uc) {
267                 /* Ignore these if they are not on this bus */
268                 if (dev->parent != bus)
269                         continue;
270                 parent_data = dev_get_parentdata(dev);
271                 ut_asserteq(FLAG_CHILD_PROBED, parent_data->flag);
272                 ut_assertok(device_remove(dev));
273                 ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
274                 ut_asserteq_ptr(dms->removed, dev);
275         }
276         test_state = NULL;
277
278         return 0;
279 }
280 DM_TEST(dm_test_bus_parent_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
281
282 /* Test that the bus can store platform data about each child */
283 static int dm_test_bus_parent_platdata(struct dm_test_state *dms)
284 {
285         struct dm_test_parent_platdata *plat;
286         struct udevice *bus, *dev;
287         int child_count;
288
289         /* Check that the bus has no children */
290         ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus));
291         device_find_first_child(bus, &dev);
292         ut_asserteq_ptr(NULL, dev);
293
294         ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
295
296         for (device_find_first_child(bus, &dev), child_count = 0;
297              dev;
298              device_find_next_child(&dev)) {
299                 /* Check that platform data is allocated */
300                 plat = dev_get_parent_platdata(dev);
301                 ut_assert(plat != NULL);
302
303                 /*
304                  * Check that it is not affected by the device being
305                  * probed/removed
306                  */
307                 plat->count++;
308                 ut_asserteq(1, plat->count);
309                 device_probe(dev);
310                 device_remove(dev);
311
312                 ut_asserteq_ptr(plat, dev_get_parent_platdata(dev));
313                 ut_asserteq(1, plat->count);
314                 ut_assertok(device_probe(dev));
315                 child_count++;
316         }
317         ut_asserteq(3, child_count);
318
319         /* Removing the bus should also have no effect (it is still bound) */
320         device_remove(bus);
321         for (device_find_first_child(bus, &dev), child_count = 0;
322              dev;
323              device_find_next_child(&dev)) {
324                 /* Check that platform data is allocated */
325                 plat = dev_get_parent_platdata(dev);
326                 ut_assert(plat != NULL);
327                 ut_asserteq(1, plat->count);
328                 child_count++;
329         }
330         ut_asserteq(3, child_count);
331
332         /* Unbind all the children */
333         do {
334                 device_find_first_child(bus, &dev);
335                 if (dev)
336                         device_unbind(dev);
337         } while (dev);
338
339         /* Now the child platdata should be removed and re-added */
340         device_probe(bus);
341         for (device_find_first_child(bus, &dev), child_count = 0;
342              dev;
343              device_find_next_child(&dev)) {
344                 /* Check that platform data is allocated */
345                 plat = dev_get_parent_platdata(dev);
346                 ut_assert(plat != NULL);
347                 ut_asserteq(0, plat->count);
348                 child_count++;
349         }
350         ut_asserteq(3, child_count);
351
352         return 0;
353 }
354 DM_TEST(dm_test_bus_parent_platdata, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);