1// SPDX-License-Identifier: GPL-2.0+
2
3#include <clk.h>
4#include <dm.h>
5#include <dt-structs.h>
6#include <irq.h>
7#include <dm/test.h>
8#include <test/test.h>
9#include <test/ut.h>
10#include <asm-generic/gpio.h>
11#include <asm/global_data.h>
12
13/* Test that we can find a device using of-platdata */
14static int dm_test_of_plat_base(struct unit_test_state *uts)
15{
16	struct udevice *dev;
17
18	ut_assertok(uclass_first_device_err(UCLASS_SERIAL, &dev));
19	ut_asserteq_str("sandbox_serial", dev->name);
20
21	return 0;
22}
23DM_TEST(dm_test_of_plat_base, UT_TESTF_SCAN_PDATA);
24
25/* Test that we can read properties from a device */
26static int dm_test_of_plat_props(struct unit_test_state *uts)
27{
28	struct dtd_sandbox_spl_test *plat;
29	struct udevice *dev;
30	int i;
31
32	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_spl_test",
33					      &dev));
34
35	plat = dev_get_plat(dev);
36	ut_assert(plat->boolval);
37	ut_asserteq(1, plat->intval);
38	ut_asserteq(3, ARRAY_SIZE(plat->intarray));
39	ut_asserteq(2, plat->intarray[0]);
40	ut_asserteq(3, plat->intarray[1]);
41	ut_asserteq(4, plat->intarray[2]);
42	ut_asserteq(5, plat->byteval);
43	ut_asserteq(1, ARRAY_SIZE(plat->maybe_empty_int));
44	ut_asserteq(0, plat->maybe_empty_int[0]);
45	ut_asserteq(3, ARRAY_SIZE(plat->bytearray));
46	ut_asserteq(6, plat->bytearray[0]);
47	ut_asserteq(0, plat->bytearray[1]);
48	ut_asserteq(0, plat->bytearray[2]);
49	ut_asserteq(9, ARRAY_SIZE(plat->longbytearray));
50	for (i = 0; i < ARRAY_SIZE(plat->longbytearray); i++)
51		ut_asserteq(9 + i, plat->longbytearray[i]);
52	ut_asserteq_str("message", plat->stringval);
53	ut_asserteq(3, ARRAY_SIZE(plat->stringarray));
54	ut_asserteq_str("multi-word", plat->stringarray[0]);
55	ut_asserteq_str("message", plat->stringarray[1]);
56	ut_asserteq_str("", plat->stringarray[2]);
57
58	ut_assertok(uclass_next_device_err(&dev));
59	plat = dev_get_plat(dev);
60	ut_assert(!plat->boolval);
61	ut_asserteq(3, plat->intval);
62	ut_asserteq(5, plat->intarray[0]);
63	ut_asserteq(0, plat->intarray[1]);
64	ut_asserteq(0, plat->intarray[2]);
65	ut_asserteq(8, plat->byteval);
66	ut_asserteq(3, ARRAY_SIZE(plat->bytearray));
67	ut_asserteq(1, plat->bytearray[0]);
68	ut_asserteq(0x23, plat->bytearray[1]);
69	ut_asserteq(0x34, plat->bytearray[2]);
70	for (i = 0; i < ARRAY_SIZE(plat->longbytearray); i++)
71		ut_asserteq(i < 4 ? 9 + i : 0, plat->longbytearray[i]);
72	ut_asserteq_str("message2", plat->stringval);
73	ut_asserteq_str("another", plat->stringarray[0]);
74	ut_asserteq_str("multi-word", plat->stringarray[1]);
75	ut_asserteq_str("message", plat->stringarray[2]);
76
77	ut_assertok(uclass_next_device_err(&dev));
78	plat = dev_get_plat(dev);
79	ut_assert(!plat->boolval);
80	ut_asserteq_str("one", plat->stringarray[0]);
81	ut_asserteq_str("", plat->stringarray[1]);
82	ut_asserteq_str("", plat->stringarray[2]);
83	ut_asserteq(1, plat->maybe_empty_int[0]);
84
85	ut_assertok(uclass_next_device_err(&dev));
86	plat = dev_get_plat(dev);
87	ut_assert(!plat->boolval);
88	ut_asserteq_str("spl", plat->stringarray[0]);
89
90	ut_asserteq(-ENODEV, uclass_next_device_err(&dev));
91
92	return 0;
93}
94DM_TEST(dm_test_of_plat_props, UT_TESTF_SCAN_PDATA);
95
96/*
97 * find_driver_info - recursively find the driver_info for a device
98 *
99 * This sets found[idx] to true when it finds the driver_info record for a
100 * device, where idx is the index in the driver_info linker list.
101 *
102 * @uts: Test state
103 * @parent: Parent to search
104 * @found: bool array to update
105 * Return: 0 if OK, non-zero on error
106 */
107static int find_driver_info(struct unit_test_state *uts, struct udevice *parent,
108			    bool found[])
109{
110	struct udevice *dev;
111
112	/* If not the root device, find the entry that caused it to be bound */
113	if (parent->parent) {
114		const int n_ents =
115			ll_entry_count(struct driver_info, driver_info);
116		int idx = -1;
117		int i;
118
119		for (i = 0; i < n_ents; i++) {
120			const struct driver_rt *drt = gd_dm_driver_rt() + i;
121
122			if (drt->dev == parent) {
123				idx = i;
124				found[idx] = true;
125				break;
126			}
127		}
128
129		ut_assert(idx != -1);
130	}
131
132	device_foreach_child(dev, parent) {
133		int ret;
134
135		ret = find_driver_info(uts, dev, found);
136		if (ret < 0)
137			return ret;
138	}
139
140	return 0;
141}
142
143/* Check that every device is recorded in its driver_info struct */
144static int dm_test_of_plat_dev(struct unit_test_state *uts)
145{
146	const int n_ents = ll_entry_count(struct driver_info, driver_info);
147	bool found[n_ents];
148	uint i;
149
150	/* Skip this test if there is no platform data */
151	if (!CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT))
152		return -EAGAIN;
153
154	/* Record the indexes that are found */
155	memset(found, '\0', sizeof(found));
156	ut_assertok(find_driver_info(uts, gd->dm_root, found));
157
158	/* Make sure that the driver entries without devices have no ->dev */
159	for (i = 0; i < n_ents; i++) {
160		const struct driver_rt *drt = gd_dm_driver_rt() + i;
161		struct udevice *dev;
162
163		if (found[i]) {
164			/* Make sure we can find it */
165			ut_assertnonnull(drt->dev);
166			ut_assertok(device_get_by_ofplat_idx(i, &dev));
167			ut_asserteq_ptr(dev, drt->dev);
168		} else {
169			ut_assertnull(drt->dev);
170			ut_asserteq(-ENOENT, device_get_by_ofplat_idx(i, &dev));
171		}
172	}
173
174	return 0;
175}
176DM_TEST(dm_test_of_plat_dev, UT_TESTF_SCAN_PDATA);
177
178/* Test handling of phandles that point to other devices */
179static int dm_test_of_plat_phandle(struct unit_test_state *uts)
180{
181	struct dtd_sandbox_clk_test *plat;
182	struct udevice *dev, *clk;
183
184	ut_assertok(uclass_first_device_err(UCLASS_MISC, &dev));
185	ut_asserteq_str("sandbox_clk_test", dev->name);
186	plat = dev_get_plat(dev);
187
188	ut_assertok(device_get_by_ofplat_idx(plat->clocks[0].idx, &clk));
189	ut_asserteq_str("sandbox_fixed_clock", clk->name);
190
191	ut_assertok(device_get_by_ofplat_idx(plat->clocks[1].idx, &clk));
192	ut_asserteq_str("sandbox_clk", clk->name);
193	ut_asserteq(1, plat->clocks[1].arg[0]);
194
195	ut_assertok(device_get_by_ofplat_idx(plat->clocks[2].idx, &clk));
196	ut_asserteq_str("sandbox_clk", clk->name);
197	ut_asserteq(0, plat->clocks[2].arg[0]);
198
199	ut_assertok(device_get_by_ofplat_idx(plat->clocks[3].idx, &clk));
200	ut_asserteq_str("sandbox_clk", clk->name);
201	ut_asserteq(3, plat->clocks[3].arg[0]);
202
203	ut_assertok(device_get_by_ofplat_idx(plat->clocks[4].idx, &clk));
204	ut_asserteq_str("sandbox_clk", clk->name);
205	ut_asserteq(2, plat->clocks[4].arg[0]);
206
207	return 0;
208}
209DM_TEST(dm_test_of_plat_phandle, UT_TESTF_SCAN_PDATA);
210
211#if CONFIG_IS_ENABLED(OF_PLATDATA_PARENT)
212/* Test that device parents are correctly set up */
213static int dm_test_of_plat_parent(struct unit_test_state *uts)
214{
215	struct udevice *rtc, *i2c;
216
217	ut_assertok(uclass_first_device_err(UCLASS_RTC, &rtc));
218	ut_assertok(uclass_first_device_err(UCLASS_I2C, &i2c));
219	ut_asserteq_ptr(i2c, dev_get_parent(rtc));
220
221	return 0;
222}
223DM_TEST(dm_test_of_plat_parent, UT_TESTF_SCAN_PDATA);
224#endif
225
226/* Test clocks with of-platdata */
227static int dm_test_of_plat_clk(struct unit_test_state *uts)
228{
229	struct dtd_sandbox_clk_test *plat;
230	struct udevice *dev;
231	struct clk clk;
232
233	ut_assertok(uclass_first_device_err(UCLASS_MISC, &dev));
234	ut_asserteq_str("sandbox_clk_test", dev->name);
235	plat = dev_get_plat(dev);
236
237	ut_assertok(clk_get_by_phandle(dev, &plat->clocks[0], &clk));
238	ut_asserteq_str("sandbox_fixed_clock", clk.dev->name);
239
240	return 0;
241}
242DM_TEST(dm_test_of_plat_clk, UT_TESTF_SCAN_PDATA);
243
244/* Test irqs with of-platdata */
245static int dm_test_of_plat_irq(struct unit_test_state *uts)
246{
247	struct dtd_sandbox_irq_test *plat;
248	struct udevice *dev;
249	struct irq irq;
250
251	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_irq_test",
252					      &dev));
253	plat = dev_get_plat(dev);
254
255	ut_assertok(irq_get_by_phandle(dev, &plat->interrupts_extended[0],
256				       &irq));
257	ut_asserteq_str("sandbox_irq", irq.dev->name);
258
259	return 0;
260}
261DM_TEST(dm_test_of_plat_irq, UT_TESTF_SCAN_PDATA);
262
263/* Test GPIOs with of-platdata */
264static int dm_test_of_plat_gpio(struct unit_test_state *uts)
265{
266	struct dtd_sandbox_gpio_test *plat;
267	struct udevice *dev;
268	struct gpio_desc desc;
269
270	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_gpio_test",
271					      &dev));
272	plat = dev_get_plat(dev);
273
274	ut_assertok(gpio_request_by_phandle(dev, &plat->test_gpios[0], &desc,
275					    GPIOD_IS_OUT));
276	ut_asserteq_str("sandbox_gpio", desc.dev->name);
277
278	return 0;
279}
280DM_TEST(dm_test_of_plat_gpio, UT_TESTF_SCAN_PDATA);
281