From 1c2e9880216a052fc3b64004a4b9586e48ac56e4 Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Tue, 16 Jan 2024 15:17:02 -0800 Subject: [PATCH] pm: device: De-couple device pm from system pm PM_DEVICE is not attached to system managed device power management. It is a very common use case targets with device runtime power management that don't want system device power management enabled. We introduce a new symbol (PM_DEVICE_SYSTEM_MANAGED) to explicit control whether or not system device power management should be globally enabled. Signed-off-by: Flavio Ceolin --- subsys/pm/CMakeLists.txt | 1 + subsys/pm/Kconfig | 23 +++++--- subsys/pm/device_system_managed.c | 89 +++++++++++++++++++++++++++++++ subsys/pm/device_system_managed.h | 22 ++++++++ subsys/pm/pm.c | 78 +++------------------------ 5 files changed, 137 insertions(+), 76 deletions(-) create mode 100644 subsys/pm/device_system_managed.c create mode 100644 subsys/pm/device_system_managed.h diff --git a/subsys/pm/CMakeLists.txt b/subsys/pm/CMakeLists.txt index a9aba72caca..0e961abda78 100644 --- a/subsys/pm/CMakeLists.txt +++ b/subsys/pm/CMakeLists.txt @@ -8,3 +8,4 @@ endif() zephyr_sources_ifdef(CONFIG_PM_DEVICE device.c) zephyr_sources_ifdef(CONFIG_PM_DEVICE_RUNTIME device_runtime.c) zephyr_sources_ifdef(CONFIG_PM_DEVICE_SHELL pm_shell.c) +zephyr_sources_ifdef(CONFIG_PM_DEVICE_SYSTEM_MANAGED device_system_managed.c) diff --git a/subsys/pm/Kconfig b/subsys/pm/Kconfig index 5caf01f1375..2b7ef998415 100644 --- a/subsys/pm/Kconfig +++ b/subsys/pm/Kconfig @@ -78,12 +78,11 @@ config PM_DEVICE bool "Device Power Management" help This option enables the device power management interface. The - interface consists of hook functions implemented by device drivers - that get called by the power manager application when the system - is going to suspend state or resuming from suspend state. This allows - device drivers to do any necessary power management operations - like turning off device clocks and peripherals. The device drivers - may also save and restore states in these hook functions. + interface implemented by device drivers are called by the power + management subsystem. This allows device drivers to do any + necessary power management operations like turning off + device clocks and peripherals. Device drivers may also save + and restore states in these hook functions. if PM_DEVICE @@ -137,6 +136,18 @@ config PM_DEVICE_SHELL Enable the device power management shell, for triggering device power management events through the shell interface. +config PM_DEVICE_SYSTEM_MANAGED + bool "System-Managed Device Power Management" + default y if !PM_DEVICE_RUNTIME_EXCLUSIVE + default y if !PM_DEVICE_RUNTIME + help + This option enables the system-managed device power + management. The power management subsystem will suspend + devices before entering a low power state. Conversely, after + the core wakes up from low power mode all suspended devices + are resumed. + + endif # PM_DEVICE endmenu diff --git a/subsys/pm/device_system_managed.c b/subsys/pm/device_system_managed.c new file mode 100644 index 00000000000..6507267d6a6 --- /dev/null +++ b/subsys/pm/device_system_managed.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +LOG_MODULE_DECLARE(pm_device, CONFIG_PM_DEVICE_LOG_LEVEL); + +#define DT_PM_DEVICE_ENABLED(node_id) \ + COND_CODE_1(DT_PROP(node_id, zephyr_pm_device_disabled), \ + (), (1 +)) + +#define DT_PM_DEVICE_NEEDED \ + (DT_FOREACH_STATUS_OKAY(zephyr_power_state, DT_PM_DEVICE_ENABLED) 0) + +#if DT_PM_DEVICE_NEEDED +TYPE_SECTION_START_EXTERN(const struct device *, pm_device_slots); + +/* Number of devices successfully suspended. */ +static size_t num_susp; + +bool pm_suspend_devices(void) +{ + const struct device *devs; + size_t devc; + + devc = z_device_get_all_static(&devs); + + num_susp = 0; + + for (const struct device *dev = devs + devc - 1; dev >= devs; dev--) { + int ret; + + /* + * Ignore uninitialized devices, busy devices, wake up sources, and + * devices with runtime PM enabled. + */ + if (!device_is_ready(dev) || pm_device_is_busy(dev) || + pm_device_wakeup_is_enabled(dev) || + pm_device_runtime_is_enabled(dev)) { + continue; + } + + ret = pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND); + /* ignore devices not supporting or already at the given state */ + if ((ret == -ENOSYS) || (ret == -ENOTSUP) || (ret == -EALREADY)) { + continue; + } else if (ret < 0) { + LOG_ERR("Device %s did not enter %s state (%d)", + dev->name, + pm_device_state_str(PM_DEVICE_STATE_SUSPENDED), + ret); + return false; + } + + TYPE_SECTION_START(pm_device_slots)[num_susp] = dev; + num_susp++; + } + + return true; +} + +void pm_resume_devices(void) +{ + for (int i = (num_susp - 1); i >= 0; i--) { + pm_device_action_run(TYPE_SECTION_START(pm_device_slots)[i], + PM_DEVICE_ACTION_RESUME); + } + + num_susp = 0; +} + +#else /* !DT_PM_DEVICE_NEEDED */ + +void pm_resume_devices(void) +{ +} + +bool pm_suspend_devices(void) +{ + return true; +} + +#endif diff --git a/subsys/pm/device_system_managed.h b/subsys/pm/device_system_managed.h new file mode 100644 index 00000000000..6c2e9b930f2 --- /dev/null +++ b/subsys/pm/device_system_managed.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SUBSYS_PM_DEVICE_SYSTEM_MANAGED_H_ +#define ZEPHYR_SUBSYS_PM_DEVICE_SYSTEM_MANAGED_H_ + +#ifdef CONFIG_PM_DEVICE_SYSTEM_MANAGED + +bool pm_suspend_devices(void); +void pm_resume_devices(void); + +#else + +bool pm_resume_devices(void) { return true; } +void pm_suspend_devices(void) {} + +#endif /* CONFIG_PM_DEVICE_SYSTEM_MANAGED */ + +#endif /* ZEPHYR_SUBSYS_PM_DEVICE_SYSTEM_MANAGED_H_ */ diff --git a/subsys/pm/pm.c b/subsys/pm/pm.c index 643a47bcffd..25e60b68a6d 100644 --- a/subsys/pm/pm.c +++ b/subsys/pm/pm.c @@ -18,6 +18,7 @@ #include #include "pm_stats.h" +#include "device_system_managed.h" #include LOG_MODULE_REGISTER(pm, CONFIG_PM_LOG_LEVEL); @@ -42,72 +43,6 @@ static struct pm_state_info z_cpus_pm_forced_state[] = { static struct k_spinlock pm_forced_state_lock; static struct k_spinlock pm_notifier_lock; -#define DT_PM_DEVICE_ENABLED(node_id) \ - COND_CODE_1(DT_PROP_OR(node_id, zephyr_pm_device_disabled, 0), \ - (), (1 +)) - -#define DT_PM_DEVICE_NEEDED \ - (DT_FOREACH_STATUS_OKAY(zephyr_power_state, DT_PM_DEVICE_ENABLED) 0) - -#if defined(CONFIG_PM_DEVICE) && DT_PM_DEVICE_NEEDED -TYPE_SECTION_START_EXTERN(const struct device *, pm_device_slots); - -#if !defined(CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE) -/* Number of devices successfully suspended. */ -static size_t num_susp; - -static int pm_suspend_devices(void) -{ - const struct device *devs; - size_t devc; - - devc = z_device_get_all_static(&devs); - - num_susp = 0; - - for (const struct device *dev = devs + devc - 1; dev >= devs; dev--) { - int ret; - - /* - * Ignore uninitialized devices, busy devices, wake up sources, and - * devices with runtime PM enabled. - */ - if (!device_is_ready(dev) || pm_device_is_busy(dev) || - pm_device_wakeup_is_enabled(dev) || - pm_device_runtime_is_enabled(dev)) { - continue; - } - - ret = pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND); - /* ignore devices not supporting or already at the given state */ - if ((ret == -ENOSYS) || (ret == -ENOTSUP) || (ret == -EALREADY)) { - continue; - } else if (ret < 0) { - LOG_ERR("Device %s did not enter %s state (%d)", - dev->name, - pm_device_state_str(PM_DEVICE_STATE_SUSPENDED), - ret); - return ret; - } - - TYPE_SECTION_START(pm_device_slots)[num_susp] = dev; - num_susp++; - } - - return 0; -} - -static void pm_resume_devices(void) -{ - for (int i = (num_susp - 1); i >= 0; i--) { - pm_device_action_run(TYPE_SECTION_START(pm_device_slots)[i], - PM_DEVICE_ACTION_RESUME); - } - - num_susp = 0; -} -#endif /* defined(CONFIG_PM_DEVICE) && DT_PM_DEVICE_NEEDED */ - /* * Function called to notify when the system is entering / exiting a * power state @@ -150,9 +85,12 @@ void pm_system_resume(void) * and it may schedule another thread. */ if (atomic_test_and_clear_bit(z_post_ops_required, id)) { -#if defined(CONFIG_PM_DEVICE) && DT_PM_DEVICE_NEEDED +#ifdef CONFIG_PM_DEVICE_SYSTEM_MANAGED if (atomic_add(&_cpus_active, 1) == 0) { - pm_resume_devices(); + if ((z_cpus_pm_state[id].state != PM_STATE_RUNTIME_IDLE) && + !z_cpus_pm_state[id].pm_device_disabled) { + pm_resume_devices(); + } } #endif pm_state_exit_post_ops(z_cpus_pm_state[id].state, z_cpus_pm_state[id].substate_id); @@ -209,11 +147,11 @@ bool pm_system_suspend(int32_t ticks) return false; } -#if defined(CONFIG_PM_DEVICE) && DT_PM_DEVICE_NEEDED +#ifdef CONFIG_PM_DEVICE_SYSTEM_MANAGED if (atomic_sub(&_cpus_active, 1) == 1) { if ((z_cpus_pm_state[id].state != PM_STATE_RUNTIME_IDLE) && !z_cpus_pm_state[id].pm_device_disabled) { - if (pm_suspend_devices()) { + if (!pm_suspend_devices()) { pm_resume_devices(); z_cpus_pm_state[id].state = PM_STATE_ACTIVE; (void)atomic_add(&_cpus_active, 1);