Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pm: Make device pm optional per state - V2 #70623

Merged
merged 10 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 33 additions & 9 deletions doc/services/pm/device.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,46 @@ For more information, see :ref:`pm-device-runtime`.
System-Managed Device Power Management
======================================

The system managed device power management (PM) framework is a method where
devices are suspended along with the system entering a CPU (or SoC) power state.
It can be enabled by setting :kconfig:option:`CONFIG_PM_DEVICE_SYSTEM_MANAGED`.
When using this method, device power management is mostly done inside
:c:func:`pm_system_suspend()` along with entering a CPU or SOC power state.
:c:func:`pm_system_suspend()`.

If a decision to enter a CPU lower power state is made, the power management
subsystem will suspend devices before changing state. The subsystem takes care
of suspending devices following their initialization order, ensuring that
subsystem will check if the selected low power state triggers device power
management and then suspend devices before changing state. The subsystem takes
care of suspending devices following their initialization order, ensuring that
possible dependencies between them are satisfied. As soon as the CPU wakes up
from a sleep state, devices are resumed in the opposite order that they were
suspended.

The decision about suspending devices when entering a low power state is done based on the
state and if it has set the property ``zephyr,pm-device-disabled``. Here is
an example of a target with two low power states with only one triggering device power
management:

.. code-block:: devicetree

/* Node in a DTS file */
cpus {
power-states {
state0: state0 {
compatible = "zephyr,power-state";
power-state-name = "standby";
min-residency-us = <5000>;
exit-latency-us = <240>;
zephyr,pm-device-disabled;
};
state1: state1 {
compatible = "zephyr,power-state";
power-state-name = "suspend-to-ram";
min-residency-us = <8000>;
exit-latency-us = <360>;
};
};
};

.. note::

When using :ref:`pm-system`, device transitions can be run from the idle thread.
Expand Down Expand Up @@ -114,12 +144,6 @@ It is important to emphasize that this method has drawbacks (see above) and
:kconfig:option:`CONFIG_PM_NEED_ALL_DEVICES_IDLE` is set and a device
is marked as busy.

.. note::

This method of device power management is disabled when
:kconfig:option:`CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE` is set to ``y`` (that is
the default value when :kconfig:option:`CONFIG_PM_DEVICE_RUNTIME` is enabled)

.. note::

Devices are suspended only when the last active core is entering a low power
Expand Down
6 changes: 6 additions & 0 deletions dts/bindings/power/zephyr,power-state.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ properties:
type: int
description: |
Worst case latency in microseconds required to exit the idle state.
zephyr,pm-device-disabled:
type: boolean
description: |
Disable system managed device power management for this state. When set,
the power management subsystem will not suspend devices before entering
this state.
12 changes: 12 additions & 0 deletions include/zephyr/pm/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,22 @@ struct pm_state_info {
* substate-id = <2>;
* min-residency-us = <20000>;
* exit-latency-us = <200>;
* zephyr,pm-device-disabled;
* };
* };
* @endcode
*/
uint8_t substate_id;

/**
* Whether or not this state triggers device power management.
*
* When this property is false the power management subsystem
* will suspend devices before entering this state and will
* properly resume them when leaving it.
*/
bool pm_device_disabled;

/**
* Minimum residency duration in microseconds. It is the minimum
* time for a given idle state to be worthwhile energywise.
Expand Down Expand Up @@ -209,6 +219,7 @@ struct pm_state_info {
.substate_id = DT_PROP_OR(node_id, substate_id, 0), \
.min_residency_us = DT_PROP_OR(node_id, min_residency_us, 0), \
.exit_latency_us = DT_PROP_OR(node_id, exit_latency_us, 0), \
.pm_device_disabled = DT_PROP(node_id, zephyr_pm_device_disabled), \
}

/**
Expand Down Expand Up @@ -260,6 +271,7 @@ struct pm_state_info {
* power-state-name = "suspend-to-ram";
* min-residency-us = <50000>;
* exit-latency-us = <500>;
* zephyr,pm-device-disabled;
* };
* };
* };
Expand Down
2 changes: 1 addition & 1 deletion samples/boards/stm32/power_mgmt/adc/prj.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE=n
CONFIG_PM_DEVICE_SYSTEM_MANAGED=y
CONFIG_ADC=y
#CONFIG_DEBUG=y
2 changes: 1 addition & 1 deletion samples/boards/stm32/power_mgmt/blinky/prj.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE=n
CONFIG_PM_DEVICE_SYSTEM_MANAGED=y
CONFIG_ASSERT=y
2 changes: 1 addition & 1 deletion samples/boards/stm32/power_mgmt/serial_wakeup/prj.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE=y
CONFIG_PM_DEVICE_SYSTEM_MANAGED=n

CONFIG_SHELL=y

Expand Down
2 changes: 1 addition & 1 deletion samples/boards/stm32/power_mgmt/suspend_to_ram/prj.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE=n
CONFIG_PM_DEVICE_SYSTEM_MANAGED=y
CONFIG_PM_S2RAM=y
CONFIG_ADC=y
CONFIG_ENTROPY_GENERATOR=y
Expand Down
1 change: 1 addition & 0 deletions subsys/pm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
26 changes: 19 additions & 7 deletions subsys/pm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non blocking: We could use more detailed information on states here.

Copy link
Member Author

@ceolin ceolin May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is hard to be specific without the risk of saying something that can be incomplete or wrong for a particular device :/


if PM_DEVICE

Expand Down Expand Up @@ -123,8 +122,9 @@ config PM_DEVICE_RUNTIME

config PM_DEVICE_RUNTIME_EXCLUSIVE
depends on PM_DEVICE_RUNTIME
bool "Use only on Runtime Power Management on system suspend / resume"
bool "[DEPRECATED] Use only on Runtime Power Management on system suspend / resume"
default y
select DEPRECATED
help
On system suspend / resume do not trigger the Device PM hooks but
only rely on Runtime PM to manage the devices power states.
Expand All @@ -136,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 changes: 89 additions & 0 deletions subsys/pm/device_system_managed.c
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions subsys/pm/device_system_managed.h
Original file line number Diff line number Diff line change
@@ -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) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ceolin, is this a bug? Why are the function definitions different in the #if and #else cases?


#endif /* CONFIG_PM_DEVICE_SYSTEM_MANAGED */

#endif /* ZEPHYR_SUBSYS_PM_DEVICE_SYSTEM_MANAGED_H_ */
Loading
Loading