/* * Copyright (c) 2018 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include "policy/pm_policy.h" #if defined(CONFIG_SYS_POWER_MANAGEMENT) #define LOG_LEVEL CONFIG_SYS_PM_LOG_LEVEL /* From power module Kconfig */ #include LOG_MODULE_DECLARE(power); /* * 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. */ #if defined(CONFIG_SOC_FAMILY_NRF) #define MAX_PM_DEVICES 15 #define MAX_DEV_NAME_LEN 16 static const char core_devices[][MAX_DEV_NAME_LEN] = { "CLOCK_32K", "CLOCK_16M", "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; const char *device_pm_state_str(u32_t state) { switch (state) { case DEVICE_PM_ACTIVE_STATE: return "active"; case DEVICE_PM_LOW_POWER_STATE: return "low power"; case DEVICE_PM_SUSPEND_STATE: return "suspend"; case DEVICE_PM_FORCE_SUSPEND_STATE: return "force suspend"; case DEVICE_PM_OFF_STATE: return "off"; default: return ""; } } static int _sys_pm_devices(u32_t state) { 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], state, NULL, NULL); if (device_retval[i]) { LOG_DBG("%s did not enter %s state", pm_device_list[idx].config->name, device_pm_state_str(state)); return device_retval[i]; } } return 0; } int sys_pm_suspend_devices(void) { return _sys_pm_devices(DEVICE_PM_SUSPEND_STATE); } int sys_pm_low_power_devices(void) { return _sys_pm_devices(DEVICE_PM_LOW_POWER_STATE); } int sys_pm_force_suspend_devices(void) { return _sys_pm_devices(DEVICE_PM_FORCE_SUSPEND_STATE); } 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, NULL, NULL); } } } 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 = ARRAY_SIZE(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 < ARRAY_SIZE(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; } } } #endif /* defined(CONFIG_SYS_POWER_MANAGEMENT) */