diff --git a/dts/bindings/base/base.yaml b/dts/bindings/base/base.yaml index f1d1109a19921e..b2e670fb5d81b5 100644 --- a/dts/bindings/base/base.yaml +++ b/dts/bindings/base/base.yaml @@ -92,3 +92,9 @@ properties: mbox-names: type: string-array description: Provided names of mailbox / IPM channel specifiers + + zephyr,deferred-init: + type: boolean + description: | + Do not initialize device automatically on boot. Device should be manually + initialized using device_init(). diff --git a/include/zephyr/device.h b/include/zephyr/device.h index 607cd76f6f22ee..27086227143a6d 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -151,6 +151,16 @@ typedef int16_t device_handle_t; #define DEVICE_DT_NAME(node_id) \ DT_PROP_OR(node_id, label, DT_NODE_FULL_NAME(node_id)) +/** + * @brief Determine if a devicetree node initialization should be deferred. + * + * @param node_id The devicetree node identifier. + * + * @return Boolean stating if node initialization should be deferred. + */ +#define DEVICE_DT_DEFER(node_id) \ + DT_PROP(node_id, zephyr_deferred_init) + /** * @brief Create a device object from a devicetree node identifier and set it up * for boot time initialization. @@ -758,6 +768,22 @@ static inline bool z_impl_device_is_ready(const struct device *dev) return z_device_is_ready(dev); } +/** + * @brief Initialize a device. + * + * A device whose initialization was deferred (by marking it as + * ``zephyr,deferred-init`` on devicetree) needs to be initialized manually via + * this call. Note that only devices whose initialization was deferred can be + * initialized via this call - one can not try to initialize a non + * initialization deferred device that failed initialization with this call. + * + * @param dev device to be initialized. + * + * @retval -ENOENT If device was not found - or isn't a deferred one. + * @retval -errno For other errors. + */ +__syscall int device_init(const struct device *dev); + /** * @} */ @@ -988,6 +1014,18 @@ static inline bool z_impl_device_is_ready(const struct device *dev) }, \ } +#define Z_DEFER_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn_) \ + static const Z_DECL_ALIGN(struct init_entry) __used __noasan \ + __attribute__((__section__(".z_deferred_init"))) \ + Z_INIT_ENTRY_NAME(DEVICE_NAME_GET(dev_id)) = { \ + .init_fn = {COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (.dev_rw), (.dev)) = \ + (init_fn_)}, \ + { \ + COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (.dev_rw), (.dev)) = \ + &DEVICE_NAME_GET(dev_id), \ + }, \ + } + /** * @brief Define a @ref device and all other required objects. * @@ -1019,7 +1057,11 @@ static inline bool z_impl_device_is_ready(const struct device *dev) Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, pm, data, config, level, \ prio, api, state, Z_DEVICE_DEPS_NAME(dev_id)); \ \ - Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn, level, prio) + COND_CODE_1(DEVICE_DT_DEFER(node_id), \ + (Z_DEFER_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, \ + init_fn)), \ + (Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn, \ + level, prio))); /** * @brief Declare a device for each status "okay" devicetree node. diff --git a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld index dfa19d13492ecc..47f20cb006c124 100644 --- a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld +++ b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld @@ -18,6 +18,9 @@ CREATE_OBJ_LEVEL(init, APPLICATION) CREATE_OBJ_LEVEL(init, SMP) __init_end = .; + __deferred_init_list_start = .; + KEEP(*(.z_deferred_init)) + __deferred_init_list_end = .; } GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) ITERABLE_SECTION_ROM_NUMERIC(device, 4) diff --git a/kernel/init.c b/kernel/init.c index fe6c176a072c0b..76a4bbe238a6ac 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -36,6 +36,7 @@ #include #include #include +#include LOG_MODULE_REGISTER(os, CONFIG_KERNEL_LOG_LEVEL); BUILD_ASSERT(CONFIG_MP_NUM_CPUS == CONFIG_MP_MAX_NUM_CPUS, @@ -301,6 +302,37 @@ extern volatile uintptr_t __stack_chk_guard; __pinned_bss bool z_sys_post_kernel; +static int do_device_init(const struct init_entry *entry) +{ + const struct device *dev = entry->dev; + int rc = 0; + + if (entry->init_fn.dev != NULL) { + rc = entry->init_fn.dev(dev); + /* Mark device initialized. If initialization + * failed, record the error condition. + */ + if (rc != 0) { + if (rc < 0) { + rc = -rc; + } + if (rc > UINT8_MAX) { + rc = UINT8_MAX; + } + dev->state->init_res = rc; + } + } + + dev->state->initialized = true; + + if (rc == 0) { + /* Run automatic device runtime enablement */ + (void)pm_device_runtime_auto_enable(dev); + } + + return rc; +} + /** * @brief Execute all the init entry initialization functions at a given level * @@ -332,36 +364,39 @@ static void z_sys_init_run_level(enum init_level level) const struct device *dev = entry->dev; if (dev != NULL) { - int rc = 0; - - if (entry->init_fn.dev != NULL) { - rc = entry->init_fn.dev(dev); - /* Mark device initialized. If initialization - * failed, record the error condition. - */ - if (rc != 0) { - if (rc < 0) { - rc = -rc; - } - if (rc > UINT8_MAX) { - rc = UINT8_MAX; - } - dev->state->init_res = rc; - } - } - - dev->state->initialized = true; - - if (rc == 0) { - /* Run automatic device runtime enablement */ - (void)pm_device_runtime_auto_enable(dev); - } + do_device_init(entry); } else { (void)entry->init_fn.sys(); } } } + +int z_impl_device_init(const struct device *dev) +{ + if (dev == NULL) { + return -ENOENT; + } + + STRUCT_SECTION_FOREACH_ALTERNATE(_deferred_init, init_entry, entry) { + if (entry->dev == dev) { + return do_device_init(entry); + } + } + + return -ENOENT; +} + +#ifdef CONFIG_USERSPACE +static inline int z_vrfy_device_init(const struct device *dev) +{ + K_OOPS(K_SYSCALL_OBJ_INIT(dev, K_OBJ_ANY)); + + return z_impl_device_init(dev); +} +#include +#endif + extern void boot_banner(void);