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>
This commit is contained in:
Flavio Ceolin 2024-01-16 15:17:02 -08:00 committed by Anas Nashif
commit 1c2e988021
5 changed files with 137 additions and 76 deletions

View file

@ -8,3 +8,4 @@ endif()
zephyr_sources_ifdef(CONFIG_PM_DEVICE device.c) zephyr_sources_ifdef(CONFIG_PM_DEVICE device.c)
zephyr_sources_ifdef(CONFIG_PM_DEVICE_RUNTIME device_runtime.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_SHELL pm_shell.c)
zephyr_sources_ifdef(CONFIG_PM_DEVICE_SYSTEM_MANAGED device_system_managed.c)

View file

@ -78,12 +78,11 @@ config PM_DEVICE
bool "Device Power Management" bool "Device Power Management"
help help
This option enables the device power management interface. The This option enables the device power management interface. The
interface consists of hook functions implemented by device drivers interface implemented by device drivers are called by the power
that get called by the power manager application when the system management subsystem. This allows device drivers to do any
is going to suspend state or resuming from suspend state. This allows necessary power management operations like turning off
device drivers to do any necessary power management operations device clocks and peripherals. Device drivers may also save
like turning off device clocks and peripherals. The device drivers and restore states in these hook functions.
may also save and restore states in these hook functions.
if PM_DEVICE if PM_DEVICE
@ -137,6 +136,18 @@ config PM_DEVICE_SHELL
Enable the device power management shell, for triggering device power Enable the device power management shell, for triggering device power
management events through the shell interface. 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 endif # PM_DEVICE
endmenu endmenu

View file

@ -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

View file

@ -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_ */

View file

@ -18,6 +18,7 @@
#include <zephyr/tracing/tracing.h> #include <zephyr/tracing/tracing.h>
#include "pm_stats.h" #include "pm_stats.h"
#include "device_system_managed.h"
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(pm, CONFIG_PM_LOG_LEVEL); 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_forced_state_lock;
static struct k_spinlock pm_notifier_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 * Function called to notify when the system is entering / exiting a
* power state * power state
@ -150,9 +85,12 @@ void pm_system_resume(void)
* and it may schedule another thread. * and it may schedule another thread.
*/ */
if (atomic_test_and_clear_bit(z_post_ops_required, id)) { 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) { 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 #endif
pm_state_exit_post_ops(z_cpus_pm_state[id].state, z_cpus_pm_state[id].substate_id); 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; 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 (atomic_sub(&_cpus_active, 1) == 1) {
if ((z_cpus_pm_state[id].state != PM_STATE_RUNTIME_IDLE) && if ((z_cpus_pm_state[id].state != PM_STATE_RUNTIME_IDLE) &&
!z_cpus_pm_state[id].pm_device_disabled) { !z_cpus_pm_state[id].pm_device_disabled) {
if (pm_suspend_devices()) { if (!pm_suspend_devices()) {
pm_resume_devices(); pm_resume_devices();
z_cpus_pm_state[id].state = PM_STATE_ACTIVE; z_cpus_pm_state[id].state = PM_STATE_ACTIVE;
(void)atomic_add(&_cpus_active, 1); (void)atomic_add(&_cpus_active, 1);