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

power: Documentation updates and general fixes #33222

Merged
merged 9 commits into from
Mar 22, 2021
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
49 changes: 41 additions & 8 deletions doc/reference/power_management/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,27 @@ have higher wake latencies. Following is a thorough list of available states:
.. doxygenenumvalue:: PM_STATE_SOFT_OFF
:project: Zephyr

.. _pm_constraints:

Power States Constraint
=======================

The power management subsystem allows different Zephyr components and
applications to set constraints on various power states preventing the
system to go these states. This can be used by devices when executing
tasks in background to avoid the system to go to state where it would
lose context. Constraints can be set, released and checked using the
follow APIs:
erwango marked this conversation as resolved.
Show resolved Hide resolved

.. doxygenfunction:: pm_constraint_set
:project: Zephyr

.. doxygenfunction:: pm_constraint_release
:project: Zephyr

.. doxygenfunction:: pm_constraint_get
:project: Zephyr

erwango marked this conversation as resolved.
Show resolved Hide resolved
Power Management Policies
=========================

Expand All @@ -102,12 +123,20 @@ The power management subsystem supports the following power management policies:
* Application
* Dummy

The policy manager is responsible to inform the power subsystem which
power state the system should go based on states available in the
platform and possible runtime :ref:`constraints<pm_constraints>`

Information about states can be get from device tree, see
:zephyr_file:`dts/bindings/power/state.yaml`.

Residency
---------

The power management system enters the power state which offers the highest
power savings, and with a minimum residency value (defined by the respective
Kconfig option) less than or equal to the scheduled system idle time duration.
power savings, and with a minimum residency value (in device tree, see
:zephyr_file:`dts/bindings/power/state.yaml`) less than or equal to
the scheduled system idle time duration.

Application
-----------
Expand All @@ -119,6 +148,10 @@ the following function.

struct pm_state_info pm_policy_next_state(int32_t ticks);

In this policy the application is free to decide which power state the
system should go based on the remaining time for the next scheduled
timeout.

Dummy
-----

Expand Down Expand Up @@ -147,7 +180,7 @@ in power saving mode. This method allows saving power even when the CPU is
active. The components that use the devices need to be power aware and should
be able to make decisions related to managing device power. In this method, the
SOC interface can enter CPU or SOC power states quickly when
:code:`sys_suspend()` gets called. This is because it does not need to
:code:`pm_system_suspend()` gets called. This is because it does not need to
spend time doing device power management if the devices are already put in
the appropriate power state by the application or component managing the
devices.
Expand All @@ -156,7 +189,7 @@ Central method
==============

In this method device power management is mostly done inside
:code:`sys_suspend()` along with entering a CPU or SOC power state.
:code:`pm_system_suspend()` along with entering a CPU or SOC power state.

If a decision to enter deep sleep is made, the implementation would enter it
only after checking if the devices are not in the middle of a hardware
Expand Down Expand Up @@ -299,21 +332,21 @@ off, then such transactions would be left in an inconsistent state. This
infrastructure guards such transactions by indicating to the SOC interface that
the device is in the middle of a hardware transaction.

When the :code:`sys_suspend()` is called, the SOC interface checks if any device
When the :code:`pm_system_suspend()` is called, the SOC interface checks if any device
is busy. The SOC interface can then decide to execute a power management scheme other than deep sleep or
to defer power management operations until the next call of
:code:`sys_suspend()`.
:code:`pm_system_suspend()`.

An alternative to using the busy status mechanism is to use the
`distributed method`_ of device power management. In such a method where the
device power management is handled in a distributed manner rather than centrally in
:code:`sys_suspend()`, the decision to enter deep sleep can be made based
:code:`pm_system_suspend()`, the decision to enter deep sleep can be made based
on whether all devices are already turned off.

This feature can be also used to emulate a hardware feature found in some SOCs
that causes the system to automatically enter deep sleep when all devices are idle.
In such an usage, the busy status can be set by default and cleared as each
device becomes idle. When :code:`sys_suspend()` is called, deep sleep can
device becomes idle. When :code:`pm_system_suspend()` is called, deep sleep can
be entered if no device is found to be busy.

