/* * Copyright (c) 2018 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include "policy/pm_policy.h" #if defined(CONFIG_PM) #define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */ #include LOG_MODULE_DECLARE(power); extern const struct device __device_start[]; extern const struct device __device_end[]; /* Indexes into all_devices for devices that support pm, * in dependency order (later may depend on earlier). */ static const struct device *pm_devices[CONFIG_PM_MAX_DEVICES]; /* Number of devices successfully suspended. */ static size_t num_susp; static bool should_suspend(const struct device *dev, uint32_t state) { int rc; uint32_t current_state; if (device_busy_check(dev) != 0) { return false; } rc = pm_device_state_get(dev, ¤t_state); if ((rc == -ENOSYS) || (rc != 0)) { LOG_DBG("Was not possible to get device %s state: %d", dev->name, rc); return false; } /* * If the device is currently powered off or the request was * to go to the same state, just ignore it. */ if ((current_state == PM_DEVICE_STATE_OFF) || (current_state == state)) { return false; } return true; } static int _pm_devices(uint32_t state) { const struct device *dev; num_susp = 0; for (dev = (__device_end - 1); dev > __device_start; dev--) { bool suspend; int rc; suspend = should_suspend(dev, state); if (suspend) { /* * Don't bother the device if it is currently * in the right state. */ rc = pm_device_state_set(dev, state, NULL, NULL); if ((rc != -ENOSYS) && (rc != 0)) { LOG_DBG("%s did not enter %s state: %d", dev->name, pm_device_state_str(state), rc); return rc; } pm_devices[num_susp] = dev; num_susp++; __ASSERT(num_susp < CONFIG_PM_MAX_DEVICES, "Number of pm devices > CONFIG_PM_MAX_DEVICES"); } } return 0; } int pm_suspend_devices(void) { return _pm_devices(PM_DEVICE_STATE_SUSPEND); } int pm_low_power_devices(void) { return _pm_devices(PM_DEVICE_STATE_LOW_POWER); } int pm_force_suspend_devices(void) { return _pm_devices(PM_DEVICE_STATE_FORCE_SUSPEND); } void pm_resume_devices(void) { size_t i; for (i = 0; i < num_susp; i++) { pm_device_state_set(pm_devices[i], PM_DEVICE_STATE_ACTIVE, NULL, NULL); } num_susp = 0; } #endif /* defined(CONFIG_PM) */ const char *pm_device_state_str(uint32_t state) { switch (state) { case PM_DEVICE_STATE_ACTIVE: return "active"; case PM_DEVICE_STATE_LOW_POWER: return "low power"; case PM_DEVICE_STATE_SUSPEND: return "suspend"; case PM_DEVICE_STATE_FORCE_SUSPEND: return "force suspend"; case PM_DEVICE_STATE_OFF: return "off"; default: return ""; } } int pm_device_state_set(const struct device *dev, uint32_t device_power_state, pm_device_cb cb, void *arg) { if (dev->pm_control == NULL) { return -ENOSYS; } return dev->pm_control(dev, PM_DEVICE_STATE_SET, &device_power_state, cb, arg); } int pm_device_state_get(const struct device *dev, uint32_t *device_power_state) { if (dev->pm_control == NULL) { return -ENOSYS; } return dev->pm_control(dev, PM_DEVICE_STATE_GET, device_power_state, NULL, NULL); }