diff --git a/cmake/linker_script/common/common-rom.cmake b/cmake/linker_script/common/common-rom.cmake index 9807a448a54c4..7e9f173d4b4fe 100644 --- a/cmake/linker_script/common/common-rom.cmake +++ b/cmake/linker_script/common/common-rom.cmake @@ -8,9 +8,6 @@ zephyr_linker_section_obj_level(SECTION init LEVEL POST_KERNEL) zephyr_linker_section_obj_level(SECTION init LEVEL APPLICATION) zephyr_linker_section_obj_level(SECTION init LEVEL SMP) -zephyr_linker_section(NAME deferred_init_list KVMA RAM_REGION GROUP RODATA_REGION) -zephyr_linker_section_configure(SECTION deferred_init_list INPUT ".z_deferred_init*" KEEP SORT NAME) - zephyr_iterable_section(NAME device NUMERIC KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN ${CONFIG_LINKER_ITERABLE_SUBALIGN}) if(CONFIG_GEN_SW_ISR_TABLE AND NOT CONFIG_DYNAMIC_INTERRUPTS) diff --git a/drivers/misc/devmux/devmux.c b/drivers/misc/devmux/devmux.c index b01ebdae45f78..f4e64fdb385b2 100644 --- a/drivers/misc/devmux/devmux.c +++ b/drivers/misc/devmux/devmux.c @@ -129,7 +129,7 @@ int z_vrfy_devmux_select_set(struct device *dev, size_t index) #include #endif -static int devmux_init(struct device *const dev) +static int devmux_init(const struct device *dev) { size_t inst = devmux_inst_get(dev); struct devmux_data *const data = dev->data; @@ -143,7 +143,7 @@ static int devmux_init(struct device *const dev) return -ENODEV; } - *dev = *config->devs[sel]; + *(struct device *)dev = *config->devs[sel]; return 0; } diff --git a/include/zephyr/device.h b/include/zephyr/device.h index 57c628b94e94e..7cd17ac41e7c8 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -135,13 +135,14 @@ typedef int16_t device_handle_t; #endif /** - * @brief Create a device object and set it up for boot time initialization. + * @brief Create a device object and set it up for boot time initialization, + * with de-init capabilities. * * This macro defines a @ref device that is automatically configured by the * kernel during system initialization. This macro should only be used when the * device is not being allocated from a devicetree node. If you are allocating a - * device from a devicetree node, use DEVICE_DT_DEFINE() or - * DEVICE_DT_INST_DEFINE() instead. + * device from a devicetree node, use DEVICE_DT_DEINIT_DEFINE() or + * DEVICE_DT_INST_DEINIT_DEFINE() instead. * * @param dev_id A unique token which is used in the name of the global device * structure as a C identifier. @@ -151,6 +152,9 @@ typedef int16_t device_handle_t; * (including terminating `NULL`) in order to be looked up from user mode. * @param init_fn Pointer to the device's initialization function, which will be * run by the kernel during system initialization. Can be `NULL`. + * @param deinit_fn Pointer to the device's de-initialization function. Can be + * `NULL`. It must release any acquired resources (e.g. pins, bus, clock...) and + * leave the device in its reset state. * @param pm Pointer to the device's power management resources, a * @ref pm_device, which will be stored in @ref device.pm field. Use `NULL` if * the device does not use PM. @@ -164,13 +168,23 @@ typedef int16_t device_handle_t; * SYS_INIT() for details. * @param api Pointer to the device's API structure. Can be `NULL`. */ -#define DEVICE_DEFINE(dev_id, name, init_fn, pm, data, config, level, prio, \ - api) \ +#define DEVICE_DEINIT_DEFINE(dev_id, name, init_fn, deinit_fn, pm, data, \ + config, level, prio, api) \ Z_DEVICE_STATE_DEFINE(dev_id); \ - Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, name, init_fn, pm, data, \ - config, level, prio, api, \ + Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, name, init_fn, deinit_fn, 0U, \ + pm, data, config, level, prio, api, \ &Z_DEVICE_STATE_NAME(dev_id)) +/** + * @brief Create a device object and set it up for boot time initialization. + * + * @see DEVICE_DEINIT_DEFINE() + */ +#define DEVICE_DEFINE(dev_id, name, init_fn, pm, data, config, level, prio, \ + api) \ + DEVICE_DEINIT_DEFINE(dev_id, name, init_fn, NULL, pm, data, config, \ + level, prio, api) + /** * @brief Return a string name for a devicetree node. * @@ -185,16 +199,6 @@ 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. @@ -213,6 +217,9 @@ typedef int16_t device_handle_t; * @param node_id The devicetree node identifier. * @param init_fn Pointer to the device's initialization function, which will be * run by the kernel during system initialization. Can be `NULL`. + * @param deinit_fn Pointer to the device's de-initialization function. Can be + * `NULL`. It must release any acquired resources (e.g. pins, bus, clock...) and + * leave the device in its reset state. * @param pm Pointer to the device's power management resources, a * @ref pm_device, which will be stored in @ref device.pm. Use `NULL` if the * device does not use PM. @@ -226,15 +233,38 @@ typedef int16_t device_handle_t; * SYS_INIT() for details. * @param api Pointer to the device's API structure. Can be `NULL`. */ -#define DEVICE_DT_DEFINE(node_id, init_fn, pm, data, config, level, prio, api, \ - ...) \ +#define DEVICE_DT_DEINIT_DEFINE(node_id, init_fn, deinit_fn, pm, data, config, \ + level, prio, api, ...) \ Z_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \ Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \ - DEVICE_DT_NAME(node_id), init_fn, pm, data, config, \ - level, prio, api, \ + DEVICE_DT_NAME(node_id), init_fn, deinit_fn, \ + Z_DEVICE_DT_FLAGS(node_id), pm, data, config, level, \ + prio, api, \ &Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)), \ __VA_ARGS__) +/** + * @brief Create a device object from a devicetree node identifier and set it up + * for boot time initialization. + * + * @see DEVICE_DT_DEINIT_DEFINE() + */ +#define DEVICE_DT_DEFINE(node_id, init_fn, pm, data, config, level, prio, api, \ + ...) \ + DEVICE_DT_DEINIT_DEFINE(node_id, init_fn, NULL, pm, data, config, \ + level, prio, api, __VA_ARGS__) + +/** + * @brief Like DEVICE_DT_DEINIT_DEFINE(), but uses an instance of a + * `DT_DRV_COMPAT` compatible instead of a node identifier. + * + * @param inst Instance number. The `node_id` argument to DEVICE_DT_DEFINE() is + * set to `DT_DRV_INST(inst)`. + * @param ... Other parameters as expected by DEVICE_DT_DEFINE(). + */ +#define DEVICE_DT_INST_DEINIT_DEFINE(inst, ...) \ + DEVICE_DT_DEINIT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__) + /** * @brief Like DEVICE_DT_DEFINE(), but uses an instance of a `DT_DRV_COMPAT` * compatible instead of a node identifier. @@ -447,6 +477,27 @@ struct device_dt_metadata; #define Z_DEVICE_DEPS_CONST const #endif +/** Device flags */ +typedef uint8_t device_flags_t; + +/** + * @name Device flags + * @{ + */ + +/** Device initialization is deferred */ +#define DEVICE_FLAG_INIT_DEFERRED BIT(0) + +/** @} */ + +/** Device operations */ +struct device_ops { + /** Initialization function */ + int (*init)(const struct device *dev); + /** De-initialization function */ + int (*deinit)(const struct device *dev); +}; + /** * @brief Runtime device structure (in ROM) per driver instance */ @@ -461,6 +512,10 @@ struct device { struct device_state *state; /** Address of the device instance private data */ void *data; + /** Device operations */ + struct device_ops ops; + /** Device flags */ + device_flags_t flags; #if defined(CONFIG_DEVICE_DEPS) || defined(__DOXYGEN__) /** * Optional pointer to dependencies associated with the device. @@ -809,17 +864,35 @@ __syscall bool device_is_ready(const struct device *dev); * * 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. + * this call. De-initialized devices can also be initialized again via this + * call. * * @param dev device to be initialized. * - * @retval -ENOENT If device was not found - or isn't a deferred one. + * @retval -EALREADY Device is already initialized. * @retval -errno For other errors. */ __syscall int device_init(const struct device *dev); +/** + * @brief De-initialize a device. + * + * When a device is de-initialized, it will release any resources it has + * acquired (e.g. pins, memory, clocks, DMA channels, etc.) and its status will + * be left as in its reset state. + * + * @warning It is the responsability of the caller to ensure that the device is + * ready to be de-initialized. + * + * @param dev device to be de-initialized. + * + * @retval 0 If successful + * @retval -EPERM If device has not been initialized. + * @retval -ENOTSUP If device does not support de-initialization. + * @retval -errno For any other errors. + */ +__syscall int device_deinit(const struct device *dev); + /** * @} */ @@ -841,6 +914,14 @@ __syscall int device_init(const struct device *dev); static Z_DECL_ALIGN(struct device_state) Z_DEVICE_STATE_NAME(dev_id) \ __attribute__((__section__(".z_devstate"))) +/** + * @brief Device flags obtained from DT. + * + * @param node_id Devicetree node identifier. + */ +#define Z_DEVICE_DT_FLAGS(node_id) \ + (DT_PROP_OR(node_id, zephyr_deferred_init, 0U) * DEVICE_FLAG_INIT_DEFERRED) + #if defined(CONFIG_DEVICE_DEPS) || defined(__DOXYGEN__) /** @@ -1055,6 +1136,9 @@ device_get_dt_nodelabels(const struct device *dev) * @brief Initializer for @ref device. * * @param name_ Name of the device. + * @param init_fn_ Init function (optional). + * @param deinit_fn_ De-init function (optional). + * @param flags_ Device flags. * @param pm_ Reference to @ref pm_device_base (optional). * @param data_ Reference to device data. * @param config_ Reference to device config. @@ -1064,14 +1148,16 @@ device_get_dt_nodelabels(const struct device *dev) * @param node_id_ Devicetree node identifier * @param dev_id_ Device identifier token, as passed to Z_DEVICE_BASE_DEFINE */ -#define Z_DEVICE_INIT(name_, pm_, data_, config_, api_, state_, deps_, node_id_, \ - dev_id_) \ +#define Z_DEVICE_INIT(name_, init_fn_, deinit_fn_, flags_, pm_, data_, config_, api_, \ + state_, deps_, node_id_, dev_id_) \ { \ .name = name_, \ .config = (config_), \ .api = (api_), \ .state = (state_), \ .data = (data_), \ + .ops = { .init = (init_fn_), .deinit = (deinit_fn_) }, \ + .flags = (flags_), \ IF_ENABLED(CONFIG_DEVICE_DEPS, (.deps = (deps_),)) /**/ \ IF_ENABLED(CONFIG_PM_DEVICE, Z_DEVICE_INIT_PM_BASE(pm_)) /**/ \ IF_ENABLED(CONFIG_DEVICE_DT_METADATA, \ @@ -1107,6 +1193,9 @@ device_get_dt_nodelabels(const struct device *dev) * software device). * @param dev_id Device identifier (used to name the defined @ref device). * @param name Name of the device. + * @param init_fn Init function. + * @param deinit_fn De-init function. + * @param flags Device flags. * @param pm Reference to @ref pm_device_base associated with the device. * (optional). * @param data Reference to device data. @@ -1116,14 +1205,15 @@ device_get_dt_nodelabels(const struct device *dev) * @param api Reference to device API. * @param ... Optional dependencies, manually specified. */ -#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, pm, data, config, level, prio, api, state, \ - deps) \ +#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, deinit_fn, flags, pm, data, config, \ + level, prio, api, state, deps) \ COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \ COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (), (const)) \ STRUCT_SECTION_ITERABLE_NAMED_ALTERNATE( \ device, COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (device_mutable), (device)), \ Z_DEVICE_SECTION_NAME(level, prio), DEVICE_NAME_GET(dev_id)) = \ - Z_DEVICE_INIT(name, pm, data, config, api, state, deps, node_id, dev_id) + Z_DEVICE_INIT(name, init_fn, deinit_fn, flags, pm, data, config, api, state, deps, \ + node_id, dev_id) /** * @brief Issue an error if the given init level is not supported. @@ -1142,43 +1232,17 @@ device_get_dt_nodelabels(const struct device *dev) * @param node_id Devicetree node id for the device (DT_INVALID_NODE if a * software device). * @param dev_id Device identifier. - * @param init_fn_ Device init function. * @param level Initialization level. * @param prio Initialization priority. */ -#define Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn_, level, prio) \ +#define Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, level, prio) \ Z_DEVICE_CHECK_INIT_LEVEL(level) \ \ static const Z_DECL_ALIGN(struct init_entry) __used __noasan Z_INIT_ENTRY_SECTION( \ level, prio, Z_DEVICE_INIT_SUB_PRIO(node_id)) \ 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_)}, \ - Z_DEVICE_INIT_ENTRY_DEV(node_id, dev_id), \ - } - -#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_)}, \ - Z_DEVICE_INIT_ENTRY_DEV(node_id, dev_id), \ - } - -/* - * Anonymous unions require C11. Some pre-C11 gcc versions have early support for anonymous - * unions but they require these braces when combined with C99 designated initializers. For - * more details see https://docs.zephyrproject.org/latest/develop/languages/cpp/ - */ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__) < 201100 -# define Z_DEVICE_INIT_ENTRY_DEV(node_id, dev_id) { Z_DEV_ENTRY_DEV(node_id, dev_id) } -#else -# define Z_DEVICE_INIT_ENTRY_DEV(node_id, dev_id) Z_DEV_ENTRY_DEV(node_id, dev_id) -#endif - -#define Z_DEV_ENTRY_DEV(node_id, dev_id) \ - COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), (.dev_rw), (.dev)) = &DEVICE_NAME_GET(dev_id) + .dev = (const struct device *)&DEVICE_NAME_GET(dev_id) \ + } /** * @brief Define a @ref device and all other required objects. @@ -1191,6 +1255,7 @@ device_get_dt_nodelabels(const struct device *dev) * @param dev_id Device identifier (used to name the defined @ref device). * @param name Name of the device. * @param init_fn Device init function. + * @param flags Device flags. * @param pm Reference to @ref pm_device_base associated with the device. * (optional). * @param data Reference to device data. @@ -1201,8 +1266,8 @@ device_get_dt_nodelabels(const struct device *dev) * @param state Reference to device state. * @param ... Optional dependencies, manually specified. */ -#define Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, pm, data, config, \ - level, prio, api, state, ...) \ +#define Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, deinit_fn, flags, pm, \ + data, config, level, prio, api, state, ...) \ Z_DEVICE_NAME_CHECK(name); \ \ IF_ENABLED(CONFIG_DEVICE_DEPS, \ @@ -1212,13 +1277,12 @@ device_get_dt_nodelabels(const struct device *dev) (IF_ENABLED(DT_NODE_EXISTS(node_id), \ (Z_DEVICE_DT_METADATA_DEFINE(node_id, dev_id);))))\ \ - Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, pm, data, config, level, \ - prio, api, state, Z_DEVICE_DEPS_NAME(dev_id)); \ - 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))); \ + Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, deinit_fn, flags, \ + pm, data, config, level, prio, api, state, \ + Z_DEVICE_DEPS_NAME(dev_id)); \ + \ + Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, level, prio); \ + \ IF_ENABLED(CONFIG_LLEXT_EXPORT_DEVICES, \ (IF_ENABLED(DT_NODE_EXISTS(node_id), \ (Z_DEVICE_EXPORT(node_id);)))) diff --git a/include/zephyr/drivers/can.h b/include/zephyr/drivers/can.h index 5f32b8c3b4867..edebeed3fff41 100644 --- a/include/zephyr/drivers/can.h +++ b/include/zephyr/drivers/can.h @@ -761,7 +761,8 @@ struct can_device_state { Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \ DEVICE_DT_NAME(node_id), \ &UTIL_CAT(Z_DEVICE_DT_DEV_ID(node_id), _init), \ - pm, data, config, level, prio, api, \ + NULL, Z_DEVICE_DT_FLAGS(node_id), pm, data, \ + config, level, prio, api, \ &(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)).devstate), \ __VA_ARGS__) diff --git a/include/zephyr/drivers/i2c.h b/include/zephyr/drivers/i2c.h index 5f1c3fbe8ae4d..5e2fd42480ef9 100644 --- a/include/zephyr/drivers/i2c.h +++ b/include/zephyr/drivers/i2c.h @@ -668,7 +668,8 @@ static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \ DEVICE_DT_NAME(node_id), \ &UTIL_CAT(Z_DEVICE_DT_DEV_ID(node_id), _init), \ - pm, data, config, level, prio, api, \ + NULL, Z_DEVICE_DT_FLAGS(node_id), pm, data, \ + config, level, prio, api, \ &(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)).devstate), \ __VA_ARGS__) diff --git a/include/zephyr/drivers/smbus.h b/include/zephyr/drivers/smbus.h index f0642d6d837e4..de197becbc2a5 100644 --- a/include/zephyr/drivers/smbus.h +++ b/include/zephyr/drivers/smbus.h @@ -510,7 +510,7 @@ static inline void smbus_xfer_stats(const struct device *dev, uint8_t sent, Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id), \ DEVICE_DT_NAME(node_id), \ &UTIL_CAT(Z_DEVICE_DT_DEV_NAME(node_id), _init),\ - pm_device, \ + NULL, Z_DEVICE_DT_FLAGS(node_id), pm_device, \ data_ptr, cfg_ptr, level, prio, \ api_ptr, \ &(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_NAME \ diff --git a/include/zephyr/drivers/spi.h b/include/zephyr/drivers/spi.h index c9aaf38c72065..ba11b5e73c612 100644 --- a/include/zephyr/drivers/spi.h +++ b/include/zephyr/drivers/spi.h @@ -579,7 +579,7 @@ struct spi_device_state { Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \ DEVICE_DT_NAME(node_id), \ &UTIL_CAT(Z_DEVICE_DT_DEV_ID(node_id), _init), \ - pm_device, \ + NULL, Z_DEVICE_DT_FLAGS(node_id), pm_device, \ data_ptr, cfg_ptr, level, prio, \ api_ptr, \ &(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)).devstate), \ @@ -614,7 +614,8 @@ static inline void spi_transceive_stats(const struct device *dev, int error, api, ...) \ Z_DEVICE_STATE_DEFINE(Z_DEVICE_DT_DEV_ID(node_id)); \ Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \ - DEVICE_DT_NAME(node_id), init_fn, pm, data, config, \ + DEVICE_DT_NAME(node_id), init_fn, NULL, \ + Z_DEVICE_DT_FLAGS(node_id), pm, data, config, \ level, prio, api, \ &Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_ID(node_id)), \ __VA_ARGS__) diff --git a/include/zephyr/init.h b/include/zephyr/init.h index 512ea9f67bea6..8d476e1a6456c 100644 --- a/include/zephyr/init.h +++ b/include/zephyr/init.h @@ -49,43 +49,6 @@ extern "C" { struct device; -/** - * @brief Initialization function for init entries. - * - * Init entries support both the system initialization and the device - * APIs. Each API has its own init function signature; hence, we have a - * union to cover both. - */ -union init_function { - /** - * System initialization function. - * - * @retval 0 On success - * @retval -errno If init fails. - */ - int (*sys)(void); - /** - * Device initialization function. - * - * @param dev Device instance. - * - * @retval 0 On success - * @retval -errno If device initialization fails. - */ - int (*dev)(const struct device *dev); -#ifdef CONFIG_DEVICE_MUTABLE - /** - * Device initialization function (rw). - * - * @param dev Device instance. - * - * @retval 0 On success - * @retval -errno If device initialization fails. - */ - int (*dev_rw)(struct device *dev); -#endif -}; - /** * @brief Structure to store initialization entry information. * @@ -101,18 +64,16 @@ union init_function { * @endinternal */ struct init_entry { - /** Initialization function. */ - union init_function init_fn; + /** + * If the init function belongs to a SYS_INIT, this field stored the + * initialization function, otherwise it is set to NULL. + */ + int (*init_fn)(void); /** * If the init entry belongs to a device, this fields stores a * reference to it, otherwise it is set to NULL. */ - union { - const struct device *dev; -#ifdef CONFIG_DEVICE_MUTABLE - struct device *dev_rw; -#endif - }; + const struct device *dev; }; /** @cond INTERNAL_HIDDEN */ @@ -151,39 +112,6 @@ struct init_entry { __attribute__((__section__( \ ".z_init_" #level STRINGIFY(prio)"_" STRINGIFY(sub_prio)"_"))) - -/* Designated initializers where added to C in C99. There were added to - * C++ 20 years later in a much more restricted form. C99 allows many - * variations: out of order, mix of designated and not, overlap, - * override,... but C++ allows none of these. See differences detailed - * in the P0329R0.pdf C++ proposal. - * Note __STDC_VERSION__ is undefined when compiling C++. - */ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__) < 201100 - -/* Anonymous unions require C11. Some pre-C11 gcc versions have early - * support for anonymous unions but they require these braces when - * combined with C99 designated initializers, see longer discussion in - * #69411. - * These braces are compatible with any C version but not with C++20. - */ -# define Z_INIT_SYS_INIT_DEV_NULL { .dev = NULL } - -#else - -/* When using -std=c++20 or higher, g++ (v12.2.0) reject braces for - * initializing anonymous unions because it is technically a mix of - * designated and not designated initializers which is not allowed in - * C++. Interestingly, the _same_ g++ version does accept the braces above - * when using -std=c++17 or lower! - * The tests/lib/cpp/cxx/ added by commit 3d9c428d57bf invoke the C++ - * compiler with a range of different `-std=...` parameters without needing - * any manual configuration. - */ -# define Z_INIT_SYS_INIT_DEV_NULL .dev = NULL - -#endif - /** @endcond */ /** @@ -238,8 +166,7 @@ struct init_entry { #define SYS_INIT_NAMED(name, init_fn_, level, prio) \ static const Z_DECL_ALIGN(struct init_entry) \ Z_INIT_ENTRY_SECTION(level, prio, 0) __used __noasan \ - Z_INIT_ENTRY_NAME(name) = {.init_fn = {.sys = (init_fn_)}, \ - Z_INIT_SYS_INIT_DEV_NULL} + Z_INIT_ENTRY_NAME(name) = {.init_fn = (init_fn_)} \ /** @} */ 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 c6920b5bec0fa..72a160531ac24 100644 --- a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld +++ b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld @@ -18,9 +18,6 @@ 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, Z_LINK_ITERABLE_SUBALIGN) diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index 85856eafa701f..7bd343ba2914b 100644 --- a/include/zephyr/net/ethernet.h +++ b/include/zephyr/net/ethernet.h @@ -1171,7 +1171,8 @@ static inline bool net_eth_is_vlan_interface(struct net_if *iface) init_fn, pm, data, config, prio, \ api, mtu) \ Z_DEVICE_STATE_DEFINE(dev_id); \ - Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, pm, data, \ + Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, NULL, \ + Z_DEVICE_DT_FLAGS(node_id), pm, data, \ config, POST_KERNEL, prio, api, \ &Z_DEVICE_STATE_NAME(dev_id)); diff --git a/include/zephyr/net/net_if.h b/include/zephyr/net/net_if.h index c8cbd4496aeda..6194e583428dc 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -3331,7 +3331,8 @@ extern int net_stats_prometheus_scrape(struct prometheus_collector *collector, init_fn, pm, data, config, prio, \ api, l2, l2_ctx_type, mtu) \ Z_DEVICE_STATE_DEFINE(dev_id); \ - Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, pm, data, \ + Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, NULL, \ + Z_DEVICE_DT_FLAGS(node_id), pm, data, \ config, POST_KERNEL, prio, api, \ &Z_DEVICE_STATE_NAME(dev_id)); \ NET_L2_DATA_INIT(dev_id, instance, l2_ctx_type); \ @@ -3478,7 +3479,8 @@ extern int net_stats_prometheus_scrape(struct prometheus_collector *collector, #define Z_NET_DEVICE_OFFLOAD_INIT(node_id, dev_id, name, init_fn, pm, \ data, config, prio, api, mtu) \ Z_DEVICE_STATE_DEFINE(dev_id); \ - Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, pm, data, \ + Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, NULL, \ + Z_DEVICE_DT_FLAGS(node_id), pm, data, \ config, POST_KERNEL, prio, api, \ &Z_DEVICE_STATE_NAME(dev_id)); \ NET_IF_OFFLOAD_INIT(dev_id, 0, mtu) diff --git a/kernel/device.c b/kernel/device.c index 7e8822695802a..ad97b0b040998 100644 --- a/kernel/device.c +++ b/kernel/device.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -142,6 +143,38 @@ bool z_impl_device_is_ready(const struct device *dev) return dev->state->initialized && (dev->state->init_res == 0U); } +int z_impl_device_deinit(const struct device *dev) +{ + int ret; + + if (!dev->state->initialized) { + return -EPERM; + } + + if (dev->ops.deinit == NULL) { + return -ENOTSUP; + } + + ret = dev->ops.deinit(dev); + if (ret < 0) { + return ret; + } + + dev->state->initialized = false; + + return 0; +} + +#ifdef CONFIG_USERSPACE +static inline int z_vrfy_device_deinit(const struct device *dev) +{ + K_OOPS(K_SYSCALL_OBJ_INIT(dev, K_OBJ_ANY)); + + return z_impl_device_deinit(dev); +} +#include +#endif + #ifdef CONFIG_DEVICE_DEPS static int device_visitor(const device_handle_t *handles, diff --git a/kernel/init.c b/kernel/init.c index d6c4fb3c70cca..564e19da1a080 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -302,13 +302,12 @@ extern volatile uintptr_t __stack_chk_guard; __pinned_bss bool z_sys_post_kernel; -static int do_device_init(const struct init_entry *entry) +static int do_device_init(const struct device *dev) { - const struct device *dev = entry->dev; int rc = 0; - if (entry->init_fn.dev != NULL) { - rc = entry->init_fn.dev(dev); + if (dev->ops.init != NULL) { + rc = dev->ops.init(dev); /* Mark device initialized. If initialization * failed, record the error condition. */ @@ -362,13 +361,15 @@ static void z_sys_init_run_level(enum init_level level) for (entry = levels[level]; entry < levels[level+1]; entry++) { const struct device *dev = entry->dev; - int result; + int result = 0; sys_trace_sys_init_enter(entry, level); if (dev != NULL) { - result = do_device_init(entry); + if ((dev->flags & DEVICE_FLAG_INIT_DEFERRED) == 0U) { + result = do_device_init(dev); + } } else { - result = entry->init_fn.sys(); + result = entry->init_fn(); } sys_trace_sys_init_exit(entry, level, result); } @@ -377,17 +378,11 @@ static void z_sys_init_run_level(enum init_level level) 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); - } + if (dev->state->initialized) { + return -EALREADY; } - return -ENOENT; + return do_device_init(dev); } #ifdef CONFIG_USERSPACE diff --git a/tests/lib/devicetree/devices/src/main.c b/tests/lib/devicetree/devices/src/main.c index 0c9cf699406fe..291a4a4785055 100644 --- a/tests/lib/devicetree/devices/src/main.c +++ b/tests/lib/devicetree/devices/src/main.c @@ -86,16 +86,16 @@ ZTEST(devicetree_devices, test_init_get) DEVICE_DT_GET(TEST_NOLABEL), NULL); /* Check init functions */ - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO)->init_fn.dev, dev_init); - zassert_equal(DEVICE_INIT_DT_GET(TEST_I2C)->init_fn.dev, dev_init); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVA)->init_fn.dev, dev_init); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVB)->init_fn.dev, dev_init); - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIOX)->init_fn.dev, dev_init); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVC)->init_fn.dev, dev_init); - zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION)->init_fn.dev, dev_init); - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO_INJECTED)->init_fn.dev, dev_init); - zassert_equal(DEVICE_INIT_GET(manual_dev)->init_fn.dev, dev_init); - zassert_equal(DEVICE_INIT_DT_GET(TEST_NOLABEL)->init_fn.dev, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_GPIO)->ops.init, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_I2C)->ops.init, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_DEVA)->ops.init, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_DEVB)->ops.init, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_GPIOX)->ops.init, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_DEVC)->ops.init, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_PARTITION)->ops.init, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_GPIO_INJECTED)->ops.init, dev_init); + zassert_equal(DEVICE_GET(manual_dev)->ops.init, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_NOLABEL)->ops.init, dev_init); } ZTEST(devicetree_devices, test_init_order)