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:
parent
c65be0cfb9
commit
1c2e988021
5 changed files with 137 additions and 76 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
89
subsys/pm/device_system_managed.c
Normal file
89
subsys/pm/device_system_managed.c
Normal 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
|
22
subsys/pm/device_system_managed.h
Normal file
22
subsys/pm/device_system_managed.h
Normal 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_ */
|
|
@ -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 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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue