| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright 2025 Google, Inc. | |
| */ | |
| #include <linux/cleanup.h> | |
| #include <linux/pm_runtime.h> | |
| #include <kunit/device.h> | |
| #include <kunit/test.h> | |
| #define DEVICE_NAME "pm_runtime_test_device" | |
| static void pm_runtime_depth_test(struct kunit *test) | |
| { | |
| struct device *dev = kunit_device_register(test, DEVICE_NAME); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); | |
| pm_runtime_enable(dev); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev)); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev)); | |
| KUNIT_EXPECT_EQ(test, 1, pm_runtime_get_sync(dev)); /* "already active" */ | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev)); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| } | |
| /* Test pm_runtime_put() and friends when already suspended. */ | |
| static void pm_runtime_already_suspended_test(struct kunit *test) | |
| { | |
| struct device *dev = kunit_device_register(test, DEVICE_NAME); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); | |
| pm_runtime_enable(dev); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| pm_runtime_get_noresume(dev); | |
| KUNIT_EXPECT_EQ(test, 1, pm_runtime_put_sync(dev)); | |
| KUNIT_EXPECT_EQ(test, 1, pm_runtime_suspend(dev)); | |
| KUNIT_EXPECT_EQ(test, 1, pm_runtime_autosuspend(dev)); | |
| KUNIT_EXPECT_EQ(test, 1, pm_request_autosuspend(dev)); | |
| pm_runtime_get_noresume(dev); | |
| KUNIT_EXPECT_EQ(test, 1, pm_runtime_put_sync_autosuspend(dev)); | |
| pm_runtime_get_noresume(dev); | |
| pm_runtime_put_autosuspend(dev); | |
| /* Grab 2 refcounts */ | |
| pm_runtime_get_noresume(dev); | |
| pm_runtime_get_noresume(dev); | |
| /* The first put() sees usage_count 1 */ | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync_autosuspend(dev)); | |
| /* The second put() sees usage_count 0 but tells us "already suspended". */ | |
| KUNIT_EXPECT_EQ(test, 1, pm_runtime_put_sync_autosuspend(dev)); | |
| /* Should have remained suspended the whole time. */ | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| } | |
| static void pm_runtime_idle_test(struct kunit *test) | |
| { | |
| struct device *dev = kunit_device_register(test, DEVICE_NAME); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); | |
| pm_runtime_enable(dev); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev)); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev)); | |
| KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_idle(dev)); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev)); | |
| pm_runtime_put_noidle(dev); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_idle(dev)); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_idle(dev)); | |
| KUNIT_EXPECT_EQ(test, -EAGAIN, pm_request_idle(dev)); | |
| } | |
| static void pm_runtime_disabled_test(struct kunit *test) | |
| { | |
| struct device *dev = kunit_device_register(test, DEVICE_NAME); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); | |
| /* Never called pm_runtime_enable() */ | |
| KUNIT_EXPECT_FALSE(test, pm_runtime_enabled(dev)); | |
| /* "disabled" is treated as "active" */ | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev)); | |
| KUNIT_EXPECT_FALSE(test, pm_runtime_suspended(dev)); | |
| /* | |
| * Note: these "fail", but they still acquire/release refcounts, so | |
| * keep them balanced. | |
| */ | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_get(dev)); | |
| pm_runtime_put(dev); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_get_sync(dev)); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_put_sync(dev)); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_get(dev)); | |
| pm_runtime_put_autosuspend(dev); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_resume_and_get(dev)); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_idle(dev)); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_request_idle(dev)); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_request_resume(dev)); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_request_autosuspend(dev)); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_suspend(dev)); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_resume(dev)); | |
| KUNIT_EXPECT_EQ(test, -EACCES, pm_runtime_autosuspend(dev)); | |
| /* Still disabled */ | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev)); | |
| KUNIT_EXPECT_FALSE(test, pm_runtime_enabled(dev)); | |
| } | |
| static void pm_runtime_error_test(struct kunit *test) | |
| { | |
| struct device *dev = kunit_device_register(test, DEVICE_NAME); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); | |
| pm_runtime_enable(dev); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| /* Fake a .runtime_resume() error */ | |
| dev->power.runtime_error = -EIO; | |
| /* | |
| * Note: these "fail", but they still acquire/release refcounts, so | |
| * keep them balanced. | |
| */ | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get(dev)); | |
| pm_runtime_put(dev); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get_sync(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_put_sync(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get(dev)); | |
| pm_runtime_put_autosuspend(dev); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_get(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_put_sync_autosuspend(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_resume_and_get(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_idle(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_request_idle(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_request_resume(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_request_autosuspend(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_suspend(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_resume(dev)); | |
| KUNIT_EXPECT_EQ(test, -EINVAL, pm_runtime_autosuspend(dev)); | |
| /* Error is still pending */ | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| KUNIT_EXPECT_EQ(test, -EIO, dev->power.runtime_error); | |
| /* Clear error */ | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_set_suspended(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, dev->power.runtime_error); | |
| /* Still suspended */ | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_get(dev)); | |
| pm_runtime_barrier(dev); | |
| pm_runtime_put(dev); | |
| pm_runtime_suspend(dev); /* flush the put(), to suspend */ | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_get_sync(dev)); | |
| pm_runtime_put_autosuspend(dev); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_resume_and_get(dev)); | |
| /* | |
| * The following should all return -EAGAIN (usage is non-zero) or 1 | |
| * (already resumed). | |
| */ | |
| KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_idle(dev)); | |
| KUNIT_EXPECT_EQ(test, -EAGAIN, pm_request_idle(dev)); | |
| KUNIT_EXPECT_EQ(test, 1, pm_request_resume(dev)); | |
| KUNIT_EXPECT_EQ(test, -EAGAIN, pm_request_autosuspend(dev)); | |
| KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_suspend(dev)); | |
| KUNIT_EXPECT_EQ(test, 1, pm_runtime_resume(dev)); | |
| KUNIT_EXPECT_EQ(test, -EAGAIN, pm_runtime_autosuspend(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_put_sync(dev)); | |
| /* Suspended again */ | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| } | |
| /* | |
| * Explore a typical probe() sequence in which a device marks itself powered, | |
| * but doesn't hold any runtime PM reference, so it suspends as soon as it goes | |
| * idle. | |
| */ | |
| static void pm_runtime_probe_active_test(struct kunit *test) | |
| { | |
| struct device *dev = kunit_device_register(test, DEVICE_NAME); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_status_suspended(dev)); | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_set_active(dev)); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev)); | |
| pm_runtime_enable(dev); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev)); | |
| /* Nothing to flush. We stay active. */ | |
| pm_runtime_barrier(dev); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_active(dev)); | |
| /* Ask for idle? Now we suspend. */ | |
| KUNIT_EXPECT_EQ(test, 0, pm_runtime_idle(dev)); | |
| KUNIT_EXPECT_TRUE(test, pm_runtime_suspended(dev)); | |
| } | |
| static struct kunit_case pm_runtime_test_cases[] = { | |
| KUNIT_CASE(pm_runtime_depth_test), | |
| KUNIT_CASE(pm_runtime_already_suspended_test), | |
| KUNIT_CASE(pm_runtime_idle_test), | |
| KUNIT_CASE(pm_runtime_disabled_test), | |
| KUNIT_CASE(pm_runtime_error_test), | |
| KUNIT_CASE(pm_runtime_probe_active_test), | |
| {} | |
| }; | |
| static struct kunit_suite pm_runtime_test_suite = { | |
| .name = "pm_runtime_test_cases", | |
| .test_cases = pm_runtime_test_cases, | |
| }; | |
| kunit_test_suite(pm_runtime_test_suite); | |
| MODULE_DESCRIPTION("Runtime power management unit test suite"); | |
| MODULE_LICENSE("GPL"); |