]> git.ipfire.org Git - people/ms/u-boot.git/blob - test/dm/bus.c
dm: core: Allow parents to have platform data for their children
[people/ms/u-boot.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);