1// SPDX-License-Identifier: GPL-2.0
2
3#include <kunit/resource.h>
4
5#include <linux/device.h>
6#include <linux/platform_device.h>
7
8#define DEVICE_NAME "test"
9
10struct test_priv {
11	bool probe_done;
12	bool release_done;
13	wait_queue_head_t probe_wq;
14	wait_queue_head_t release_wq;
15	struct device *dev;
16};
17
18static int platform_device_devm_init(struct kunit *test)
19{
20	struct test_priv *priv;
21
22	priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
23	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
24	init_waitqueue_head(&priv->probe_wq);
25	init_waitqueue_head(&priv->release_wq);
26
27	test->priv = priv;
28
29	return 0;
30}
31
32static void devm_device_action(void *ptr)
33{
34	struct test_priv *priv = ptr;
35
36	priv->release_done = true;
37	wake_up_interruptible(&priv->release_wq);
38}
39
40static void devm_put_device_action(void *ptr)
41{
42	struct test_priv *priv = ptr;
43
44	put_device(priv->dev);
45	priv->release_done = true;
46	wake_up_interruptible(&priv->release_wq);
47}
48
49#define RELEASE_TIMEOUT_MS	100
50
51/*
52 * Tests that a platform bus, non-probed device will run its
53 * device-managed actions when unregistered.
54 */
55static void platform_device_devm_register_unregister_test(struct kunit *test)
56{
57	struct platform_device *pdev;
58	struct test_priv *priv = test->priv;
59	int ret;
60
61	pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
62	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
63
64	ret = platform_device_add(pdev);
65	KUNIT_ASSERT_EQ(test, ret, 0);
66
67	priv->dev = &pdev->dev;
68
69	ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
70	KUNIT_ASSERT_EQ(test, ret, 0);
71
72	platform_device_unregister(pdev);
73
74	ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
75					       msecs_to_jiffies(RELEASE_TIMEOUT_MS));
76	KUNIT_EXPECT_GT(test, ret, 0);
77}
78
79/*
80 * Tests that a platform bus, non-probed device will run its
81 * device-managed actions when unregistered, even if someone still holds
82 * a reference to it.
83 */
84static void platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
85{
86	struct platform_device *pdev;
87	struct test_priv *priv = test->priv;
88	int ret;
89
90	pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
91	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
92
93	ret = platform_device_add(pdev);
94	KUNIT_ASSERT_EQ(test, ret, 0);
95
96	priv->dev = &pdev->dev;
97
98	get_device(priv->dev);
99
100	ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
101	KUNIT_ASSERT_EQ(test, ret, 0);
102
103	platform_device_unregister(pdev);
104
105	ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
106					       msecs_to_jiffies(RELEASE_TIMEOUT_MS));
107	KUNIT_EXPECT_GT(test, ret, 0);
108}
109
110static int fake_probe(struct platform_device *pdev)
111{
112	struct test_priv *priv = platform_get_drvdata(pdev);
113
114	priv->probe_done = true;
115	wake_up_interruptible(&priv->probe_wq);
116
117	return 0;
118}
119
120static struct platform_driver fake_driver = {
121	.probe	= fake_probe,
122	.driver = {
123		.name = DEVICE_NAME,
124	},
125};
126
127/*
128 * Tests that a platform bus, probed device will run its device-managed
129 * actions when unregistered.
130 */
131static void probed_platform_device_devm_register_unregister_test(struct kunit *test)
132{
133	struct platform_device *pdev;
134	struct test_priv *priv = test->priv;
135	int ret;
136
137	ret = platform_driver_register(&fake_driver);
138	KUNIT_ASSERT_EQ(test, ret, 0);
139
140	pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
141	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
142
143	priv->dev = &pdev->dev;
144	platform_set_drvdata(pdev, priv);
145
146	ret = platform_device_add(pdev);
147	KUNIT_ASSERT_EQ(test, ret, 0);
148
149	ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done,
150					       msecs_to_jiffies(RELEASE_TIMEOUT_MS));
151	KUNIT_ASSERT_GT(test, ret, 0);
152
153	ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
154	KUNIT_ASSERT_EQ(test, ret, 0);
155
156	platform_device_unregister(pdev);
157
158	ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
159					       msecs_to_jiffies(RELEASE_TIMEOUT_MS));
160	KUNIT_EXPECT_GT(test, ret, 0);
161
162	platform_driver_unregister(&fake_driver);
163}
164
165/*
166 * Tests that a platform bus, probed device will run its device-managed
167 * actions when unregistered, even if someone still holds a reference to
168 * it.
169 */
170static void probed_platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
171{
172	struct platform_device *pdev;
173	struct test_priv *priv = test->priv;
174	int ret;
175
176	ret = platform_driver_register(&fake_driver);
177	KUNIT_ASSERT_EQ(test, ret, 0);
178
179	pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
180	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
181
182	priv->dev = &pdev->dev;
183	platform_set_drvdata(pdev, priv);
184
185	ret = platform_device_add(pdev);
186	KUNIT_ASSERT_EQ(test, ret, 0);
187
188	ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done,
189					       msecs_to_jiffies(RELEASE_TIMEOUT_MS));
190	KUNIT_ASSERT_GT(test, ret, 0);
191
192	get_device(priv->dev);
193
194	ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
195	KUNIT_ASSERT_EQ(test, ret, 0);
196
197	platform_device_unregister(pdev);
198
199	ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
200					       msecs_to_jiffies(RELEASE_TIMEOUT_MS));
201	KUNIT_EXPECT_GT(test, ret, 0);
202
203	platform_driver_unregister(&fake_driver);
204}
205
206static struct kunit_case platform_device_devm_tests[] = {
207	KUNIT_CASE(platform_device_devm_register_unregister_test),
208	KUNIT_CASE(platform_device_devm_register_get_unregister_with_devm_test),
209	KUNIT_CASE(probed_platform_device_devm_register_unregister_test),
210	KUNIT_CASE(probed_platform_device_devm_register_get_unregister_with_devm_test),
211	{}
212};
213
214static struct kunit_suite platform_device_devm_test_suite = {
215	.name = "platform-device-devm",
216	.init = platform_device_devm_init,
217	.test_cases = platform_device_devm_tests,
218};
219
220kunit_test_suite(platform_device_devm_test_suite);
221
222MODULE_DESCRIPTION("Test module for platform devices");
223MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
224MODULE_LICENSE("GPL");
225