subsys: power: Add OS managed Power Management framework
Add support for OS managed Power Management framework for Zephyr under 'subsys/power'. This framework takes care of implementing the _sys_soc_suspend/_sys_soc_resume API's, a PM policy based on SoC Low Power residencies and also provides necessary API's to do devices suspend and resume. Also add necessary changes to support the existing Application managed Power Management framework. Signed-off-by: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
This commit is contained in:
parent
1301cc636b
commit
2ad647857c
9 changed files with 496 additions and 0 deletions
|
@ -14,6 +14,29 @@ menuconfig SYS_POWER_MANAGEMENT
|
|||
timer is due to expire.
|
||||
|
||||
if SYS_POWER_MANAGEMENT
|
||||
choice POWER_MANAGEMENT_CONTROL
|
||||
prompt "Power Management Control"
|
||||
default PM_CONTROL_APP
|
||||
help
|
||||
Select the Application managed or OS managed power saving
|
||||
mechanism.
|
||||
|
||||
config PM_CONTROL_APP
|
||||
bool
|
||||
prompt "Handled at Application level"
|
||||
help
|
||||
This option enbles the Application to handle all the Power
|
||||
Management flows for the platform.
|
||||
|
||||
config PM_CONTROL_OS
|
||||
bool
|
||||
prompt "Handle at OS level"
|
||||
help
|
||||
This option allows the OS to handle all the Power
|
||||
Management flows for the platform.
|
||||
|
||||
endchoice # POWER_MANAGEMENT_CONTROL
|
||||
|
||||
config SYS_POWER_LOW_POWER_STATE
|
||||
bool "Low power state"
|
||||
depends on SYS_POWER_LOW_POWER_STATE_SUPPORTED
|
||||
|
|
|
@ -14,3 +14,4 @@ add_subdirectory_ifdef(CONFIG_USB usb)
|
|||
add_subdirectory(random)
|
||||
add_subdirectory(storage)
|
||||
add_subdirectory_ifdef(CONFIG_SETTINGS settings)
|
||||
add_subdirectory_ifdef(CONFIG_PM_CONTROL_OS power)
|
||||
|
|
|
@ -34,3 +34,5 @@ source "subsys/storage/Kconfig"
|
|||
source "subsys/settings/Kconfig"
|
||||
|
||||
source "subsys/app_memory/Kconfig"
|
||||
|
||||
source "subsys/power/Kconfig"
|
||||
|
|
5
subsys/power/CMakeLists.txt
Normal file
5
subsys/power/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
zephyr_sources(
|
||||
power.c
|
||||
policy.c
|
||||
device.c
|
||||
)
|
88
subsys/power/Kconfig
Normal file
88
subsys/power/Kconfig
Normal file
|
@ -0,0 +1,88 @@
|
|||
|
||||
if PM_CONTROL_OS
|
||||
menu "OS Power Management"
|
||||
|
||||
if SYS_POWER_LOW_POWER_STATE
|
||||
config PM_CONTROL_OS_LPS
|
||||
bool "Platform supports LPS"
|
||||
help
|
||||
Select this option if SoC support LPS state.
|
||||
|
||||
if PM_CONTROL_OS_LPS
|
||||
config PM_LPS_MIN_RES
|
||||
int "LPS minimum residency"
|
||||
default 5
|
||||
help
|
||||
Minimum residency in ticks to enter LPS state.
|
||||
endif
|
||||
|
||||
config PM_CONTROL_OS_LPS_1
|
||||
bool "Platform supports LPS_1"
|
||||
help
|
||||
Select this option if SoC support LPS_1 state.
|
||||
|
||||
if PM_CONTROL_OS_LPS_1
|
||||
config PM_LPS_1_MIN_RES
|
||||
int "LPS_1 minimum residency"
|
||||
default 10
|
||||
help
|
||||
Minimum residency in ticks to enter LPS_1 state.
|
||||
endif
|
||||
|
||||
config PM_CONTROL_OS_LPS_2
|
||||
bool "Platform supports LPS_2"
|
||||
help
|
||||
Select this option if SoC support LPS_2 state.
|
||||
|
||||
if PM_CONTROL_OS_LPS_2
|
||||
config PM_LPS_2_MIN_RES
|
||||
int "LPS_2 minimum residency"
|
||||
default 30
|
||||
help
|
||||
Minimum residency in ticks to enter LPS_2 state.
|
||||
endif
|
||||
endif # SYS_POWER_LOW_POWER_STATE
|
||||
|
||||
if SYS_POWER_DEEP_SLEEP
|
||||
config PM_CONTROL_OS_DEEP_SLEEP
|
||||
bool "Platform supports DEEP_SLEEP"
|
||||
help
|
||||
Select this option if SoC support DEEP_SLEEP state.
|
||||
|
||||
if PM_CONTROL_OS_DEEP_SLEEP
|
||||
config PM_DEEP_SLEEP_MIN_RES
|
||||
int "DEEP_SLEEP minimum residency"
|
||||
default 60
|
||||
help
|
||||
Minimum residency in ticks to enter DEEP_SLEEP state.
|
||||
endif
|
||||
|
||||
config PM_CONTROL_OS_DEEP_SLEEP_1
|
||||
bool "Platform supports DEEP_SLEEP_1"
|
||||
help
|
||||
Select this option if SoC support DEEP_SLEEP_1 state.
|
||||
|
||||
if PM_CONTROL_OS_DEEP_SLEEP_1
|
||||
config PM_DEEP_SLEEP_1_MIN_RES
|
||||
int "DEEP_SLEEP_1 minimum residency"
|
||||
default 90
|
||||
help
|
||||
Minimum residency in ticks to enter DEEP_SLEEP_1 state.
|
||||
endif
|
||||
|
||||
config PM_CONTROL_OS_DEEP_SLEEP_2
|
||||
bool "Platform supports DEEP_SLEEP_2"
|
||||
help
|
||||
Select this option if SoC support DEEP_SLEEP_2 state.
|
||||
|
||||
if PM_CONTROL_OS_DEEP_SLEEP_2
|
||||
config PM_DEEP_SLEEP_2_MIN_RES
|
||||
int "DEEP_SLEEP_2 minimum residency"
|
||||
default 120
|
||||
help
|
||||
Minimum residency in ticks to enter DEEP_SLEEP_2 state.
|
||||
endif
|
||||
endif # PM_CONTROL_OS_DEEP_SLEEP
|
||||
|
||||
endmenu
|
||||
endif # PM_CONTROL_OS
|
115
subsys/power/device.c
Normal file
115
subsys/power/device.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <kernel.h>
|
||||
#include <string.h>
|
||||
#include <soc.h>
|
||||
#include <device.h>
|
||||
#include "pm_policy.h"
|
||||
|
||||
#define LOG_MODULE_NAME power
|
||||
#define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE();
|
||||
|
||||
/*
|
||||
* FIXME: Remove the conditional inclusion of
|
||||
* core_devices array once we enble the capability
|
||||
* to build the device list based on devices power
|
||||
* and clock domain dependencies.
|
||||
*/
|
||||
#ifdef CONFIG_SOC_SERIES_NRF52X
|
||||
#define MAX_PM_DEVICES 15
|
||||
#define NUM_CORE_DEVICES 4
|
||||
#define MAX_DEV_NAME_LEN 16
|
||||
static const char core_devices[NUM_CORE_DEVICES][MAX_DEV_NAME_LEN] = {
|
||||
"clk_k32src",
|
||||
"clk_m16src",
|
||||
"sys_clock",
|
||||
"UART_0",
|
||||
};
|
||||
#else
|
||||
#error "Add SoC's core devices list for PM"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Ordered list to store devices on which
|
||||
* device power policies would be executed.
|
||||
*/
|
||||
static int device_ordered_list[MAX_PM_DEVICES];
|
||||
static int device_retval[MAX_PM_DEVICES];
|
||||
static struct device *pm_device_list;
|
||||
static int device_count;
|
||||
|
||||
int sys_pm_suspend_devices(void)
|
||||
{
|
||||
for (int i = device_count - 1; i >= 0; i--) {
|
||||
int idx = device_ordered_list[i];
|
||||
|
||||
/* TODO: Improve the logic by checking device status
|
||||
* and set the device states accordingly.
|
||||
*/
|
||||
device_retval[i] = device_set_power_state(&pm_device_list[idx],
|
||||
DEVICE_PM_SUSPEND_STATE);
|
||||
if (device_retval[i]) {
|
||||
LOG_ERR("%s suspend operation failed\n",
|
||||
pm_device_list[idx].config->name);
|
||||
return device_retval[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sys_pm_resume_devices(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < device_count; i++) {
|
||||
if (!device_retval[i]) {
|
||||
int idx = device_ordered_list[i];
|
||||
|
||||
device_set_power_state(&pm_device_list[idx],
|
||||
DEVICE_PM_ACTIVE_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sys_pm_create_device_list(void)
|
||||
{
|
||||
int count;
|
||||
int i, j;
|
||||
bool is_core_dev;
|
||||
|
||||
/*
|
||||
* Create an ordered list of devices that will be suspended.
|
||||
* Ordering should be done based on dependencies. Devices
|
||||
* in the beginning of the list will be resumed first.
|
||||
*/
|
||||
device_list_get(&pm_device_list, &count);
|
||||
|
||||
/* Reserve for 32KHz, 16MHz, system clock, etc... */
|
||||
device_count = NUM_CORE_DEVICES;
|
||||
|
||||
for (i = 0; (i < count) && (device_count < MAX_PM_DEVICES); i++) {
|
||||
|
||||
/* Check if the device is core device */
|
||||
for (j = 0, is_core_dev = false; j < NUM_CORE_DEVICES; j++) {
|
||||
if (!strcmp(pm_device_list[i].config->name,
|
||||
&core_devices[j][0])) {
|
||||
is_core_dev = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_core_dev) {
|
||||
device_ordered_list[j] = i;
|
||||
} else {
|
||||
device_ordered_list[device_count++] = i;
|
||||
}
|
||||
}
|
||||
}
|
57
subsys/power/pm_policy.h
Normal file
57
subsys/power/pm_policy.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _PM_POLICY_H_
|
||||
#define _PM_POLICY_H_
|
||||
|
||||
#include <power.h>
|
||||
#include <soc_power.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Function to create device PM list
|
||||
*/
|
||||
extern void sys_pm_create_device_list(void);
|
||||
|
||||
/**
|
||||
* @brief Function to suspend the devices in PM device list
|
||||
*/
|
||||
extern int sys_pm_suspend_devices(void);
|
||||
|
||||
/**
|
||||
* @brief Function to resume the devices in PM device list
|
||||
*/
|
||||
extern void sys_pm_resume_devices(void);
|
||||
|
||||
/**
|
||||
* @brief Function to get the next PM state based on the ticks
|
||||
*/
|
||||
extern int sys_pm_policy_next_state(s32_t ticks, enum power_states *state);
|
||||
|
||||
/**
|
||||
* @brief Application defined function for Lower Power entry
|
||||
*
|
||||
* Application defined function for doing any target specific operations
|
||||
* for low power entry.
|
||||
*/
|
||||
extern void sys_pm_notify_lps_entry(enum power_states state);
|
||||
|
||||
/**
|
||||
* @brief Application defined function for Lower Power exit
|
||||
*
|
||||
* Application defined function for doing any target specific operations
|
||||
* for low power exit.
|
||||
*/
|
||||
extern void sys_pm_notify_lps_exit(enum power_states state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _PM_POLICY_H_ */
|
98
subsys/power/policy.c
Normal file
98
subsys/power/policy.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <kernel.h>
|
||||
#include <soc.h>
|
||||
#include "pm_policy.h"
|
||||
|
||||
#define LOG_MODULE_NAME power
|
||||
#define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE();
|
||||
|
||||
#ifdef CONFIG_TICKLESS_KERNEL
|
||||
#define SECS_TO_TICKS CONFIG_TICKLESS_KERNEL_TIME_UNIT_IN_MICRO_SECS
|
||||
#else
|
||||
#define SECS_TO_TICKS CONFIG_SYS_CLOCK_TICKS_PER_SEC
|
||||
#endif
|
||||
|
||||
#if !(defined(CONFIG_PM_CONTROL_OS_LPS) || \
|
||||
defined(CONFIG_PM_CONTROL_OS_LPS_1) || \
|
||||
defined(CONFIG_PM_CONTROL_OS_LPS_2) || \
|
||||
defined(CONFIG_PM_CONTROL_OS_DEEP_SLEEP) || \
|
||||
defined(CONFIG_PM_CONTROL_OS_DEEP_SLEEP_1) || \
|
||||
defined(CONFIG_PM_CONTROL_OS_DEEP_SLEEP_2))
|
||||
#error "Low Power states not selected by policy"
|
||||
#endif
|
||||
|
||||
struct sys_soc_pm_policy {
|
||||
enum power_states pm_state;
|
||||
int sys_state;
|
||||
int min_residency;
|
||||
};
|
||||
|
||||
/* PM Policy based on SoC/Platform residency requirements */
|
||||
static struct sys_soc_pm_policy pm_policy[] = {
|
||||
#ifdef CONFIG_PM_CONTROL_OS_LPS
|
||||
{SYS_POWER_STATE_CPU_LPS, SYS_PM_LOW_POWER_STATE,
|
||||
CONFIG_PM_LPS_MIN_RES * SECS_TO_TICKS},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_CONTROL_OS_LPS_1
|
||||
{SYS_POWER_STATE_CPU_LPS_1, SYS_PM_LOW_POWER_STATE,
|
||||
CONFIG_PM_LPS_1_MIN_RES * SECS_TO_TICKS},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_CONTROL_OS_LPS_2
|
||||
{SYS_POWER_STATE_CPU_LPS_2, SYS_PM_LOW_POWER_STATE,
|
||||
CONFIG_PM_LPS_2_MIN_RES * SECS_TO_TICKS},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_CONTROL_OS_DEEP_SLEEP
|
||||
{SYS_POWER_STATE_DEEP_SLEEP, SYS_PM_DEEP_SLEEP,
|
||||
CONFIG_PM_DEEP_SLEEP_MIN_RES * SECS_TO_TICKS},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_CONTROL_OS_DEEP_SLEEP_1
|
||||
{SYS_POWER_STATE_DEEP_SLEEP_1, SYS_PM_DEEP_SLEEP,
|
||||
CONFIG_PM_DEEP_SLEEP_1_MIN_RES * SECS_TO_TICKS},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_CONTROL_OS_DEEP_SLEEP_2
|
||||
{SYS_POWER_STATE_DEEP_SLEEP_2, SYS_PM_DEEP_SLEEP,
|
||||
CONFIG_PM_DEEP_SLEEP_2_MIN_RES * SECS_TO_TICKS},
|
||||
#endif
|
||||
};
|
||||
|
||||
int sys_pm_policy_next_state(s32_t ticks, enum power_states *pm_state)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ((ticks != K_FOREVER) && (ticks < pm_policy[0].min_residency)) {
|
||||
LOG_ERR("Not enough time for PM operations: %d\n", ticks);
|
||||
return SYS_PM_NOT_HANDLED;
|
||||
}
|
||||
|
||||
for (i = 0; i < (ARRAY_SIZE(pm_policy) - 1); i++) {
|
||||
if ((ticks >= pm_policy[i].min_residency) &&
|
||||
(ticks < pm_policy[i + 1].min_residency)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_sys_soc_is_valid_power_state(pm_policy[i].pm_state)) {
|
||||
LOG_ERR("pm_state(%d) not supported by SoC\n",
|
||||
pm_policy[i].pm_state);
|
||||
return SYS_PM_NOT_HANDLED;
|
||||
}
|
||||
|
||||
*pm_state = pm_policy[i].pm_state;
|
||||
LOG_DBG("pm_state: %d, min_residency: %d, idx: %d\n",
|
||||
*pm_state, pm_policy[i].min_residency, i);
|
||||
|
||||
return pm_policy[i].sys_state;
|
||||
}
|
107
subsys/power/power.c
Normal file
107
subsys/power/power.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <kernel.h>
|
||||
#include <init.h>
|
||||
#include <string.h>
|
||||
#include <soc.h>
|
||||
#include "pm_policy.h"
|
||||
|
||||
#define LOG_MODULE_NAME power
|
||||
#define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER();
|
||||
|
||||
static int post_ops_done = 1;
|
||||
static enum power_states pm_state;
|
||||
|
||||
int _sys_soc_suspend(s32_t ticks)
|
||||
{
|
||||
int sys_state;
|
||||
|
||||
post_ops_done = 0;
|
||||
|
||||
sys_state = sys_pm_policy_next_state(ticks, &pm_state);
|
||||
|
||||
switch (sys_state) {
|
||||
case SYS_PM_LOW_POWER_STATE:
|
||||
sys_pm_notify_lps_entry(pm_state);
|
||||
/* Do CPU LPS operations */
|
||||
_sys_soc_set_power_state(pm_state);
|
||||
break;
|
||||
case SYS_PM_DEEP_SLEEP:
|
||||
/* Don't need pm idle exit event notification */
|
||||
_sys_soc_pm_idle_exit_notification_disable();
|
||||
|
||||
sys_pm_notify_lps_entry(pm_state);
|
||||
|
||||
/* Save device states and turn off peripherals as necessary */
|
||||
if (sys_pm_suspend_devices()) {
|
||||
LOG_ERR("System level device suspend failed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enter CPU deep sleep state */
|
||||
_sys_soc_set_power_state(pm_state);
|
||||
|
||||
/* Turn on peripherals and restore device states as necessary */
|
||||
sys_pm_resume_devices();
|
||||
break;
|
||||
default:
|
||||
/* No PM operations */
|
||||
LOG_DBG("\nNo PM operations done\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (sys_state != SYS_PM_NOT_HANDLED) {
|
||||
/*
|
||||
* Do any arch or soc specific post operations specific to the
|
||||
* power state.
|
||||
*/
|
||||
if (!post_ops_done) {
|
||||
post_ops_done = 1;
|
||||
sys_pm_notify_lps_exit(pm_state);
|
||||
_sys_soc_power_state_post_ops(pm_state);
|
||||
}
|
||||
}
|
||||
|
||||
return sys_state;
|
||||
}
|
||||
|
||||
void _sys_soc_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 _sys_soc_pm_idle_exit_notification_disable() if this
|
||||
* notification is not required.
|
||||
*/
|
||||
if (!post_ops_done) {
|
||||
post_ops_done = 1;
|
||||
sys_pm_notify_lps_exit(pm_state);
|
||||
_sys_soc_power_state_post_ops(pm_state);
|
||||
}
|
||||
}
|
||||
|
||||
static int sys_pm_init(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
sys_pm_create_device_list();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(sys_pm_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
Loading…
Add table
Add a link
Reference in a new issue