diff --git a/arch/arm/soc/nordic_nrf/nrf52/power.c b/arch/arm/soc/nordic_nrf/nrf52/power.c index a43a030c5780..fb8bb3c022e2 100644 --- a/arch/arm/soc/nordic_nrf/nrf52/power.c +++ b/arch/arm/soc/nordic_nrf/nrf52/power.c @@ -100,3 +100,21 @@ void _sys_soc_power_state_post_ops(enum power_states state) break; } } + +bool _sys_soc_is_valid_power_state(enum power_states state) +{ + switch (state) { + case SYS_POWER_STATE_CPU_LPS: + case SYS_POWER_STATE_CPU_LPS_1: +#if defined(CONFIG_SYS_POWER_DEEP_SLEEP) + case SYS_POWER_STATE_DEEP_SLEEP: +#endif + return true; + break; + default: + SYS_LOG_DBG("Unsupported State\n"); + break; + } + + return false; +} diff --git a/arch/arm/soc/nordic_nrf/nrf52/soc_power.h b/arch/arm/soc/nordic_nrf/nrf52/soc_power.h index 701104f73848..74cc9cb115a4 100644 --- a/arch/arm/soc/nordic_nrf/nrf52/soc_power.h +++ b/arch/arm/soc/nordic_nrf/nrf52/soc_power.h @@ -27,6 +27,11 @@ enum power_states { */ void _sys_soc_set_power_state(enum power_states state); +/** + * @brief Check a low power state is supported by SoC + */ +bool _sys_soc_is_valid_power_state(enum power_states state); + /** * @brief Do any SoC or architecture specific post ops after low power states. */ diff --git a/kernel/Kconfig.power_mgmt b/kernel/Kconfig.power_mgmt index 73baa7be807f..8e6c15e54834 100644 --- a/kernel/Kconfig.power_mgmt +++ b/kernel/Kconfig.power_mgmt @@ -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 diff --git a/samples/subsys/power/power.rst b/samples/subsys/power/power.rst new file mode 100644 index 000000000000..8e2c53ba882c --- /dev/null +++ b/samples/subsys/power/power.rst @@ -0,0 +1,10 @@ +.. _power_management-samples: + +Power Management Samples +######################## + +.. toctree:: + :maxdepth: 1 + :glob: + + **/* diff --git a/samples/subsys/power/power_mgr/CMakeLists.txt b/samples/subsys/power/power_mgr/CMakeLists.txt new file mode 100644 index 000000000000..800ba5841af3 --- /dev/null +++ b/samples/subsys/power/power_mgr/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.8.2) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/subsys/power/power_mgr/README.rst b/samples/subsys/power/power_mgr/README.rst new file mode 100644 index 000000000000..17e5bc64c933 --- /dev/null +++ b/samples/subsys/power/power_mgr/README.rst @@ -0,0 +1,79 @@ +.. _os-power-mgr-sample: + +OS Power management demo +########################### + +Overview +******** + +This sample demonstrates OS managed power saving mechanism through the sample +application which will periodically go sleep there by invoking the idle thread +which will call the _sys_soc_suspend() to enter into low power states. The Low +Power state will be selected based on the next timeout event. + +Requirements +************ + +This application uses nrf52 DK board for the demo. + +Building, Flashing and Running +****************************** + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/power/power_mgr + :board: nrf52_pca10040 + :goals: build flash + :compact: + +Running: + +1. Open UART terminal. +2. Power Cycle Device. +3. Device will enter into Low Power Modes periodically. + + +Sample Output +================= +nrf52 core output +----------------- + +.. code-block:: console + + ***OS Power Management Demo on arm**** + Demo Description + Application creates Idleness, Due to which System Idle Thread is + scheduled and it enters into various Low Power States. + + <-- App doing busy wait for 10 Sec --> + + <-- App going to sleep for 6000 msec --> + Entering Low Power state (0) + Entering Low Power state (0) + Entering Low Power state (0) + Entering Low Power state (0) + + <-- App doing busy wait for 10 Sec --> + + <-- App going to sleep for 11000 msec --> + Entering Low Power state (1) + Entering Low Power state (1) + Entering Low Power state (1) + Entering Low Power state (1) + + <-- App doing busy wait for 10 Sec --> + + <-- App going to sleep for 6000 msec --> + Entering Low Power state (0) + Entering Low Power state (0) + Entering Low Power state (0) + Entering Low Power state (0) + + <-- App doing busy wait for 10 Sec --> + + <-- App going to sleep for 11000 msec --> + Entering Low Power state (1) + Entering Low Power state (1) + Entering Low Power state (1) + Entering Low Power state (1) + OS managed Power Management Test completed + diff --git a/samples/subsys/power/power_mgr/prj.conf b/samples/subsys/power/power_mgr/prj.conf new file mode 100644 index 000000000000..f1e799e11bdc --- /dev/null +++ b/samples/subsys/power/power_mgr/prj.conf @@ -0,0 +1,13 @@ +CONFIG_SYS_POWER_MANAGEMENT=y +CONFIG_SYS_POWER_LOW_POWER_STATE=y +CONFIG_SYS_POWER_DEEP_SLEEP=y +CONFIG_DEVICE_POWER_MANAGEMENT=y +CONFIG_TICKLESS_IDLE=y + +CONFIG_PM_CONTROL_OS=y +CONFIG_PM_CONTROL_OS_LPS=y +CONFIG_PM_LPS_MIN_RES=5 +CONFIG_PM_CONTROL_OS_LPS_1=y +CONFIG_PM_LPS_1_MIN_RES=10 +CONFIG_PM_CONTROL_OS_DEEP_SLEEP=y +CONFIG_PM_DEEP_SLEEP_MIN_RES=60 diff --git a/samples/subsys/power/power_mgr/prj_tickless.conf b/samples/subsys/power/power_mgr/prj_tickless.conf new file mode 100644 index 000000000000..fa8e0f4dc114 --- /dev/null +++ b/samples/subsys/power/power_mgr/prj_tickless.conf @@ -0,0 +1,16 @@ +CONFIG_NUM_COOP_PRIORITIES=29 +CONFIG_NUM_PREEMPT_PRIORITIES=40 +CONFIG_SYS_POWER_MANAGEMENT=y +CONFIG_SYS_POWER_LOW_POWER_STATE=y +CONFIG_SYS_POWER_DEEP_SLEEP=y +CONFIG_DEVICE_POWER_MANAGEMENT=y +CONFIG_TICKLESS_KERNEL=y +CONFIG_TICKLESS_IDLE=y + +CONFIG_PM_CONTROL_OS=y +CONFIG_PM_CONTROL_OS_LPS=y +CONFIG_PM_LPS_MIN_RES=5 +CONFIG_PM_CONTROL_OS_LPS_1=y +CONFIG_PM_LPS_1_MIN_RES=10 +CONFIG_PM_CONTROL_OS_DEEP_SLEEP=y +CONFIG_PM_DEEP_SLEEP_MIN_RES=60 diff --git a/samples/subsys/power/power_mgr/sample.yaml b/samples/subsys/power/power_mgr/sample.yaml new file mode 100644 index 000000000000..a665dfdfc775 --- /dev/null +++ b/samples/subsys/power/power_mgr/sample.yaml @@ -0,0 +1,6 @@ +sample: + name: OS managed Power Management Sample +tests: + ospm.low_power_state: + platform_whitelist: nrf52840_pca10056 nrf52_pca10040 + tags: power diff --git a/samples/subsys/power/power_mgr/src/main.c b/samples/subsys/power/power_mgr/src/main.c new file mode 100644 index 000000000000..2765a424af13 --- /dev/null +++ b/samples/subsys/power/power_mgr/src/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SECONDS_TO_SLEEP 1 + +#define BUSY_WAIT_DELAY_US (10 * 1000000) + +#define LPS_STATE_ENTER_TO ((CONFIG_PM_LPS_MIN_RES + 1) * 1000) +#define LPS1_STATE_ENTER_TO ((CONFIG_PM_LPS_1_MIN_RES + 1) * 1000) + +#define DEMO_DESCRIPTION \ + "Demo Description\n" \ + "Application creates Idleness, Due to which System Idle Thread is\n"\ + "scheduled and it enters into various Low Power States.\n"\ + +void sys_pm_notify_lps_entry(enum power_states state) +{ + printk("Entering Low Power state (%d)\n", state); +} + +void sys_pm_notify_lps_exit(enum power_states state) +{ + printk("Entering Low Power state (%d)\n", state); +} + +/* Application main Thread */ +void main(void) +{ + printk("\n\n***OS Power Management Demo on %s****\n", CONFIG_ARCH); + printk(DEMO_DESCRIPTION); + + for (int i = 1; i <= 4; i++) { + printk("\n<-- App doing busy wait for 10 Sec -->\n"); + k_busy_wait(BUSY_WAIT_DELAY_US); + + /* Create Idleness to make Idle thread run */ + if ((i % 2) == 0) { + printk("\n<-- App going to sleep for %d msec -->\n", + LPS1_STATE_ENTER_TO); + k_sleep(LPS1_STATE_ENTER_TO); + } else { + printk("\n<-- App going to sleep for %d msec -->\n", + LPS_STATE_ENTER_TO); + k_sleep(LPS_STATE_ENTER_TO); + } + } + + printk("OS managed Power Management Test completed\n"); +} diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index 3e71bac6f69e..50b74b669752 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -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) diff --git a/subsys/Kconfig b/subsys/Kconfig index 0630d7d67518..f3deb779855f 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -34,3 +34,5 @@ source "subsys/storage/Kconfig" source "subsys/settings/Kconfig" source "subsys/app_memory/Kconfig" + +source "subsys/power/Kconfig" diff --git a/subsys/power/CMakeLists.txt b/subsys/power/CMakeLists.txt new file mode 100644 index 000000000000..fc6ed85e682b --- /dev/null +++ b/subsys/power/CMakeLists.txt @@ -0,0 +1,5 @@ +zephyr_sources( + power.c + policy.c + device.c + ) diff --git a/subsys/power/Kconfig b/subsys/power/Kconfig new file mode 100644 index 000000000000..d88499e5b2a1 --- /dev/null +++ b/subsys/power/Kconfig @@ -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 diff --git a/subsys/power/device.c b/subsys/power/device.c new file mode 100644 index 000000000000..2f4111835442 --- /dev/null +++ b/subsys/power/device.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "pm_policy.h" + +#define LOG_MODULE_NAME power +#define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */ +#include +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; + } + } +} diff --git a/subsys/power/pm_policy.h b/subsys/power/pm_policy.h new file mode 100644 index 000000000000..0fb5a99d039c --- /dev/null +++ b/subsys/power/pm_policy.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _PM_POLICY_H_ +#define _PM_POLICY_H_ + +#include +#include + +#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_ */ diff --git a/subsys/power/policy.c b/subsys/power/policy.c new file mode 100644 index 000000000000..ef204e1dde51 --- /dev/null +++ b/subsys/power/policy.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "pm_policy.h" + +#define LOG_MODULE_NAME power +#define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */ +#include +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; +} diff --git a/subsys/power/power.c b/subsys/power/power.c new file mode 100644 index 000000000000..fa039836a889 --- /dev/null +++ b/subsys/power/power.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "pm_policy.h" + +#define LOG_MODULE_NAME power +#define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */ +#include +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);