2018-08-16 10:39:40 +05:30
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Intel Corporation.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zephyr.h>
|
|
|
|
#include <kernel.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <device.h>
|
2018-09-17 13:16:47 +05:30
|
|
|
#include "policy/pm_policy.h"
|
2018-08-16 10:39:40 +05:30
|
|
|
|
2020-09-01 18:31:40 -04:00
|
|
|
#if defined(CONFIG_PM)
|
|
|
|
#define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */
|
2018-08-16 10:39:40 +05:30
|
|
|
#include <logging/log.h>
|
2018-08-09 08:48:18 -05:00
|
|
|
LOG_MODULE_DECLARE(power);
|
2018-08-16 10:39:40 +05:30
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-09-06 21:45:40 +08:00
|
|
|
|
|
|
|
__weak const char *const z_pm_core_devices[] = {
|
2019-11-26 11:59:32 +01:00
|
|
|
#if defined(CONFIG_SOC_FAMILY_NRF)
|
2020-03-14 11:01:38 -05:00
|
|
|
"CLOCK",
|
2018-08-16 10:39:40 +05:30
|
|
|
"sys_clock",
|
|
|
|
"UART_0",
|
2019-10-18 11:37:33 -07:00
|
|
|
#elif defined(CONFIG_SOC_SERIES_CC13X2_CC26X2)
|
|
|
|
"sys_clock",
|
|
|
|
"UART_0",
|
2020-03-02 10:13:13 +01:00
|
|
|
#elif defined(CONFIG_SOC_SERIES_KINETIS_K6X)
|
2020-03-31 09:46:02 -05:00
|
|
|
DT_LABEL(DT_INST(0, nxp_kinetis_ethernet)),
|
2020-05-19 19:45:00 +02:00
|
|
|
#elif defined(CONFIG_NET_TEST)
|
|
|
|
"",
|
2020-06-09 09:50:01 +02:00
|
|
|
#elif defined(CONFIG_SOC_SERIES_STM32L4X) || defined(CONFIG_SOC_SERIES_STM32WBX)
|
2020-05-27 10:41:06 +02:00
|
|
|
"sys_clock",
|
2018-08-16 10:39:40 +05:30
|
|
|
#endif
|
2020-09-06 21:45:40 +08:00
|
|
|
NULL
|
|
|
|
};
|
2018-08-16 10:39:40 +05:30
|
|
|
|
2020-03-14 15:51:31 -05:00
|
|
|
/* Ordinal of sufficient size to index available devices. */
|
2020-05-27 11:26:57 -05:00
|
|
|
typedef uint16_t device_idx_t;
|
2020-03-14 15:51:31 -05:00
|
|
|
|
|
|
|
/* The maximum value representable with a device_idx_t. */
|
|
|
|
#define DEVICE_IDX_MAX ((device_idx_t)(-1))
|
|
|
|
|
|
|
|
/* An array of all devices in the application. */
|
2020-04-30 20:33:38 +02:00
|
|
|
static const struct device *all_devices;
|
2020-03-14 15:51:31 -05:00
|
|
|
|
|
|
|
/* Indexes into all_devices for devices that support pm,
|
|
|
|
* in dependency order (later may depend on earlier).
|
2018-08-16 10:39:40 +05:30
|
|
|
*/
|
2020-09-06 21:45:40 +08:00
|
|
|
static device_idx_t pm_devices[CONFIG_PM_MAX_DEVICES];
|
2020-03-14 15:51:31 -05:00
|
|
|
|
|
|
|
/* Number of devices that support pm */
|
|
|
|
static device_idx_t num_pm;
|
|
|
|
|
|
|
|
/* Number of devices successfully suspended. */
|
|
|
|
static device_idx_t num_susp;
|
2018-08-16 10:39:40 +05:30
|
|
|
|
2020-09-01 21:46:30 -04:00
|
|
|
static int _pm_devices(uint32_t state)
|
2018-08-16 10:39:40 +05:30
|
|
|
{
|
2020-03-14 15:51:31 -05:00
|
|
|
num_susp = 0;
|
|
|
|
|
|
|
|
for (int i = num_pm - 1; i >= 0; i--) {
|
|
|
|
device_idx_t idx = pm_devices[i];
|
2020-04-30 20:33:38 +02:00
|
|
|
const struct device *dev = &all_devices[idx];
|
2020-03-14 15:51:31 -05:00
|
|
|
int rc;
|
2018-08-16 10:39:40 +05:30
|
|
|
|
|
|
|
/* TODO: Improve the logic by checking device status
|
|
|
|
* and set the device states accordingly.
|
|
|
|
*/
|
2020-03-14 15:51:31 -05:00
|
|
|
rc = device_set_power_state(dev, state, NULL, NULL);
|
2020-05-20 10:19:17 -05:00
|
|
|
if ((rc != -ENOTSUP) && (rc != 0)) {
|
2020-03-14 15:51:31 -05:00
|
|
|
LOG_DBG("%s did not enter %s state: %d",
|
2020-03-09 12:49:07 +01:00
|
|
|
dev->name, device_pm_state_str(state), rc);
|
2020-03-14 15:51:31 -05:00
|
|
|
return rc;
|
2018-08-16 10:39:40 +05:30
|
|
|
}
|
2020-03-14 15:51:31 -05:00
|
|
|
|
|
|
|
++num_susp;
|
2018-08-16 10:39:40 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-01 21:46:30 -04:00
|
|
|
int pm_suspend_devices(void)
|
2020-01-04 13:18:08 +08:00
|
|
|
{
|
2020-09-01 21:46:30 -04:00
|
|
|
return _pm_devices(DEVICE_PM_SUSPEND_STATE);
|
2020-01-21 22:53:48 +01:00
|
|
|
}
|
2020-01-04 13:18:08 +08:00
|
|
|
|
2020-09-01 21:46:30 -04:00
|
|
|
int pm_low_power_devices(void)
|
2020-01-21 22:53:48 +01:00
|
|
|
{
|
2020-09-01 21:46:30 -04:00
|
|
|
return _pm_devices(DEVICE_PM_LOW_POWER_STATE);
|
2020-01-04 13:18:08 +08:00
|
|
|
}
|
|
|
|
|
2020-09-01 21:46:30 -04:00
|
|
|
int pm_force_suspend_devices(void)
|
2018-10-09 11:08:39 +05:30
|
|
|
{
|
2020-09-01 21:46:30 -04:00
|
|
|
return _pm_devices(DEVICE_PM_FORCE_SUSPEND_STATE);
|
2018-10-09 11:08:39 +05:30
|
|
|
}
|
|
|
|
|
2020-09-01 21:46:30 -04:00
|
|
|
void pm_resume_devices(void)
|
2018-08-16 10:39:40 +05:30
|
|
|
{
|
2020-03-14 15:51:31 -05:00
|
|
|
device_idx_t pmi = num_pm - num_susp;
|
2018-08-16 10:39:40 +05:30
|
|
|
|
2020-03-14 15:51:31 -05:00
|
|
|
num_susp = 0;
|
|
|
|
while (pmi < num_pm) {
|
|
|
|
device_idx_t idx = pm_devices[pmi];
|
2018-08-16 10:39:40 +05:30
|
|
|
|
2020-03-14 15:51:31 -05:00
|
|
|
device_set_power_state(&all_devices[idx],
|
|
|
|
DEVICE_PM_ACTIVE_STATE,
|
|
|
|
NULL, NULL);
|
|
|
|
++pmi;
|
2018-08-16 10:39:40 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 21:46:30 -04:00
|
|
|
void pm_create_device_list(void)
|
2018-08-16 10:39:40 +05:30
|
|
|
{
|
2020-06-22 08:55:37 -05:00
|
|
|
size_t count = z_device_get_all_static(&all_devices);
|
2020-09-06 21:45:40 +08:00
|
|
|
device_idx_t pmi, core_dev;
|
2018-08-16 10:39:40 +05:30
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-03-14 15:51:31 -05:00
|
|
|
|
2020-06-22 08:55:37 -05:00
|
|
|
__ASSERT_NO_MSG(count <= DEVICE_IDX_MAX);
|
2018-08-16 10:39:40 +05:30
|
|
|
|
2020-03-14 15:51:31 -05:00
|
|
|
/* Reserve initial slots for core devices. */
|
2020-09-06 21:45:40 +08:00
|
|
|
core_dev = 0;
|
|
|
|
while (z_pm_core_devices[core_dev]) {
|
|
|
|
core_dev++;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_pm = core_dev;
|
|
|
|
__ASSERT_NO_MSG(num_pm <= CONFIG_PM_MAX_DEVICES);
|
2018-08-16 10:39:40 +05:30
|
|
|
|
2020-09-06 21:45:40 +08:00
|
|
|
for (pmi = 0; pmi < count; pmi++) {
|
2020-03-14 15:51:31 -05:00
|
|
|
device_idx_t cdi = 0;
|
|
|
|
const struct device *dev = &all_devices[pmi];
|
2018-08-16 10:39:40 +05:30
|
|
|
|
2020-03-14 15:51:31 -05:00
|
|
|
/* Ignore "device"s that don't support PM */
|
2020-11-05 05:43:02 -06:00
|
|
|
if ((dev->device_pm_control == NULL) ||
|
|
|
|
(dev->device_pm_control == device_pm_control_nop)) {
|
2020-03-14 15:51:31 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the device is a core device, which has a
|
|
|
|
* reserved slot.
|
|
|
|
*/
|
2020-09-06 21:45:40 +08:00
|
|
|
while (z_pm_core_devices[cdi]) {
|
|
|
|
if (strcmp(dev->name, z_pm_core_devices[cdi]) == 0) {
|
2020-03-14 15:51:31 -05:00
|
|
|
pm_devices[cdi] = pmi;
|
2018-08-16 10:39:40 +05:30
|
|
|
break;
|
|
|
|
}
|
2020-03-14 15:51:31 -05:00
|
|
|
++cdi;
|
2018-08-16 10:39:40 +05:30
|
|
|
}
|
|
|
|
|
2020-03-14 15:51:31 -05:00
|
|
|
/* Append the device if it doesn't have a reserved slot. */
|
2020-09-06 21:45:40 +08:00
|
|
|
if (cdi == core_dev) {
|
2020-03-14 15:51:31 -05:00
|
|
|
pm_devices[num_pm++] = pmi;
|
2018-08-16 10:39:40 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-01 18:31:40 -04:00
|
|
|
#endif /* defined(CONFIG_PM) */
|
2021-01-25 15:08:48 +01:00
|
|
|
|
|
|
|
const char *device_pm_state_str(uint32_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 "";
|
|
|
|
}
|
|
|
|
}
|