/* * Copyright (c) 2021 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #define NUMBER_OF_DEVICES 3 #define TEST_DOMAIN DT_NODELABEL(test_domain) #define TEST_DEVA DT_NODELABEL(test_dev_a) #define TEST_DEVB DT_NODELABEL(test_dev_b) static const struct device *const domain = DEVICE_DT_GET(TEST_DOMAIN); static const struct device *const deva = DEVICE_DT_GET(TEST_DEVA); static const struct device *const devb = DEVICE_DT_GET(TEST_DEVB); static const struct device *devc; static int testing_domain_on_notitication; static int testing_domain_off_notitication; static int domain_pm_action(const struct device *dev, enum pm_device_action action) { int rc = 0; switch (action) { case PM_DEVICE_ACTION_RESUME: /* Switch power on */ pm_device_children_action_run(dev, PM_DEVICE_ACTION_TURN_ON, NULL); break; case PM_DEVICE_ACTION_SUSPEND: pm_device_children_action_run(dev, PM_DEVICE_ACTION_TURN_OFF, NULL); break; case PM_DEVICE_ACTION_TURN_ON: __fallthrough; case PM_DEVICE_ACTION_TURN_OFF: break; default: rc = -ENOTSUP; } return rc; } static int deva_pm_action(const struct device *dev, enum pm_device_action pm_action) { ARG_UNUSED(dev); if (testing_domain_on_notitication > 0) { if (pm_action == PM_DEVICE_ACTION_TURN_ON) { testing_domain_on_notitication--; } } else if (testing_domain_off_notitication > 0) { if (pm_action == PM_DEVICE_ACTION_TURN_OFF) { testing_domain_off_notitication--; } } return 0; } /* * Device B will return -ENOTSUP for TURN_ON and TURN_OFF actions. * This way we can check if the subsystem properly handled its state. */ static int devb_pm_action(const struct device *dev, enum pm_device_action pm_action) { int ret = 0; ARG_UNUSED(dev); if (testing_domain_on_notitication > 0) { if (pm_action == PM_DEVICE_ACTION_TURN_ON) { ret = -ENOTSUP; testing_domain_on_notitication--; } } else if (testing_domain_off_notitication > 0) { if (pm_action == PM_DEVICE_ACTION_TURN_OFF) { ret = -ENOTSUP; testing_domain_off_notitication--; } } return ret; } PM_DEVICE_DT_DEFINE(TEST_DOMAIN, domain_pm_action); DEVICE_DT_DEFINE(TEST_DOMAIN, NULL, PM_DEVICE_DT_GET(TEST_DOMAIN), NULL, NULL, POST_KERNEL, 10, NULL); PM_DEVICE_DT_DEFINE(TEST_DEVA, deva_pm_action); DEVICE_DT_DEFINE(TEST_DEVA, NULL, PM_DEVICE_DT_GET(TEST_DEVA), NULL, NULL, POST_KERNEL, 20, NULL); PM_DEVICE_DT_DEFINE(TEST_DEVB, devb_pm_action); DEVICE_DT_DEFINE(TEST_DEVB, NULL, PM_DEVICE_DT_GET(TEST_DEVB), NULL, NULL, POST_KERNEL, 30, NULL); PM_DEVICE_DEFINE(devc, deva_pm_action); DEVICE_DEFINE(devc, "devc", NULL, PM_DEVICE_GET(devc), NULL, NULL, POST_KERNEL, 40, NULL); /** * @brief Test the power domain behavior * * Scenarios tested: * * - get + put multiple devices under a domain * - notification when domain state changes */ ZTEST(power_domain_1cpu, test_power_domain_device_runtime) { int ret; enum pm_device_state state; devc = DEVICE_GET(devc); pm_device_init_suspended(domain); pm_device_init_suspended(deva); pm_device_init_suspended(devb); pm_device_init_suspended(devc); pm_device_runtime_enable(domain); pm_device_runtime_enable(deva); pm_device_runtime_enable(devb); pm_device_runtime_enable(devc); ret = pm_device_power_domain_remove(devc, domain); zassert_equal(ret, -ENOENT); ret = pm_device_power_domain_add(devc, domain); zassert_equal(ret, 0); /* At this point all devices should be SUSPENDED */ pm_device_state_get(domain, &state); zassert_equal(state, PM_DEVICE_STATE_SUSPENDED); pm_device_state_get(deva, &state); zassert_equal(state, PM_DEVICE_STATE_SUSPENDED); pm_device_state_get(devb, &state); zassert_equal(state, PM_DEVICE_STATE_SUSPENDED); pm_device_state_get(devc, &state); zassert_equal(state, PM_DEVICE_STATE_SUSPENDED); /* Now test if "get" a device will resume the domain */ ret = pm_device_runtime_get(deva); zassert_equal(ret, 0); pm_device_state_get(deva, &state); zassert_equal(state, PM_DEVICE_STATE_ACTIVE); pm_device_state_get(domain, &state); zassert_equal(state, PM_DEVICE_STATE_ACTIVE); ret = pm_device_runtime_get(devc); zassert_equal(ret, 0); ret = pm_device_runtime_get(devb); zassert_equal(ret, 0); ret = pm_device_runtime_put(deva); zassert_equal(ret, 0); /* * The domain has to still be active since device B * is still in use. */ pm_device_state_get(domain, &state); zassert_equal(state, PM_DEVICE_STATE_ACTIVE); /* * Now the domain should be suspended since there is no * one using it. */ ret = pm_device_runtime_put(devb); zassert_equal(ret, 0); ret = pm_device_runtime_put(devc); zassert_equal(ret, 0); pm_device_state_get(domain, &state); zassert_equal(state, PM_DEVICE_STATE_SUSPENDED); /* * With the domain suspended the device state should be OFF, since * the power was completely cut. */ pm_device_state_get(devb, &state); zassert_equal(state, PM_DEVICE_STATE_OFF); pm_device_state_get(deva, &state); zassert_equal(state, PM_DEVICE_STATE_OFF); /* * Now lets test that devices are notified when the domain * changes its state. */ /* Three devices has to get the notification */ testing_domain_on_notitication = NUMBER_OF_DEVICES; ret = pm_device_runtime_get(domain); zassert_equal(ret, 0); zassert_equal(testing_domain_on_notitication, 0); testing_domain_off_notitication = NUMBER_OF_DEVICES; ret = pm_device_runtime_put(domain); zassert_equal(ret, 0); zassert_equal(testing_domain_off_notitication, 0); ret = pm_device_power_domain_remove(devc, domain); zassert_equal(ret, 0); } #define TEST_DOMAIN_BALANCED DT_NODELABEL(test_domain_balanced) #define TEST_DEV_BALANCED DT_NODELABEL(test_dev_balanced) PM_DEVICE_DT_DEFINE(TEST_DOMAIN_BALANCED, domain_pm_action); DEVICE_DT_DEFINE(TEST_DOMAIN_BALANCED, NULL, PM_DEVICE_DT_GET(TEST_DOMAIN_BALANCED), NULL, NULL, POST_KERNEL, 10, NULL); PM_DEVICE_DT_DEFINE(TEST_DEV_BALANCED, deva_pm_action); DEVICE_DT_DEFINE(TEST_DEV_BALANCED, NULL, PM_DEVICE_DT_GET(TEST_DEV_BALANCED), NULL, NULL, POST_KERNEL, 20, NULL); /** * @brief Test power domain requests are balanced * * Scenarios tested: * * - get + put device with a PD while PM is disabled */ ZTEST(power_domain_1cpu, test_power_domain_device_balanced) { const struct device *balanced_domain = DEVICE_DT_GET(TEST_DOMAIN_BALANCED); const struct device *dev = DEVICE_DT_GET(TEST_DEV_BALANCED); enum pm_device_state state; int ret; /* Init domain */ pm_device_init_suspended(balanced_domain); pm_device_runtime_enable(balanced_domain); /* At this point domain should be SUSPENDED */ pm_device_state_get(balanced_domain, &state); zassert_equal(state, PM_DEVICE_STATE_SUSPENDED); /* Get and put the device without PM enabled should not change the domain */ ret = pm_device_runtime_get(dev); zassert_equal(ret, 0); ret = pm_device_runtime_put(dev); zassert_equal(ret, 0); pm_device_state_get(balanced_domain, &state); zassert_equal(state, PM_DEVICE_STATE_SUSPENDED); /* Same thing with the domain in active state */ ret = pm_device_runtime_get(balanced_domain); zassert_equal(ret, 0); pm_device_state_get(balanced_domain, &state); zassert_equal(state, PM_DEVICE_STATE_ACTIVE); ret = pm_device_runtime_get(dev); zassert_equal(ret, 0); ret = pm_device_runtime_put(dev); zassert_equal(ret, 0); pm_device_state_get(balanced_domain, &state); zassert_equal(state, PM_DEVICE_STATE_ACTIVE); } ZTEST(power_domain_1cpu, test_on_power_domain) { zassert_true(device_is_ready(domain), "Device is not ready!"); zassert_true(device_is_ready(deva), "Device is not ready!"); devc = DEVICE_GET(devc); zassert_true(device_is_ready(devc), "Device is not ready!"); pm_device_power_domain_remove(deva, domain); zassert_false(pm_device_on_power_domain(deva), "deva is in the power domain."); pm_device_power_domain_add(deva, domain); zassert_true(pm_device_on_power_domain(deva), "deva is not in the power domain."); pm_device_power_domain_add(devc, domain); zassert_true(pm_device_on_power_domain(devc), "devc is not in the power domain."); pm_device_power_domain_remove(devc, domain); zassert_false(pm_device_on_power_domain(devc), "devc in in the power domain."); } ZTEST_SUITE(power_domain_1cpu, NULL, NULL, ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);