17f7acd19SBrian Norris // SPDX-License-Identifier: GPL-2.0
27f7acd19SBrian Norris /*
37f7acd19SBrian Norris * Copyright 2025 Google, Inc.
47f7acd19SBrian Norris */
57f7acd19SBrian Norris
67f7acd19SBrian Norris #include <linux/cleanup.h>
77f7acd19SBrian Norris #include <linux/pm_runtime.h>
87f7acd19SBrian Norris #include <kunit/device.h>
97f7acd19SBrian Norris #include <kunit/test.h>
107f7acd19SBrian Norris
117f7acd19SBrian Norris #define DEVICE_NAME "pm_runtime_test_device"
127f7acd19SBrian Norris
pm_runtime_depth_test(struct kunit * test)137f7acd19SBrian Norris static void pm_runtime_depth_test(struct kunit *test)
147f7acd19SBrian Norris {
157f7acd19SBrian Norris struct device *dev = kunit_device_register(test, DEVICE_NAME);
167f7acd19SBrian Norris
17*92158faeSDan Carpenter KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
187f7acd19SBrian Norris
197f7acd19SBrian Norris pm_runtime_enable(dev);
207f7acd19SBrian Norris
217f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
227f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
237f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
247f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_runtime_get_sync(dev)); /* "already active" */
257f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
267f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
277f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
287f7acd19SBrian Norris }
297f7acd19SBrian Norris
307f7acd19SBrian Norris /* Test pm_runtime_put() and friends when already suspended. */
pm_runtime_already_suspended_test(struct kunit * test)317f7acd19SBrian Norris static void pm_runtime_already_suspended_test(struct kunit *test)
327f7acd19SBrian Norris {
337f7acd19SBrian Norris struct device *dev = kunit_device_register(test, DEVICE_NAME);
347f7acd19SBrian Norris
35*92158faeSDan Carpenter KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
367f7acd19SBrian Norris
377f7acd19SBrian Norris pm_runtime_enable(dev);
387f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
397f7acd19SBrian Norris
407f7acd19SBrian Norris pm_runtime_get_noresume(dev);
417f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_barrier(dev)); /* no wakeup needed */
427f7acd19SBrian Norris pm_runtime_put(dev);
437f7acd19SBrian Norris
447f7acd19SBrian Norris pm_runtime_get_noresume(dev);
45d0b8651aSBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_runtime_put_sync(dev));
467f7acd19SBrian Norris
477f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_runtime_suspend(dev));
487f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_runtime_autosuspend(dev));
497f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_request_autosuspend(dev));
507f7acd19SBrian Norris
517f7acd19SBrian Norris pm_runtime_get_noresume(dev);
527f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_runtime_put_sync_autosuspend(dev));
537f7acd19SBrian Norris
547f7acd19SBrian Norris pm_runtime_get_noresume(dev);
557f7acd19SBrian Norris pm_runtime_put_autosuspend(dev);
567f7acd19SBrian Norris
577f7acd19SBrian Norris /* Grab 2 refcounts */
587f7acd19SBrian Norris pm_runtime_get_noresume(dev);
597f7acd19SBrian Norris pm_runtime_get_noresume(dev);
607f7acd19SBrian Norris /* The first put() sees usage_count 1 */
617f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync_autosuspend(dev));
627f7acd19SBrian Norris /* The second put() sees usage_count 0 but tells us "already suspended". */
637f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_runtime_put_sync_autosuspend(dev));
647f7acd19SBrian Norris
657f7acd19SBrian Norris /* Should have remained suspended the whole time. */
667f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
677f7acd19SBrian Norris }
687f7acd19SBrian Norris
pm_runtime_idle_test(struct kunit * test)697f7acd19SBrian Norris static void pm_runtime_idle_test(struct kunit *test)
707f7acd19SBrian Norris {
717f7acd19SBrian Norris struct device *dev = kunit_device_register(test, DEVICE_NAME);
727f7acd19SBrian Norris
73*92158faeSDan Carpenter KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
747f7acd19SBrian Norris
757f7acd19SBrian Norris pm_runtime_enable(dev);
767f7acd19SBrian Norris
777f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
787f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
797f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
807f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_idle(dev));
817f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
827f7acd19SBrian Norris pm_runtime_put_noidle(dev);
837f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
847f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_idle(dev));
857f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
867f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_idle(dev));
877f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EAGAIN, pm_request_idle(dev));
887f7acd19SBrian Norris }
897f7acd19SBrian Norris
pm_runtime_disabled_test(struct kunit * test)907f7acd19SBrian Norris static void pm_runtime_disabled_test(struct kunit *test)
917f7acd19SBrian Norris {
927f7acd19SBrian Norris struct device *dev = kunit_device_register(test, DEVICE_NAME);
937f7acd19SBrian Norris
94*92158faeSDan Carpenter KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
957f7acd19SBrian Norris
967f7acd19SBrian Norris /* Never called pm_runtime_enable() */
977f7acd19SBrian Norris KUNIT_EXPECT_FALSE(test, pm_runtime_enabled(dev));
987f7acd19SBrian Norris
997f7acd19SBrian Norris /* "disabled" is treated as "active" */
1007f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
1017f7acd19SBrian Norris KUNIT_EXPECT_FALSE(test, pm_runtime_suspended(dev));
1027f7acd19SBrian Norris
1037f7acd19SBrian Norris /*
1047f7acd19SBrian Norris * Note: these "fail", but they still acquire/release refcounts, so
1057f7acd19SBrian Norris * keep them balanced.
1067f7acd19SBrian Norris */
1077f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_get(dev));
1087f7acd19SBrian Norris pm_runtime_put(dev);
1097f7acd19SBrian Norris
1107f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_get_sync(dev));
1117f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_put_sync(dev));
1127f7acd19SBrian Norris
1137f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_get(dev));
1147f7acd19SBrian Norris pm_runtime_put_autosuspend(dev);
1157f7acd19SBrian Norris
1167f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_resume_and_get(dev));
1177f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_idle(dev));
1187f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_request_idle(dev));
1197f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_request_resume(dev));
1207f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_request_autosuspend(dev));
1217f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_suspend(dev));
1227f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_resume(dev));
1237f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_autosuspend(dev));
1247f7acd19SBrian Norris
1257f7acd19SBrian Norris /* Still disabled */
1267f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
1277f7acd19SBrian Norris KUNIT_EXPECT_FALSE(test, pm_runtime_enabled(dev));
1287f7acd19SBrian Norris }
1297f7acd19SBrian Norris
pm_runtime_error_test(struct kunit * test)1307f7acd19SBrian Norris static void pm_runtime_error_test(struct kunit *test)
1317f7acd19SBrian Norris {
1327f7acd19SBrian Norris struct device *dev = kunit_device_register(test, DEVICE_NAME);
1337f7acd19SBrian Norris
134*92158faeSDan Carpenter KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
1357f7acd19SBrian Norris
1367f7acd19SBrian Norris pm_runtime_enable(dev);
1377f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
1387f7acd19SBrian Norris
1397f7acd19SBrian Norris /* Fake a .runtime_resume() error */
1407f7acd19SBrian Norris dev->power.runtime_error = -EIO;
1417f7acd19SBrian Norris
1427f7acd19SBrian Norris /*
1437f7acd19SBrian Norris * Note: these "fail", but they still acquire/release refcounts, so
1447f7acd19SBrian Norris * keep them balanced.
1457f7acd19SBrian Norris */
1467f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get(dev));
1477f7acd19SBrian Norris pm_runtime_put(dev);
1487f7acd19SBrian Norris
1497f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get_sync(dev));
1507f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_put_sync(dev));
1517f7acd19SBrian Norris
1527f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get(dev));
1537f7acd19SBrian Norris pm_runtime_put_autosuspend(dev);
1547f7acd19SBrian Norris
1557f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get(dev));
1567f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_put_sync_autosuspend(dev));
1577f7acd19SBrian Norris
1587f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_resume_and_get(dev));
1597f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_idle(dev));
1607f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_request_idle(dev));
1617f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_request_resume(dev));
1627f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_request_autosuspend(dev));
1637f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_suspend(dev));
1647f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_resume(dev));
1657f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_autosuspend(dev));
1667f7acd19SBrian Norris
1677f7acd19SBrian Norris /* Error is still pending */
1687f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
1697f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EIO, dev->power.runtime_error);
1707f7acd19SBrian Norris /* Clear error */
1717f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_set_suspended(dev));
1727f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, dev->power.runtime_error);
1737f7acd19SBrian Norris /* Still suspended */
1747f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
1757f7acd19SBrian Norris
1767f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_get(dev));
1777f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_runtime_barrier(dev)); /* resume was pending */
1787f7acd19SBrian Norris pm_runtime_put(dev);
1797f7acd19SBrian Norris pm_runtime_suspend(dev); /* flush the put(), to suspend */
1807f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
1817f7acd19SBrian Norris
1827f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
1837f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
1847f7acd19SBrian Norris
1857f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev));
1867f7acd19SBrian Norris pm_runtime_put_autosuspend(dev);
1877f7acd19SBrian Norris
1887f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_resume_and_get(dev));
1897f7acd19SBrian Norris
1907f7acd19SBrian Norris /*
1917f7acd19SBrian Norris * The following should all return -EAGAIN (usage is non-zero) or 1
1927f7acd19SBrian Norris * (already resumed).
1937f7acd19SBrian Norris */
1947f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_idle(dev));
1957f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EAGAIN, pm_request_idle(dev));
1967f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_request_resume(dev));
1977f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EAGAIN, pm_request_autosuspend(dev));
1987f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_suspend(dev));
1997f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 1, pm_runtime_resume(dev));
2007f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_autosuspend(dev));
2017f7acd19SBrian Norris
2027f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev));
2037f7acd19SBrian Norris
2047f7acd19SBrian Norris /* Suspended again */
2057f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
2067f7acd19SBrian Norris }
2077f7acd19SBrian Norris
2087f7acd19SBrian Norris /*
2097f7acd19SBrian Norris * Explore a typical probe() sequence in which a device marks itself powered,
2107f7acd19SBrian Norris * but doesn't hold any runtime PM reference, so it suspends as soon as it goes
2117f7acd19SBrian Norris * idle.
2127f7acd19SBrian Norris */
pm_runtime_probe_active_test(struct kunit * test)2137f7acd19SBrian Norris static void pm_runtime_probe_active_test(struct kunit *test)
2147f7acd19SBrian Norris {
2157f7acd19SBrian Norris struct device *dev = kunit_device_register(test, DEVICE_NAME);
2167f7acd19SBrian Norris
217*92158faeSDan Carpenter KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
2187f7acd19SBrian Norris
2197f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_status_suspended(dev));
2207f7acd19SBrian Norris
2217f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_set_active(dev));
2227f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
2237f7acd19SBrian Norris
2247f7acd19SBrian Norris pm_runtime_enable(dev);
2257f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
2267f7acd19SBrian Norris
2277f7acd19SBrian Norris /* Nothing to flush. We stay active. */
2287f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_barrier(dev));
2297f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev));
2307f7acd19SBrian Norris
2317f7acd19SBrian Norris /* Ask for idle? Now we suspend. */
2327f7acd19SBrian Norris KUNIT_EXPECT_EQ(test, 0, pm_runtime_idle(dev));
2337f7acd19SBrian Norris KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev));
2347f7acd19SBrian Norris }
2357f7acd19SBrian Norris
2367f7acd19SBrian Norris static struct kunit_case pm_runtime_test_cases[] = {
2377f7acd19SBrian Norris KUNIT_CASE(pm_runtime_depth_test),
2387f7acd19SBrian Norris KUNIT_CASE(pm_runtime_already_suspended_test),
2397f7acd19SBrian Norris KUNIT_CASE(pm_runtime_idle_test),
2407f7acd19SBrian Norris KUNIT_CASE(pm_runtime_disabled_test),
2417f7acd19SBrian Norris KUNIT_CASE(pm_runtime_error_test),
2427f7acd19SBrian Norris KUNIT_CASE(pm_runtime_probe_active_test),
2437f7acd19SBrian Norris {}
2447f7acd19SBrian Norris };
2457f7acd19SBrian Norris
2467f7acd19SBrian Norris static struct kunit_suite pm_runtime_test_suite = {
2477f7acd19SBrian Norris .name = "pm_runtime_test_cases",
2487f7acd19SBrian Norris .test_cases = pm_runtime_test_cases,
2497f7acd19SBrian Norris };
2507f7acd19SBrian Norris
2517f7acd19SBrian Norris kunit_test_suite(pm_runtime_test_suite);
2527f7acd19SBrian Norris MODULE_DESCRIPTION("Runtime power management unit test suite");
2537f7acd19SBrian Norris MODULE_LICENSE("GPL");
254