Here are the APIs used to set, clear, and check the busy status of devices.
Expand Down
2 changes: 1 addition & 1 deletion drivers/i2c/i2c_dw.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ static int i2c_dw_transfer(const struct device *dev,

/*
* While waiting at device_sync_sem, kernel can switch to idle
* task which in turn can call sys_suspend() hook of Power
* task which in turn can call pm_system_suspend() hook of Power
* Management App (PMA).
* device_busy_set() call here, would indicate to PMA that it should not
* execute PM policies that would turn off this ip block, causing an
Expand Down
87 changes: 50 additions & 37 deletions include/power/power.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ extern "C" {

#ifdef CONFIG_PM

extern unsigned char pm_idle_exit_notify;

/**
* @brief System Power Management API
*
Expand All @@ -48,6 +46,10 @@ extern unsigned char pm_idle_exit_notify;
*
* @note These callbacks can be called from the ISR of the event
* that caused the kernel exit from idling.
*
* @note It is not allowed to call @ref pm_notifier_unregister or
* @ref pm_notifier_register from these callbacks because they are called
* with the spin locked in those functions.
*/
struct pm_notifier {
sys_snode_t _node;
Expand Down Expand Up @@ -76,17 +78,6 @@ struct pm_notifier {
*/
void pm_power_state_force(struct pm_state_info info);

/**
* @brief Put processor into a power state.
*
* This function implements the SoC specific details necessary
* to put the processor into available power states.
*
* @param info Power state which should be used in the ongoing
* suspend operation.
*/
void pm_power_state_set(struct pm_state_info info);

#ifdef CONFIG_PM_DEBUG
/**
* @brief Dump Low Power states related debug info
Expand All @@ -97,6 +88,41 @@ void pm_dump_debug_info(void);

#endif /* CONFIG_PM_DEBUG */

/**
* @brief Register a power management notifier
*
* Register the given notifier from the power management notification
* list.
*
* @param notifier pm_notifier object to be registered.
*/
void pm_notifier_register(struct pm_notifier *notifier);

/**
* @brief Unregister a power management notifier
*
* Remove the given notifier from the power management notification
* list. After that this object callbacks will not be called.
*
* @param notifier pm_notifier object to be unregistered.
*
* @return 0 if the notifier was successfully removed, a negative value
* otherwise.
*/
int pm_notifier_unregister(struct pm_notifier *notifier);

/**
* @}
*/

/**
* @brief System Power Management Constraint API
*
* @defgroup system_power_management_constraint_api Constraint API
* @ingroup power_management_api
* @{
*/

/**
* @brief Set a constraint for a power state
*
Expand Down Expand Up @@ -135,7 +161,6 @@ void pm_constraint_release(enum pm_state state);
*/
bool pm_constraint_get(enum pm_state state);


/**
* @}
*/
Expand All @@ -148,6 +173,17 @@ bool pm_constraint_get(enum pm_state state);
* @{
*/

/**
* @brief Put processor into a power state.
*
* This function implements the SoC specific details necessary
* to put the processor into available power states.
*
* @param info Power state which should be used in the ongoing
* suspend operation.
*/
void pm_power_state_set(struct pm_state_info info);

/**
* @brief Do any SoC or architecture specific post ops after sleep state exits.
*
Expand All @@ -158,29 +194,6 @@ bool pm_constraint_get(enum pm_state state);
*/
void pm_power_state_exit_post_ops(struct pm_state_info info);

/**
* @brief Register a power management notifier
*
* Register the given notifier from the power management notification
* list.
*
* @param notifier pm_notifier object to be registered.
*/
void pm_notifier_register(struct pm_notifier *notifier);

/**
* @brief Unregister a power management notifier
*
* Remove the given notifier from the power management notification
* list. After that this object callbacks will not be called.
*
* @param notifier pm_notifier object to be unregistered.
*
* @return 0 if the notifier was successfully removed, a negative value
* otherwise.
*/
int pm_notifier_unregister(struct pm_notifier *notifier);

/**
* @}
*/
Expand Down
77 changes: 42 additions & 35 deletions subsys/power/power.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ __weak void pm_power_state_exit_post_ops(struct pm_state_info info)
/*
* This function is supposed to be overridden to do SoC or
* architecture specific post ops after sleep state exits.
*
* The kernel expects that irqs are unlocked after this.
*/

irq_unlock(0);
erwango marked this conversation as resolved.
Show resolved Hide resolved
}

__weak void pm_power_state_set(struct pm_state_info info)
Expand Down Expand Up @@ -108,6 +112,31 @@ static inline void pm_state_notify(bool entering_state)
k_spin_unlock(&pm_notifier_lock, pm_notifier_key);
}

void pm_system_resume(void)
{
/*
* This notification is called from the ISR of the event
* that caused exit from kernel idling after PM operations.
*
* Some CPU low power states require enabling of interrupts
* atomically when entering those states. The wake up from
* such a state first executes code in the ISR of the interrupt
* that caused the wake. This hook will be called from the ISR.
* For such CPU LPS states, do post operations and restores here.
* The kernel scheduler will get control after the ISR finishes
* and it may schedule another thread.
*
* Call pm_idle_exit_notification_disable() if this
* notification is not required.
*/
if (!post_ops_done) {
post_ops_done = 1;
pm_power_state_exit_post_ops(z_power_state);
pm_state_notify(false);
k_sched_unlock();
}
}

void pm_power_state_force(struct pm_state_info info)
{
__ASSERT(info.state < PM_STATES_LEN,
Expand All @@ -122,16 +151,13 @@ void pm_power_state_force(struct pm_state_info info)
post_ops_done = 0;
pm_state_notify(true);

k_sched_lock();
pm_debug_start_timer();
/* Enter power state */
pm_power_state_set(z_power_state);
pm_debug_stop_timer();

if (!post_ops_done) {
post_ops_done = 1;
pm_state_notify(false);
pm_power_state_exit_post_ops(z_power_state);
}
pm_system_resume();
}

#if CONFIG_PM_DEVICE
Expand Down Expand Up @@ -180,6 +206,16 @@ enum pm_state pm_system_suspend(int32_t ticks)
break;
}
#endif
/*
* This function runs with interruptions locked but it is
* expected the SoC to unlock them in
* pm_power_state_exit_post_ops() when returning to active
* state. We don't want to be scheduled out yet, first we need
* to send a notification about leaving the idle state. So,
* we lock the scheduler here and unlock just after we have
* sent the notification in pm_system_resume().
*/
k_sched_lock();
pm_debug_start_timer();
/* Enter power state */
pm_state_notify(true);
Expand All @@ -194,40 +230,11 @@ enum pm_state pm_system_suspend(int32_t ticks)
}
#endif
pm_log_debug_info(z_power_state.state);

if (!post_ops_done) {
post_ops_done = 1;
pm_state_notify(false);
pm_power_state_exit_post_ops(z_power_state);
}
pm_system_resume();

return z_power_state.state;
}

void pm_system_resume(void)
{
/*
* This notification is called from the ISR of the event
* that caused exit from kernel idling after PM operations.
*
* Some CPU low power states require enabling of interrupts
* atomically when entering those states. The wake up from
* such a state first executes code in the ISR of the interrupt
* that caused the wake. This hook will be called from the ISR.
* For such CPU LPS states, do post operations and restores here.
* The kernel scheduler will get control after the ISR finishes
* and it may schedule another thread.
*
* Call pm_idle_exit_notification_disable() if this
* notification is not required.
*/
if (!post_ops_done) {
post_ops_done = 1;
pm_state_notify(false);
pm_power_state_exit_post_ops(z_power_state);
}
}

void pm_notifier_register(struct pm_notifier *notifier)
{
k_spinlock_key_t pm_notifier_key = k_spin_lock(&pm_notifier_lock);
Expand Down