projects/linux·blob master · refs

337 lines · 10.0 KiB

1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
4 */
5
6#include <linux/array_size.h>
7#include <linux/device.h>
8#include <linux/fwnode.h>
9#include <linux/list.h>
10#include <linux/platform_device.h>
11#include <linux/property.h>
12#include <linux/time.h>
13#include <linux/types.h>
14#include <linux/wait.h>
15
16#include <kunit/fwnode.h>
17#include <kunit/platform_device.h>
18#include <kunit/test.h>
19
20static int swnode_count_suppliers(struct fwnode_handle *fwnode)
21{
22 struct fwnode_link *link;
23 unsigned int count = 0;
24
25 /*
26 * The suppliers and consumers lists should typically only be accessed
27 * with the fwnode_link_lock taken but it's private to the driver core.
28 *
29 * These are tests and at this point nobody should be modifying them so
30 * let's just access the list.
31 */
32 list_for_each_entry(link, &fwnode->suppliers, c_hook)
33 count++;
34
35 return count;
36}
37
38/* True if a supplier link con->sup exists, checked from both list ends. */
39static bool swnode_has_link(struct fwnode_handle *consumer,
40 struct fwnode_handle *supplier)
41{
42 bool from_con = false, from_sup = false;
43 struct fwnode_link *link;
44
45 list_for_each_entry(link, &consumer->suppliers, c_hook) {
46 if (link->supplier == supplier && link->consumer == consumer)
47 from_con = true;
48 }
49
50 list_for_each_entry(link, &supplier->consumers, s_hook) {
51 if (link->supplier == supplier && link->consumer == consumer)
52 from_sup = true;
53 }
54
55 return from_con && from_sup;
56}
57
58/* A single reference creates exactly one supplier link, on both list ends. */
59static void swnode_devlink_test_single_ref(struct kunit *test)
60{
61 static const struct software_node supp_swnode = {
62 .name = "swnode-devlink-test-supplier",
63 };
64
65 struct fwnode_handle *cons_fwnode, *supp_fwnode;
66 int ret;
67
68 const struct property_entry props[] = {
69 PROPERTY_ENTRY_REF("supplier", &supp_swnode),
70 { }
71 };
72
73 supp_fwnode = kunit_software_node_register(test, &supp_swnode);
74 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode);
75
76 cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL);
77 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode);
78
79 ret = fwnode_call_int_op(cons_fwnode, add_links);
80 KUNIT_EXPECT_EQ(test, ret, 0);
81
82 KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 1);
83 KUNIT_EXPECT_TRUE(test, swnode_has_link(cons_fwnode, supp_fwnode));
84}
85
86/* Multiple distinct references create multiple supplier links. */
87static void swnode_devlink_test_multiple_refs(struct kunit *test)
88{
89 static const struct software_node supp1_swnode = {
90 .name = "swnode-devlink-test-supplier-1",
91 };
92 static const struct software_node supp2_swnode = {
93 .name = "swnode-devlink-test-supplier-2",
94 };
95 static const struct software_node *supp_nodes[] = {
96 &supp1_swnode, &supp2_swnode, NULL
97 };
98
99 const struct property_entry props[] = {
100 PROPERTY_ENTRY_REF("foo", &supp1_swnode),
101 PROPERTY_ENTRY_REF("bar", &supp2_swnode),
102 { }
103 };
104
105 struct fwnode_handle *fwnode;
106 int ret;
107
108 ret = kunit_software_node_register_node_group(test, supp_nodes);
109 KUNIT_ASSERT_EQ(test, ret, 0);
110
111 fwnode = kunit_fwnode_create_software_node(test, props, NULL);
112 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
113
114 ret = fwnode_call_int_op(fwnode, add_links);
115 KUNIT_EXPECT_EQ(test, ret, 0);
116
117 KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2);
118 KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode)));
119 KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode)));
120}
121
122/* A reference to an unregistered node creates no link (graceful skip). */
123static void swnode_devlink_test_unregistered_ref(struct kunit *test)
124{
125 static const struct software_node supp_swnode = {
126 .name = "swnode-devlink-test-supplier",
127 };
128
129 const struct property_entry props[] = {
130 PROPERTY_ENTRY_REF("supplier", &supp_swnode),
131 { }
132 };
133
134 struct fwnode_handle *fwnode;
135 int ret;
136
137 fwnode = kunit_fwnode_create_software_node(test, props, NULL);
138 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
139
140 ret = fwnode_call_int_op(fwnode, add_links);
141 KUNIT_EXPECT_EQ(test, ret, 0);
142 KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 0);
143}
144
145/* Graph "remote-endpoint" references are excluded. */
146static void swnode_devlink_test_remote_endpoint_excluded(struct kunit *test)
147{
148 static const struct software_node ep_swnode = {
149 .name = "swnode-devlink-test-end-point"
150 };
151
152 const struct property_entry props[] = {
153 PROPERTY_ENTRY_REF("remote-endpoint", &ep_swnode),
154 { }
155 };
156
157 struct fwnode_handle *cons_fwnode, *supp_fwnode;
158 int ret;
159
160 supp_fwnode = kunit_software_node_register(test, &ep_swnode);
161 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode);
162
163 cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL);
164 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode);
165
166 ret = fwnode_call_int_op(cons_fwnode, add_links);
167 KUNIT_EXPECT_EQ(test, ret, 0);
168 KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 0);
169}
170
171/* A reference array creates one link per registered element. */
172static void swnode_devlink_test_ref_array(struct kunit *test)
173{
174 static const struct software_node supp1_swnode = {
175 .name = "swnode-devlink-test-supplier-1",
176 };
177 static const struct software_node supp2_swnode = {
178 .name = "swnode-devlink-test-supplier-2",
179 };
180 static const struct software_node *supp_nodes[] = {
181 &supp1_swnode, &supp2_swnode, NULL
182 };
183 static const struct software_node_ref_args refs[] = {
184 SOFTWARE_NODE_REFERENCE(&supp1_swnode),
185 SOFTWARE_NODE_REFERENCE(&supp2_swnode, 4, 2),
186 };
187
188 const struct property_entry props[] = {
189 PROPERTY_ENTRY_REF_ARRAY("suppliers", refs),
190 { }
191 };
192
193 struct fwnode_handle *fwnode;
194 int ret;
195
196 ret = kunit_software_node_register_node_group(test, supp_nodes);
197 KUNIT_ASSERT_EQ(test, ret, 0);
198
199 fwnode = kunit_fwnode_create_software_node(test, props, NULL);
200 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
201
202 ret = fwnode_call_int_op(fwnode, add_links);
203 KUNIT_EXPECT_EQ(test, ret, 0);
204
205 KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2);
206 KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode)));
207 KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode)));
208}
209
210/*
211 * End-to-end test: fw_devlink must defer a consumer's probe until its
212 * supplier has probed.
213 *
214 * The reference created by software_node_add_links() is only useful if the
215 * driver core promotes it to a real device_link and uses it to order probing.
216 * This test drives actual probing through the platform bus and asserts the
217 * supplier binds before the consumer.
218 */
219
220#define SWNODE_DEVLINK_TEST_SUPPLIER "swnode-link-supplier"
221#define SWNODE_DEVLINK_TEST_CONSUMER "swnode-link-consumer"
222#define SWNODE_DEVLINK_TEST_TIMEOUT_MS (2 * MSEC_PER_SEC)
223
224struct swnode_test_probe_order {
225 /* Names in the order their drivers' .probe ran. */
226 const char *probed[2];
227 unsigned int count;
228 wait_queue_head_t wq;
229};
230
231static int swnode_test_record_probe(struct platform_device *pdev)
232{
233 struct swnode_test_probe_order *order = platform_get_drvdata(pdev);
234
235 if (order && order->count < ARRAY_SIZE(order->probed)) {
236 order->probed[order->count++] = dev_name(&pdev->dev);
237 wake_up_interruptible(&order->wq);
238 }
239
240 return 0;
241}
242
243static struct platform_driver swnode_test_supplier_driver = {
244 .probe = swnode_test_record_probe,
245 .driver = {
246 .name = SWNODE_DEVLINK_TEST_SUPPLIER,
247 },
248};
249
250static struct platform_driver swnode_test_consumer_driver = {
251 .probe = swnode_test_record_probe,
252 .driver = {
253 .name = SWNODE_DEVLINK_TEST_CONSUMER,
254 },
255};
256
257static void swnode_devlink_test_probe_order(struct kunit *test)
258{
259 static const struct software_node supplier_swnode = {
260 .name = "swnode-devlink-test-supplier",
261 };
262
263 const struct property_entry consumer_props[] = {
264 PROPERTY_ENTRY_REF("supplier-ref", &supplier_swnode),
265 { }
266 };
267
268 struct platform_device *supplier, *consumer;
269 struct swnode_test_probe_order *order;
270 struct fwnode_handle *fwnode;
271 int ret;
272
273 order = kunit_kzalloc(test, sizeof(*order), GFP_KERNEL);
274 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, order);
275 init_waitqueue_head(&order->wq);
276
277 fwnode = kunit_software_node_register(test, &supplier_swnode);
278 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
279
280 ret = kunit_platform_driver_register(test, &swnode_test_supplier_driver);
281 KUNIT_ASSERT_EQ(test, ret, 0);
282 ret = kunit_platform_driver_register(test, &swnode_test_consumer_driver);
283 KUNIT_ASSERT_EQ(test, ret, 0);
284
285 supplier = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_SUPPLIER,
286 PLATFORM_DEVID_NONE);
287 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supplier);
288 consumer = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_CONSUMER,
289 PLATFORM_DEVID_NONE);
290 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, consumer);
291
292 platform_set_drvdata(supplier, order);
293 platform_set_drvdata(consumer, order);
294
295 ret = kunit_device_add_software_node(test, &supplier->dev, &supplier_swnode);
296 KUNIT_ASSERT_EQ(test, ret, 0);
297 ret = device_create_managed_software_node(&consumer->dev,
298 consumer_props, NULL);
299 KUNIT_ASSERT_EQ(test, ret, 0);
300
301 ret = kunit_platform_device_add(test, consumer);
302 KUNIT_ASSERT_EQ(test, ret, 0);
303 ret = kunit_platform_device_add(test, supplier);
304 KUNIT_ASSERT_EQ(test, ret, 0);
305
306 ret = wait_event_interruptible_timeout(order->wq,
307 order->count == 2,
308 msecs_to_jiffies(SWNODE_DEVLINK_TEST_TIMEOUT_MS));
309 KUNIT_ASSERT_GT(test, ret, 0);
310
311 KUNIT_EXPECT_STREQ(test, order->probed[0], SWNODE_DEVLINK_TEST_SUPPLIER);
312 KUNIT_EXPECT_STREQ(test, order->probed[1], SWNODE_DEVLINK_TEST_CONSUMER);
313
314 /* Tear down the consumer (and its device link) before the supplier. */
315 kunit_platform_device_unregister(test, consumer);
316}
317
318static struct kunit_case swnode_test_cases[] = {
319 KUNIT_CASE(swnode_devlink_test_single_ref),
320 KUNIT_CASE(swnode_devlink_test_multiple_refs),
321 KUNIT_CASE(swnode_devlink_test_unregistered_ref),
322 KUNIT_CASE(swnode_devlink_test_remote_endpoint_excluded),
323 KUNIT_CASE(swnode_devlink_test_ref_array),
324 KUNIT_CASE(swnode_devlink_test_probe_order),
325 { }
326};
327
328static struct kunit_suite swnode_test_suite = {
329 .name = "software-node-links",
330 .test_cases = swnode_test_cases,
331};
332
333kunit_test_suite(swnode_test_suite);
334
335MODULE_DESCRIPTION("Test module for software node fw_devlink support");
336MODULE_AUTHOR("Bartosz Golaszewski <[email protected]>");
337MODULE_LICENSE("GPL");