pm: device: Dynamically add a device to a power domain
Add API to add devices to a power domain in runtime. The number of devices that can be added is defined in build time. The script gen_handles.py will check the number defined in `CONFIG_PM_DEVICE_POWER_DOMAIN_DYNAMIC` to resize the handles vector, adding empty slots in the supported sector to be used later. Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
This commit is contained in:
parent
b2d3fdceff
commit
0b13b44a66
9 changed files with 183 additions and 3 deletions
|
@ -840,6 +840,12 @@ zephyr_get_include_directories_for_lang(C
|
||||||
STRIP_PREFIX # Don't use a -I prefix
|
STRIP_PREFIX # Don't use a -I prefix
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(CONFIG_PM_DEVICE_POWER_DOMAIN_DYNAMIC)
|
||||||
|
set(number_of_dynamic_devices ${CONFIG_PM_DEVICE_POWER_DOMAIN_DYNAMIC_NUM})
|
||||||
|
else()
|
||||||
|
set(number_of_dynamic_devices 0)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CONFIG_HAS_DTS)
|
if(CONFIG_HAS_DTS)
|
||||||
# dev_handles.c is generated from ${ZEPHYR_LINK_STAGE_EXECUTABLE} by
|
# dev_handles.c is generated from ${ZEPHYR_LINK_STAGE_EXECUTABLE} by
|
||||||
# gen_handles.py
|
# gen_handles.py
|
||||||
|
@ -849,6 +855,7 @@ if(CONFIG_HAS_DTS)
|
||||||
${PYTHON_EXECUTABLE}
|
${PYTHON_EXECUTABLE}
|
||||||
${ZEPHYR_BASE}/scripts/gen_handles.py
|
${ZEPHYR_BASE}/scripts/gen_handles.py
|
||||||
--output-source dev_handles.c
|
--output-source dev_handles.c
|
||||||
|
--num-dynamic-devices ${number_of_dynamic_devices}
|
||||||
--kernel $<TARGET_FILE:${ZEPHYR_LINK_STAGE_EXECUTABLE}>
|
--kernel $<TARGET_FILE:${ZEPHYR_LINK_STAGE_EXECUTABLE}>
|
||||||
--zephyr-base ${ZEPHYR_BASE}
|
--zephyr-base ${ZEPHYR_BASE}
|
||||||
--start-symbol "$<TARGET_PROPERTY:linker,devices_start_symbol>"
|
--start-symbol "$<TARGET_PROPERTY:linker,devices_start_symbol>"
|
||||||
|
|
|
@ -444,6 +444,12 @@ struct device_state {
|
||||||
|
|
||||||
struct pm_device;
|
struct pm_device;
|
||||||
|
|
||||||
|
#ifdef CONFIG_HAS_DYNAMIC_DEVICE_HANDLES
|
||||||
|
#define Z_DEVICE_HANDLES_CONST
|
||||||
|
#else
|
||||||
|
#define Z_DEVICE_HANDLES_CONST const
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Runtime device structure (in ROM) per driver instance
|
* @brief Runtime device structure (in ROM) per driver instance
|
||||||
*/
|
*/
|
||||||
|
@ -465,7 +471,8 @@ struct device {
|
||||||
* extracted with dedicated API, such as
|
* extracted with dedicated API, such as
|
||||||
* device_required_handles_get().
|
* device_required_handles_get().
|
||||||
*/
|
*/
|
||||||
const device_handle_t *const handles;
|
Z_DEVICE_HANDLES_CONST device_handle_t * const handles;
|
||||||
|
|
||||||
#ifdef CONFIG_PM_DEVICE
|
#ifdef CONFIG_PM_DEVICE
|
||||||
/** Reference to the device PM resources. */
|
/** Reference to the device PM resources. */
|
||||||
struct pm_device * const pm;
|
struct pm_device * const pm;
|
||||||
|
@ -877,9 +884,9 @@ __deprecated static inline int device_usable_check(const struct device *dev)
|
||||||
*/
|
*/
|
||||||
BUILD_ASSERT(sizeof(device_handle_t) == 2, "fix the linker scripts");
|
BUILD_ASSERT(sizeof(device_handle_t) == 2, "fix the linker scripts");
|
||||||
#define Z_DEVICE_DEFINE_HANDLES(node_id, dev_name, ...) \
|
#define Z_DEVICE_DEFINE_HANDLES(node_id, dev_name, ...) \
|
||||||
extern const device_handle_t \
|
extern Z_DEVICE_HANDLES_CONST device_handle_t \
|
||||||
Z_DEVICE_HANDLE_NAME(node_id, dev_name)[]; \
|
Z_DEVICE_HANDLE_NAME(node_id, dev_name)[]; \
|
||||||
const device_handle_t \
|
Z_DEVICE_HANDLES_CONST device_handle_t \
|
||||||
__aligned(sizeof(device_handle_t)) \
|
__aligned(sizeof(device_handle_t)) \
|
||||||
__attribute__((__weak__, \
|
__attribute__((__weak__, \
|
||||||
__section__(".__device_handles_pass1"))) \
|
__section__(".__device_handles_pass1"))) \
|
||||||
|
|
|
@ -45,6 +45,19 @@
|
||||||
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_HAS_DYNAMIC_DEVICE_HANDLES)
|
||||||
|
SECTION_DATA_PROLOGUE(device_handles,,)
|
||||||
|
{
|
||||||
|
__device_handles_start = .;
|
||||||
|
#ifdef LINKER_DEVICE_HANDLES_PASS1
|
||||||
|
KEEP(*(SORT(.__device_handles_pass1*)));
|
||||||
|
#else
|
||||||
|
KEEP(*(SORT(.__device_handles_pass2*)));
|
||||||
|
#endif /* LINKER_DEVICE_HANDLES_PASS1 */
|
||||||
|
__device_handles_end = .;
|
||||||
|
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
||||||
|
#endif /* CONFIG_HAS_DYNAMIC_DEVICE_HANDLES */
|
||||||
|
|
||||||
SECTION_DATA_PROLOGUE(initshell,,)
|
SECTION_DATA_PROLOGUE(initshell,,)
|
||||||
{
|
{
|
||||||
/* link in shell initialization objects for all modules that
|
/* link in shell initialization objects for all modules that
|
||||||
|
|
|
@ -220,6 +220,7 @@
|
||||||
KEEP(*(".dbg_thread_info"));
|
KEEP(*(".dbg_thread_info"));
|
||||||
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
||||||
|
|
||||||
|
#if !defined(CONFIG_HAS_DYNAMIC_DEVICE_HANDLES)
|
||||||
SECTION_DATA_PROLOGUE(device_handles,,)
|
SECTION_DATA_PROLOGUE(device_handles,,)
|
||||||
{
|
{
|
||||||
__device_handles_start = .;
|
__device_handles_start = .;
|
||||||
|
@ -230,3 +231,4 @@
|
||||||
#endif /* LINKER_DEVICE_HANDLES_PASS1 */
|
#endif /* LINKER_DEVICE_HANDLES_PASS1 */
|
||||||
__device_handles_end = .;
|
__device_handles_end = .;
|
||||||
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
|
||||||
|
#endif /* !CONFIG_HAS_DYNAMIC_DEVICE_HANDLES */
|
||||||
|
|
|
@ -521,6 +521,36 @@ bool pm_device_state_is_locked(const struct device *dev);
|
||||||
*/
|
*/
|
||||||
bool pm_device_on_power_domain(const struct device *dev);
|
bool pm_device_on_power_domain(const struct device *dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a device to a power domain.
|
||||||
|
*
|
||||||
|
* This function adds a device to a given power domain.
|
||||||
|
*
|
||||||
|
* @param dev Device to be added to the power domain.
|
||||||
|
* @param domain Power domain.
|
||||||
|
*
|
||||||
|
* @retval 0 If successful.
|
||||||
|
* @retval -EALREADY If device is already part of the power domain.
|
||||||
|
* @retval -ENOSYS If the application was built without power domain support.
|
||||||
|
* @retval -ENOSPC If there is no space available in the power domain to add the device.
|
||||||
|
*/
|
||||||
|
int pm_device_power_domain_add(const struct device *dev,
|
||||||
|
const struct device *domain);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a device from a power domain.
|
||||||
|
*
|
||||||
|
* This function removes a device from a given power domain.
|
||||||
|
*
|
||||||
|
* @param dev Device to be removed from the power domain.
|
||||||
|
* @param domain Power domain.
|
||||||
|
*
|
||||||
|
* @retval 0 If successful.
|
||||||
|
* @retval -ENOSYS If the application was built without power domain support.
|
||||||
|
* @retval -ENOENT If device is not in the given domain.
|
||||||
|
*/
|
||||||
|
int pm_device_power_domain_remove(const struct device *dev,
|
||||||
|
const struct device *domain);
|
||||||
#else
|
#else
|
||||||
static inline void pm_device_init_suspended(const struct device *dev)
|
static inline void pm_device_init_suspended(const struct device *dev)
|
||||||
{
|
{
|
||||||
|
@ -579,6 +609,19 @@ static inline bool pm_device_on_power_domain(const struct device *dev)
|
||||||
ARG_UNUSED(dev);
|
ARG_UNUSED(dev);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int pm_device_power_domain_add(const struct device *dev,
|
||||||
|
const struct device *domain)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int pm_device_power_domain_remove(const struct device *dev,
|
||||||
|
const struct device *domain)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_PM_DEVICE */
|
#endif /* CONFIG_PM_DEVICE */
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
|
@ -928,4 +928,14 @@ config THREAD_LOCAL_STORAGE
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
|
menu "Device Options"
|
||||||
|
|
||||||
|
config HAS_DYNAMIC_DEVICE_HANDLES
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
Hidden option that makes possible to manipulate device handles at
|
||||||
|
runtime.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
rsource "Kconfig.vm"
|
rsource "Kconfig.vm"
|
||||||
|
|
|
@ -62,6 +62,8 @@ def parse_args():
|
||||||
|
|
||||||
parser.add_argument("-k", "--kernel", required=True,
|
parser.add_argument("-k", "--kernel", required=True,
|
||||||
help="Input zephyr ELF binary")
|
help="Input zephyr ELF binary")
|
||||||
|
parser.add_argument("-d", "--num-dynamic-devices", required=False, default=0,
|
||||||
|
type=int, help="Input number of dynamic devices allowed")
|
||||||
parser.add_argument("-o", "--output-source", required=True,
|
parser.add_argument("-o", "--output-source", required=True,
|
||||||
help="Output source file")
|
help="Output source file")
|
||||||
|
|
||||||
|
@ -112,6 +114,7 @@ def symbol_handle_data(elf, sym):
|
||||||
# These match the corresponding constants in <device.h>
|
# These match the corresponding constants in <device.h>
|
||||||
DEVICE_HANDLE_SEP = -32768
|
DEVICE_HANDLE_SEP = -32768
|
||||||
DEVICE_HANDLE_ENDS = 32767
|
DEVICE_HANDLE_ENDS = 32767
|
||||||
|
DEVICE_HANDLE_NULL = 0
|
||||||
def handle_name(hdl):
|
def handle_name(hdl):
|
||||||
if hdl == DEVICE_HANDLE_SEP:
|
if hdl == DEVICE_HANDLE_SEP:
|
||||||
return "DEVICE_HANDLE_SEP"
|
return "DEVICE_HANDLE_SEP"
|
||||||
|
@ -336,6 +339,7 @@ def main():
|
||||||
else:
|
else:
|
||||||
sup_paths.append('(%s)' % dn.path)
|
sup_paths.append('(%s)' % dn.path)
|
||||||
hdls.extend(dn.__device.dev_handle for dn in sn.__supports)
|
hdls.extend(dn.__device.dev_handle for dn in sn.__supports)
|
||||||
|
hdls.extend(DEVICE_HANDLE_NULL for dn in range(args.num_dynamic_devices))
|
||||||
|
|
||||||
# Terminate the array with the end symbol
|
# Terminate the array with the end symbol
|
||||||
hdls.append(DEVICE_HANDLE_ENDS)
|
hdls.append(DEVICE_HANDLE_ENDS)
|
||||||
|
|
|
@ -82,6 +82,20 @@ config PM_DEVICE_POWER_DOMAIN
|
||||||
devices that depend on a domain will be notified when this
|
devices that depend on a domain will be notified when this
|
||||||
domain is suspended or resumed.
|
domain is suspended or resumed.
|
||||||
|
|
||||||
|
config PM_DEVICE_POWER_DOMAIN_DYNAMIC
|
||||||
|
bool "Dynamically bind devices to a Power Pomain"
|
||||||
|
depends on PM_DEVICE_POWER_DOMAIN
|
||||||
|
select HAS_DYNAMIC_DEVICE_HANDLES
|
||||||
|
help
|
||||||
|
Enable support for dynamically bind devices to a Power Domain.
|
||||||
|
|
||||||
|
config PM_DEVICE_POWER_DOMAIN_DYNAMIC_NUM
|
||||||
|
int "Number of devices that can dynamically be bind to a Power Domain"
|
||||||
|
depends on PM_DEVICE_POWER_DOMAIN_DYNAMIC
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
The number of devices that can dynamically be bind to a Power Domain.
|
||||||
|
|
||||||
config PM_DEVICE_RUNTIME
|
config PM_DEVICE_RUNTIME
|
||||||
bool "Runtime Device Power Management"
|
bool "Runtime Device Power Management"
|
||||||
help
|
help
|
||||||
|
|
|
@ -101,6 +101,82 @@ int pm_device_action_run(const struct device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int power_domain_add_or_remove(const struct device *dev,
|
||||||
|
const struct device *domain,
|
||||||
|
bool add)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_HAS_DYNAMIC_DEVICE_HANDLES)
|
||||||
|
device_handle_t *rv = domain->handles;
|
||||||
|
device_handle_t dev_handle = -1;
|
||||||
|
extern const struct device __device_start[];
|
||||||
|
extern const struct device __device_end[];
|
||||||
|
size_t i, region = 0;
|
||||||
|
size_t numdev = __device_end - __device_start;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Supported devices are stored as device handle and not
|
||||||
|
* device pointers. So, it is necessary to find what is
|
||||||
|
* the handle associated to the given device.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < numdev; i++) {
|
||||||
|
if (&__device_start[i] == dev) {
|
||||||
|
dev_handle = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The last part is to find an available slot in the
|
||||||
|
* supported section of handles array and replace it
|
||||||
|
* with the device handle.
|
||||||
|
*/
|
||||||
|
while (region != 2) {
|
||||||
|
if (*rv == DEVICE_HANDLE_SEP) {
|
||||||
|
region++;
|
||||||
|
}
|
||||||
|
rv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (rv[i] != DEVICE_HANDLE_ENDS) {
|
||||||
|
if (add == false) {
|
||||||
|
if (rv[i] == dev_handle) {
|
||||||
|
dev->pm->domain = NULL;
|
||||||
|
rv[i] = DEVICE_HANDLE_NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (rv[i] == DEVICE_HANDLE_NULL) {
|
||||||
|
dev->pm->domain = domain;
|
||||||
|
rv[i] = dev_handle;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return add ? -ENOSPC : -ENOENT;
|
||||||
|
#else
|
||||||
|
ARG_UNUSED(dev);
|
||||||
|
ARG_UNUSED(domain);
|
||||||
|
ARG_UNUSED(add);
|
||||||
|
|
||||||
|
return -ENOSYS;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int pm_device_power_domain_remove(const struct device *dev,
|
||||||
|
const struct device *domain)
|
||||||
|
{
|
||||||
|
return power_domain_add_or_remove(dev, domain, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pm_device_power_domain_add(const struct device *dev,
|
||||||
|
const struct device *domain)
|
||||||
|
{
|
||||||
|
return power_domain_add_or_remove(dev, domain, true);
|
||||||
|
}
|
||||||
|
|
||||||
void pm_device_children_action_run(const struct device *dev,
|
void pm_device_children_action_run(const struct device *dev,
|
||||||
enum pm_device_action action,
|
enum pm_device_action action,
|
||||||
pm_device_action_failed_cb_t failure_cb)
|
pm_device_action_failed_cb_t failure_cb)
|
||||||
|
@ -121,6 +197,10 @@ void pm_device_children_action_run(const struct device *dev,
|
||||||
device_handle_t dh = handles[i];
|
device_handle_t dh = handles[i];
|
||||||
const struct device *cdev = device_from_handle(dh);
|
const struct device *cdev = device_from_handle(dh);
|
||||||
|
|
||||||
|
if (cdev == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
rc = pm_device_action_run(cdev, action);
|
rc = pm_device_action_run(cdev, action);
|
||||||
if ((failure_cb != NULL) && (rc < 0)) {
|
if ((failure_cb != NULL) && (rc < 0)) {
|
||||||
/* Stop the iteration if the callback requests it */
|
/* Stop the iteration if the callback requests it */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue