Skip to content

Commit 0b13b44

Browse files
Flavio Ceolinmbolivar-nordic
Flavio Ceolin
authored andcommitted
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>
1 parent b2d3fdc commit 0b13b44

File tree

9 files changed

+183
-3
lines changed

9 files changed

+183
-3
lines changed

CMakeLists.txt

+7
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,12 @@ zephyr_get_include_directories_for_lang(C
840840
STRIP_PREFIX # Don't use a -I prefix
841841
)
842842

843+
if(CONFIG_PM_DEVICE_POWER_DOMAIN_DYNAMIC)
844+
set(number_of_dynamic_devices ${CONFIG_PM_DEVICE_POWER_DOMAIN_DYNAMIC_NUM})
845+
else()
846+
set(number_of_dynamic_devices 0)
847+
endif()
848+
843849
if(CONFIG_HAS_DTS)
844850
# dev_handles.c is generated from ${ZEPHYR_LINK_STAGE_EXECUTABLE} by
845851
# gen_handles.py
@@ -849,6 +855,7 @@ if(CONFIG_HAS_DTS)
849855
${PYTHON_EXECUTABLE}
850856
${ZEPHYR_BASE}/scripts/gen_handles.py
851857
--output-source dev_handles.c
858+
--num-dynamic-devices ${number_of_dynamic_devices}
852859
--kernel $<TARGET_FILE:${ZEPHYR_LINK_STAGE_EXECUTABLE}>
853860
--zephyr-base ${ZEPHYR_BASE}
854861
--start-symbol "$<TARGET_PROPERTY:linker,devices_start_symbol>"

include/zephyr/device.h

+10-3
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,12 @@ struct device_state {
444444

445445
struct pm_device;
446446

447+
#ifdef CONFIG_HAS_DYNAMIC_DEVICE_HANDLES
448+
#define Z_DEVICE_HANDLES_CONST
449+
#else
450+
#define Z_DEVICE_HANDLES_CONST const
451+
#endif
452+
447453
/**
448454
* @brief Runtime device structure (in ROM) per driver instance
449455
*/
@@ -465,7 +471,8 @@ struct device {
465471
* extracted with dedicated API, such as
466472
* device_required_handles_get().
467473
*/
468-
const device_handle_t *const handles;
474+
Z_DEVICE_HANDLES_CONST device_handle_t * const handles;
475+
469476
#ifdef CONFIG_PM_DEVICE
470477
/** Reference to the device PM resources. */
471478
struct pm_device * const pm;
@@ -877,9 +884,9 @@ __deprecated static inline int device_usable_check(const struct device *dev)
877884
*/
878885
BUILD_ASSERT(sizeof(device_handle_t) == 2, "fix the linker scripts");
879886
#define Z_DEVICE_DEFINE_HANDLES(node_id, dev_name, ...) \
880-
extern const device_handle_t \
887+
extern Z_DEVICE_HANDLES_CONST device_handle_t \
881888
Z_DEVICE_HANDLE_NAME(node_id, dev_name)[]; \
882-
const device_handle_t \
889+
Z_DEVICE_HANDLES_CONST device_handle_t \
883890
__aligned(sizeof(device_handle_t)) \
884891
__attribute__((__weak__, \
885892
__section__(".__device_handles_pass1"))) \

include/zephyr/linker/common-ram.ld

+13
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@
4545
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
4646
#endif
4747

48+
#if defined(CONFIG_HAS_DYNAMIC_DEVICE_HANDLES)
49+
SECTION_DATA_PROLOGUE(device_handles,,)
50+
{
51+
__device_handles_start = .;
52+
#ifdef LINKER_DEVICE_HANDLES_PASS1
53+
KEEP(*(SORT(.__device_handles_pass1*)));
54+
#else
55+
KEEP(*(SORT(.__device_handles_pass2*)));
56+
#endif /* LINKER_DEVICE_HANDLES_PASS1 */
57+
__device_handles_end = .;
58+
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
59+
#endif /* CONFIG_HAS_DYNAMIC_DEVICE_HANDLES */
60+
4861
SECTION_DATA_PROLOGUE(initshell,,)
4962
{
5063
/* link in shell initialization objects for all modules that

include/zephyr/linker/common-rom.ld

+2
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@
220220
KEEP(*(".dbg_thread_info"));
221221
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
222222

223+
#if !defined(CONFIG_HAS_DYNAMIC_DEVICE_HANDLES)
223224
SECTION_DATA_PROLOGUE(device_handles,,)
224225
{
225226
__device_handles_start = .;
@@ -230,3 +231,4 @@
230231
#endif /* LINKER_DEVICE_HANDLES_PASS1 */
231232
__device_handles_end = .;
232233
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
234+
#endif /* !CONFIG_HAS_DYNAMIC_DEVICE_HANDLES */

include/zephyr/pm/device.h

+43
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,36 @@ bool pm_device_state_is_locked(const struct device *dev);
521521
*/
522522
bool pm_device_on_power_domain(const struct device *dev);
523523

524+
/**
525+
* @brief Add a device to a power domain.
526+
*
527+
* This function adds a device to a given power domain.
528+
*
529+
* @param dev Device to be added to the power domain.
530+
* @param domain Power domain.
531+
*
532+
* @retval 0 If successful.
533+
* @retval -EALREADY If device is already part of the power domain.
534+
* @retval -ENOSYS If the application was built without power domain support.
535+
* @retval -ENOSPC If there is no space available in the power domain to add the device.
536+
*/
537+
int pm_device_power_domain_add(const struct device *dev,
538+
const struct device *domain);
539+
540+
/**
541+
* @brief Remove a device from a power domain.
542+
*
543+
* This function removes a device from a given power domain.
544+
*
545+
* @param dev Device to be removed from the power domain.
546+
* @param domain Power domain.
547+
*
548+
* @retval 0 If successful.
549+
* @retval -ENOSYS If the application was built without power domain support.
550+
* @retval -ENOENT If device is not in the given domain.
551+
*/
552+
int pm_device_power_domain_remove(const struct device *dev,
553+
const struct device *domain);
524554
#else
525555
static inline void pm_device_init_suspended(const struct device *dev)
526556
{
@@ -579,6 +609,19 @@ static inline bool pm_device_on_power_domain(const struct device *dev)
579609
ARG_UNUSED(dev);
580610
return false;
581611
}
612+
613+
static inline int pm_device_power_domain_add(const struct device *dev,
614+
const struct device *domain)
615+
{
616+
return -ENOSYS;
617+
}
618+
619+
static inline int pm_device_power_domain_remove(const struct device *dev,
620+
const struct device *domain)
621+
{
622+
return -ENOSYS;
623+
}
624+
582625
#endif /* CONFIG_PM_DEVICE */
583626

584627
/** @} */

kernel/Kconfig

+10
Original file line numberDiff line numberDiff line change
@@ -928,4 +928,14 @@ config THREAD_LOCAL_STORAGE
928928

929929
endmenu
930930

931+
menu "Device Options"
932+
933+
config HAS_DYNAMIC_DEVICE_HANDLES
934+
bool
935+
help
936+
Hidden option that makes possible to manipulate device handles at
937+
runtime.
938+
939+
endmenu
940+
931941
rsource "Kconfig.vm"

scripts/gen_handles.py

+4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ def parse_args():
6262

6363
parser.add_argument("-k", "--kernel", required=True,
6464
help="Input zephyr ELF binary")
65+
parser.add_argument("-d", "--num-dynamic-devices", required=False, default=0,
66+
type=int, help="Input number of dynamic devices allowed")
6567
parser.add_argument("-o", "--output-source", required=True,
6668
help="Output source file")
6769

@@ -112,6 +114,7 @@ def symbol_handle_data(elf, sym):
112114
# These match the corresponding constants in <device.h>
113115
DEVICE_HANDLE_SEP = -32768
114116
DEVICE_HANDLE_ENDS = 32767
117+
DEVICE_HANDLE_NULL = 0
115118
def handle_name(hdl):
116119
if hdl == DEVICE_HANDLE_SEP:
117120
return "DEVICE_HANDLE_SEP"
@@ -336,6 +339,7 @@ def main():
336339
else:
337340
sup_paths.append('(%s)' % dn.path)
338341
hdls.extend(dn.__device.dev_handle for dn in sn.__supports)
342+
hdls.extend(DEVICE_HANDLE_NULL for dn in range(args.num_dynamic_devices))
339343

340344
# Terminate the array with the end symbol
341345
hdls.append(DEVICE_HANDLE_ENDS)

subsys/pm/Kconfig

+14
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,20 @@ config PM_DEVICE_POWER_DOMAIN
8282
devices that depend on a domain will be notified when this
8383
domain is suspended or resumed.
8484

85+
config PM_DEVICE_POWER_DOMAIN_DYNAMIC
86+
bool "Dynamically bind devices to a Power Pomain"
87+
depends on PM_DEVICE_POWER_DOMAIN
88+
select HAS_DYNAMIC_DEVICE_HANDLES
89+
help
90+
Enable support for dynamically bind devices to a Power Domain.
91+
92+
config PM_DEVICE_POWER_DOMAIN_DYNAMIC_NUM
93+
int "Number of devices that can dynamically be bind to a Power Domain"
94+
depends on PM_DEVICE_POWER_DOMAIN_DYNAMIC
95+
default 1
96+
help
97+
The number of devices that can dynamically be bind to a Power Domain.
98+
8599
config PM_DEVICE_RUNTIME
86100
bool "Runtime Device Power Management"
87101
help

subsys/pm/device.c

+80
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,82 @@ int pm_device_action_run(const struct device *dev,
101101
return 0;
102102
}
103103

104+
static int power_domain_add_or_remove(const struct device *dev,
105+
const struct device *domain,
106+
bool add)
107+
{
108+
#if defined(CONFIG_HAS_DYNAMIC_DEVICE_HANDLES)
109+
device_handle_t *rv = domain->handles;
110+
device_handle_t dev_handle = -1;
111+
extern const struct device __device_start[];
112+
extern const struct device __device_end[];
113+
size_t i, region = 0;
114+
size_t numdev = __device_end - __device_start;
115+
116+
/*
117+
* Supported devices are stored as device handle and not
118+
* device pointers. So, it is necessary to find what is
119+
* the handle associated to the given device.
120+
*/
121+
for (i = 0; i < numdev; i++) {
122+
if (&__device_start[i] == dev) {
123+
dev_handle = i + 1;
124+
break;
125+
}
126+
}
127+
128+
/*
129+
* The last part is to find an available slot in the
130+
* supported section of handles array and replace it
131+
* with the device handle.
132+
*/
133+
while (region != 2) {
134+
if (*rv == DEVICE_HANDLE_SEP) {
135+
region++;
136+
}
137+
rv++;
138+
}
139+
140+
i = 0;
141+
while (rv[i] != DEVICE_HANDLE_ENDS) {
142+
if (add == false) {
143+
if (rv[i] == dev_handle) {
144+
dev->pm->domain = NULL;
145+
rv[i] = DEVICE_HANDLE_NULL;
146+
return 0;
147+
}
148+
} else {
149+
if (rv[i] == DEVICE_HANDLE_NULL) {
150+
dev->pm->domain = domain;
151+
rv[i] = dev_handle;
152+
return 0;
153+
}
154+
}
155+
++i;
156+
}
157+
158+
return add ? -ENOSPC : -ENOENT;
159+
#else
160+
ARG_UNUSED(dev);
161+
ARG_UNUSED(domain);
162+
ARG_UNUSED(add);
163+
164+
return -ENOSYS;
165+
#endif
166+
}
167+
168+
int pm_device_power_domain_remove(const struct device *dev,
169+
const struct device *domain)
170+
{
171+
return power_domain_add_or_remove(dev, domain, false);
172+
}
173+
174+
int pm_device_power_domain_add(const struct device *dev,
175+
const struct device *domain)
176+
{
177+
return power_domain_add_or_remove(dev, domain, true);
178+
}
179+
104180
void pm_device_children_action_run(const struct device *dev,
105181
enum pm_device_action action,
106182
pm_device_action_failed_cb_t failure_cb)
@@ -121,6 +197,10 @@ void pm_device_children_action_run(const struct device *dev,
121197
device_handle_t dh = handles[i];
122198
const struct device *cdev = device_from_handle(dh);
123199

200+
if (cdev == NULL) {
201+
continue;
202+
}
203+
124204
rc = pm_device_action_run(cdev, action);
125205
if ((failure_cb != NULL) && (rc < 0)) {
126206
/* Stop the iteration if the callback requests it */

0 commit comments

Comments
 (0)