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 <flavio.ceolin@intel.com>
diff --git a/subsys/pm/CMakeLists.txt b/subsys/pm/CMakeLists.txt
index a9aba72..0e961ab 100644
--- a/subsys/pm/CMakeLists.txt
+++ b/subsys/pm/CMakeLists.txt
@@ -8,3 +8,4 @@
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 5caf01f..2b7ef99 100644
--- a/subsys/pm/Kconfig
+++ b/subsys/pm/Kconfig
@@ -78,12 +78,11 @@
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 @@
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 0000000..6507267
--- /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 <zephyr/kernel.h>
+#include <zephyr/pm/device.h>
+#include <zephyr/pm/device_runtime.h>
+
+#include <zephyr/logging/log.h>
+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 0000000..6c2e9b9
--- /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 643a47b..25e60b6 100644
--- a/subsys/pm/pm.c
+++ b/subsys/pm/pm.c
@@ -18,6 +18,7 @@
#include <zephyr/tracing/tracing.h>
#include "pm_stats.h"
+#include "device_system_managed.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(pm, CONFIG_PM_LOG_LEVEL);
@@ -42,72 +43,6 @@
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 @@
* 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 @@
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);