pm: Add power domain infra structure

Add support for power domains on Zephyr. Power domains are implemented
as simple devices so they can use the existent Zephyr API, for resume
and suspend sync and async and also reference count.

The pm subsystem will ensure that domains are resumed before and
suspended after devices using them. For device runtime power
management, every time the device is got or released the same actions
is done to the domain it belongs.

As domains are implemented as simple devices, it is totally acceptable
a domain belongs to another domain.

Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
This commit is contained in:
Flavio Ceolin 2021-10-01 14:07:28 -07:00 committed by Anas Nashif
commit ddfa048058
4 changed files with 47 additions and 0 deletions

View file

@ -16,3 +16,13 @@ properties:
Wake up capable devices are disabled (interruptions will not wake up
the system) by default but they can be enabled at runtime if necessary.
power-domain:
required: false
type: phandle
description: |
Power domain the device belongs to.
The device will be notified when the power domain it belongs to is either
suspended or resumed.

View file

@ -0,0 +1,8 @@
# Copyright (c) 2021, Intel Corporation
# SPDX-License-Identifier: Apache-2.0
description: Properties for power domains
compatible: "power-domain"
include: base.yaml

View file

@ -116,6 +116,8 @@ struct pm_device {
enum pm_device_state state;
/** Device PM action callback */
pm_device_action_cb_t action_cb;
/** Power Domain it belongs */
const struct device *domain;
};
#ifdef CONFIG_PM_DEVICE_RUNTIME
@ -145,6 +147,7 @@ struct pm_device {
DT_NODE_EXISTS(node_id), \
(DT_PROP_OR(node_id, wakeup_source, 0)),\
(0)) << PM_DEVICE_FLAG_WS_CAPABLE), \
.domain = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(node_id, power_domain))\
}
/**

View file

@ -95,6 +95,14 @@ static void runtime_suspend_work(struct k_work *work)
k_condvar_broadcast(&pm->condvar);
k_mutex_unlock(&pm->lock);
/*
* On async put, we have to suspend the domain when the device
* finishes its operation
*/
if (pm->domain != NULL) {
(void)pm_device_runtime_put(pm->domain);
}
__ASSERT(ret == 0, "Could not suspend device (%d)", ret);
}
@ -113,6 +121,17 @@ int pm_device_runtime_get(const struct device *dev)
goto unlock;
}
/*
* If the device is under a power domain, the domain has to be get
* first.
*/
if (pm->domain != NULL) {
ret = pm_device_runtime_get(pm->domain);
if (ret != 0) {
goto unlock;
}
}
pm->usage++;
if (!k_is_pre_kernel()) {
@ -150,6 +169,13 @@ int pm_device_runtime_put(const struct device *dev)
SYS_PORT_TRACING_FUNC_ENTER(pm, device_runtime_put, dev);
ret = runtime_suspend(dev, false);
/*
* Now put the domain
*/
if ((ret == 0) && dev->pm->domain != NULL) {
ret = pm_device_runtime_put(dev->pm->domain);
}
SYS_PORT_TRACING_FUNC_EXIT(pm, device_runtime_put, dev, ret);
return ret;