From 90582fde4a1fd0e17f856fadcb2b0824e1f845f7 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Thu, 9 Jan 2025 17:41:25 +0100 Subject: [PATCH 01/10] init: drop anonymous union It just complicates things. It is not C99 strandard, and since C11 is not mandatory, it is better to play safe here. Signed-off-by: Gerard Marull-Paretas --- include/zephyr/device.h | 34 +++++++++---------------- include/zephyr/init.h | 2 +- kernel/init.c | 6 ++--- tests/lib/devicetree/devices/src/main.c | 20 +++++++-------- 4 files changed, 26 insertions(+), 36 deletions(-) diff --git a/include/zephyr/device.h b/include/zephyr/device.h index 57c628b94e94e..c7536a45a3d2f 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -1152,33 +1152,23 @@ device_get_dt_nodelabels(const struct device *dev) 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), \ - } + COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), \ + (.init_fn = { .dev_rw = init_fn_ }, \ + .dev = { .dev_rw = &DEVICE_NAME_GET(dev_id)}), \ + (.init_fn = { .dev = init_fn_ }, \ + .dev = { .dev = &DEVICE_NAME_GET(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) + COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), \ + (.init_fn = { .dev_rw = init_fn_ }, \ + .dev = { .dev_rw = &DEVICE_NAME_GET(dev_id)}), \ + (.init_fn = { .dev = init_fn_ }, \ + .dev = { .dev = &DEVICE_NAME_GET(dev_id)})) \ + } /** * @brief Define a @ref device and all other required objects. diff --git a/include/zephyr/init.h b/include/zephyr/init.h index 512ea9f67bea6..c8c705be7f521 100644 --- a/include/zephyr/init.h +++ b/include/zephyr/init.h @@ -112,7 +112,7 @@ struct init_entry { #ifdef CONFIG_DEVICE_MUTABLE struct device *dev_rw; #endif - }; + } dev; }; /** @cond INTERNAL_HIDDEN */ diff --git a/kernel/init.c b/kernel/init.c index d6c4fb3c70cca..1cae55555d8d9 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -304,7 +304,7 @@ bool z_sys_post_kernel; static int do_device_init(const struct init_entry *entry) { - const struct device *dev = entry->dev; + const struct device *dev = entry->dev.dev; int rc = 0; if (entry->init_fn.dev != NULL) { @@ -361,7 +361,7 @@ static void z_sys_init_run_level(enum init_level level) const struct init_entry *entry; for (entry = levels[level]; entry < levels[level+1]; entry++) { - const struct device *dev = entry->dev; + const struct device *dev = entry->dev.dev; int result; sys_trace_sys_init_enter(entry, level); @@ -382,7 +382,7 @@ int z_impl_device_init(const struct device *dev) } STRUCT_SECTION_FOREACH_ALTERNATE(_deferred_init, init_entry, entry) { - if (entry->dev == dev) { + if (entry->dev.dev == dev) { return do_device_init(entry); } } diff --git a/tests/lib/devicetree/devices/src/main.c b/tests/lib/devicetree/devices/src/main.c index 0c9cf699406fe..4aaf02fded2a7 100644 --- a/tests/lib/devicetree/devices/src/main.c +++ b/tests/lib/devicetree/devices/src/main.c @@ -64,25 +64,25 @@ DEVICE_DT_DEFINE(TEST_NOLABEL, dev_init, NULL, ZTEST(devicetree_devices, test_init_get) { /* Check device pointers */ - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO)->dev.dev, DEVICE_DT_GET(TEST_GPIO), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_I2C)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_I2C)->dev.dev, DEVICE_DT_GET(TEST_I2C), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVA)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVA)->dev.dev, DEVICE_DT_GET(TEST_DEVA), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVB)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVB)->dev.dev, DEVICE_DT_GET(TEST_DEVB), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIOX)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIOX)->dev.dev, DEVICE_DT_GET(TEST_GPIOX), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVC)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVC)->dev.dev, DEVICE_DT_GET(TEST_DEVC), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION)->dev.dev, DEVICE_DT_GET(TEST_PARTITION), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO_INJECTED)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO_INJECTED)->dev.dev, DEVICE_DT_GET(TEST_GPIO_INJECTED), NULL); - zassert_equal(DEVICE_INIT_GET(manual_dev)->dev, + zassert_equal(DEVICE_INIT_GET(manual_dev)->dev.dev, DEVICE_GET(manual_dev), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_NOLABEL)->dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_NOLABEL)->dev.dev, DEVICE_DT_GET(TEST_NOLABEL), NULL); /* Check init functions */ From 5cfa14db2c83dd4e593f86e0166c489f7f4b6371 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Thu, 9 Jan 2025 17:32:34 +0100 Subject: [PATCH 02/10] device: store init function in struct device Preparation work for device init/de-init functionality. Signed-off-by: Gerard Marull-Paretas --- include/zephyr/device.h | 42 ++++++++++++------------- kernel/init.c | 11 +++---- tests/lib/devicetree/devices/src/main.c | 20 ++++++------ 3 files changed, 35 insertions(+), 38 deletions(-) diff --git a/include/zephyr/device.h b/include/zephyr/device.h index c7536a45a3d2f..d8394ef75a6b6 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -461,6 +461,8 @@ struct device { struct device_state *state; /** Address of the device instance private data */ void *data; + /** Initialization function (optional) */ + int (*init_fn)(const struct device *); #if defined(CONFIG_DEVICE_DEPS) || defined(__DOXYGEN__) /** * Optional pointer to dependencies associated with the device. @@ -1055,6 +1057,7 @@ 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 pm_ Reference to @ref pm_device_base (optional). * @param data_ Reference to device data. * @param config_ Reference to device config. @@ -1064,14 +1067,15 @@ 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_, pm_, data_, config_, api_, state_, deps_, \ + node_id_, dev_id_) \ { \ .name = name_, \ .config = (config_), \ .api = (api_), \ .state = (state_), \ .data = (data_), \ + .init_fn = (init_fn_), \ 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 +1111,7 @@ 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 pm Reference to @ref pm_device_base associated with the device. * (optional). * @param data Reference to device data. @@ -1116,14 +1121,14 @@ 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, 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, pm, data, config, api, state, deps, node_id, dev_id) /** * @brief Issue an error if the given init level is not supported. @@ -1142,32 +1147,27 @@ 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)) = { \ COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), \ - (.init_fn = { .dev_rw = init_fn_ }, \ - .dev = { .dev_rw = &DEVICE_NAME_GET(dev_id)}), \ - (.init_fn = { .dev = init_fn_ }, \ - .dev = { .dev = &DEVICE_NAME_GET(dev_id)})) \ + (.dev = { .dev_rw = &DEVICE_NAME_GET(dev_id)}), \ + (.dev = { .dev = &DEVICE_NAME_GET(dev_id)})) \ } -#define Z_DEFER_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, init_fn_) \ +#define Z_DEFER_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id) \ static const Z_DECL_ALIGN(struct init_entry) __used __noasan \ __attribute__((__section__(".z_deferred_init"))) \ Z_INIT_ENTRY_NAME(DEVICE_NAME_GET(dev_id)) = { \ COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), \ - (.init_fn = { .dev_rw = init_fn_ }, \ - .dev = { .dev_rw = &DEVICE_NAME_GET(dev_id)}), \ - (.init_fn = { .dev = init_fn_ }, \ - .dev = { .dev = &DEVICE_NAME_GET(dev_id)})) \ + (.dev = { .dev_rw = &DEVICE_NAME_GET(dev_id)}), \ + (.dev = { .dev = &DEVICE_NAME_GET(dev_id)})) \ } /** @@ -1202,13 +1202,11 @@ 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)); \ + Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_fn, 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_DEFER_DEVICE_INIT_ENTRY_DEFINE(node_id, 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/kernel/init.c b/kernel/init.c index 1cae55555d8d9..222b6ff021c41 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.dev; int rc = 0; - if (entry->init_fn.dev != NULL) { - rc = entry->init_fn.dev(dev); + if (dev->init_fn != NULL) { + rc = dev->init_fn(dev); /* Mark device initialized. If initialization * failed, record the error condition. */ @@ -366,7 +365,7 @@ static void z_sys_init_run_level(enum init_level level) sys_trace_sys_init_enter(entry, level); if (dev != NULL) { - result = do_device_init(entry); + result = do_device_init(dev); } else { result = entry->init_fn.sys(); } @@ -383,7 +382,7 @@ int z_impl_device_init(const struct device *dev) STRUCT_SECTION_FOREACH_ALTERNATE(_deferred_init, init_entry, entry) { if (entry->dev.dev == dev) { - return do_device_init(entry); + return do_device_init(dev); } } diff --git a/tests/lib/devicetree/devices/src/main.c b/tests/lib/devicetree/devices/src/main.c index 4aaf02fded2a7..d40917077c6c7 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)->init_fn, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_I2C)->init_fn, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_DEVA)->init_fn, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_DEVB)->init_fn, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_GPIOX)->init_fn, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_DEVC)->init_fn, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_PARTITION)->init_fn, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_GPIO_INJECTED)->init_fn, dev_init); + zassert_equal(DEVICE_GET(manual_dev)->init_fn, dev_init); + zassert_equal(DEVICE_DT_GET(TEST_NOLABEL)->init_fn, dev_init); } ZTEST(devicetree_devices, test_init_order) From 1ff7288b8c9d5dd737c052eda401e24e9ac37616 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Thu, 9 Jan 2025 17:52:14 +0100 Subject: [PATCH 03/10] init: drop init_fn union Device init function is no longer taken from `struct init_entry`, so there's no need to keep such union. Signed-off-by: Gerard Marull-Paretas --- include/zephyr/init.h | 80 ++++--------------------------------------- kernel/init.c | 2 +- 2 files changed, 7 insertions(+), 75 deletions(-) diff --git a/include/zephyr/init.h b/include/zephyr/init.h index c8c705be7f521..d071445a0aae7 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,8 +64,11 @@ 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. @@ -151,39 +117,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 +171,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/kernel/init.c b/kernel/init.c index 222b6ff021c41..6eeed757c6db6 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -367,7 +367,7 @@ static void z_sys_init_run_level(enum init_level level) if (dev != NULL) { result = do_device_init(dev); } else { - result = entry->init_fn.sys(); + result = entry->init_fn(); } sys_trace_sys_init_exit(entry, level, result); } From af7b745dbdc87ff61ac83a3f940ba8e22f28c36a Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Thu, 9 Jan 2025 17:57:17 +0100 Subject: [PATCH 04/10] init: drop device union from struct init_entry Such union is rather redundant, considering a simple const cast can be done when initializing the init entry. Note that the init_entry does not need to be touched now that struct device stores the init call. It is merely an init entry sorted by linker scripts, so we can intertwine devices and SYS_INIT. Signed-off-by: Gerard Marull-Paretas --- include/zephyr/device.h | 8 ++------ include/zephyr/init.h | 7 +------ kernel/init.c | 4 ++-- tests/lib/devicetree/devices/src/main.c | 20 ++++++++++---------- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/include/zephyr/device.h b/include/zephyr/device.h index d8394ef75a6b6..e0ba141fac0d2 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -1156,18 +1156,14 @@ device_get_dt_nodelabels(const struct device *dev) 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)) = { \ - COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), \ - (.dev = { .dev_rw = &DEVICE_NAME_GET(dev_id)}), \ - (.dev = { .dev = &DEVICE_NAME_GET(dev_id)})) \ + .dev = (const struct device *)&DEVICE_NAME_GET(dev_id) \ } #define Z_DEFER_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id) \ static const Z_DECL_ALIGN(struct init_entry) __used __noasan \ __attribute__((__section__(".z_deferred_init"))) \ Z_INIT_ENTRY_NAME(DEVICE_NAME_GET(dev_id)) = { \ - COND_CODE_1(Z_DEVICE_IS_MUTABLE(node_id), \ - (.dev = { .dev_rw = &DEVICE_NAME_GET(dev_id)}), \ - (.dev = { .dev = &DEVICE_NAME_GET(dev_id)})) \ + .dev = (const struct device *)&DEVICE_NAME_GET(dev_id) \ } /** diff --git a/include/zephyr/init.h b/include/zephyr/init.h index d071445a0aae7..8d476e1a6456c 100644 --- a/include/zephyr/init.h +++ b/include/zephyr/init.h @@ -73,12 +73,7 @@ struct init_entry { * 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 - } dev; + const struct device *dev; }; /** @cond INTERNAL_HIDDEN */ diff --git a/kernel/init.c b/kernel/init.c index 6eeed757c6db6..2c3b13deb35c6 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -360,7 +360,7 @@ static void z_sys_init_run_level(enum init_level level) const struct init_entry *entry; for (entry = levels[level]; entry < levels[level+1]; entry++) { - const struct device *dev = entry->dev.dev; + const struct device *dev = entry->dev; int result; sys_trace_sys_init_enter(entry, level); @@ -381,7 +381,7 @@ int z_impl_device_init(const struct device *dev) } STRUCT_SECTION_FOREACH_ALTERNATE(_deferred_init, init_entry, entry) { - if (entry->dev.dev == dev) { + if (entry->dev == dev) { return do_device_init(dev); } } diff --git a/tests/lib/devicetree/devices/src/main.c b/tests/lib/devicetree/devices/src/main.c index d40917077c6c7..6b4f700f82b15 100644 --- a/tests/lib/devicetree/devices/src/main.c +++ b/tests/lib/devicetree/devices/src/main.c @@ -64,25 +64,25 @@ DEVICE_DT_DEFINE(TEST_NOLABEL, dev_init, NULL, ZTEST(devicetree_devices, test_init_get) { /* Check device pointers */ - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO)->dev.dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO)->dev, DEVICE_DT_GET(TEST_GPIO), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_I2C)->dev.dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_I2C)->dev, DEVICE_DT_GET(TEST_I2C), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVA)->dev.dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVA)->dev, DEVICE_DT_GET(TEST_DEVA), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVB)->dev.dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVB)->dev, DEVICE_DT_GET(TEST_DEVB), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIOX)->dev.dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIOX)->dev, DEVICE_DT_GET(TEST_GPIOX), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVC)->dev.dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_DEVC)->dev, DEVICE_DT_GET(TEST_DEVC), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION)->dev.dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_PARTITION)->dev, DEVICE_DT_GET(TEST_PARTITION), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO_INJECTED)->dev.dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_GPIO_INJECTED)->dev, DEVICE_DT_GET(TEST_GPIO_INJECTED), NULL); - zassert_equal(DEVICE_INIT_GET(manual_dev)->dev.dev, + zassert_equal(DEVICE_INIT_GET(manual_dev)->dev, DEVICE_GET(manual_dev), NULL); - zassert_equal(DEVICE_INIT_DT_GET(TEST_NOLABEL)->dev.dev, + zassert_equal(DEVICE_INIT_DT_GET(TEST_NOLABEL)->dev, DEVICE_DT_GET(TEST_NOLABEL), NULL); /* Check init functions */ From 317849bd04950bcaf10d1f946a1e149fddb642ae Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Mon, 13 Jan 2025 10:06:54 +0100 Subject: [PATCH 05/10] device: introduce device flags Introduce a new field to store device flags. Only device deferred init flag has been added, replacing usage of linker hackery to know wether a device requires initialization at boot time or not. This change will be helpful in the near future as devices will become reference counted, so we will need to know wether they have been initialized or not. Signed-off-by: Gerard Marull-Paretas --- cmake/linker_script/common/common-rom.cmake | 3 - include/zephyr/device.h | 75 +++++++++++-------- include/zephyr/drivers/can.h | 3 +- include/zephyr/drivers/i2c.h | 3 +- include/zephyr/drivers/smbus.h | 2 +- include/zephyr/drivers/spi.h | 5 +- .../common-rom/common-rom-kernel-devices.ld | 3 - include/zephyr/net/ethernet.h | 3 +- include/zephyr/net/net_if.h | 6 +- kernel/init.c | 17 +++-- 10 files changed, 68 insertions(+), 52 deletions(-) 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/include/zephyr/device.h b/include/zephyr/device.h index e0ba141fac0d2..9c6358c1595ba 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -167,7 +167,7 @@ typedef int16_t device_handle_t; #define DEVICE_DEFINE(dev_id, name, init_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, \ + Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, name, init_fn, 0U, pm, data, \ config, level, prio, api, \ &Z_DEVICE_STATE_NAME(dev_id)) @@ -185,16 +185,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. @@ -230,8 +220,9 @@ typedef int16_t device_handle_t; ...) \ 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, \ + 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__) @@ -447,6 +438,19 @@ 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) + +/** @} */ + /** * @brief Runtime device structure (in ROM) per driver instance */ @@ -463,6 +467,8 @@ struct device { void *data; /** Initialization function (optional) */ int (*init_fn)(const struct device *); + /** Device flags */ + device_flags_t flags; #if defined(CONFIG_DEVICE_DEPS) || defined(__DOXYGEN__) /** * Optional pointer to dependencies associated with the device. @@ -843,6 +849,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__) /** @@ -1058,6 +1072,7 @@ device_get_dt_nodelabels(const struct device *dev) * * @param name_ Name of the device. * @param init_fn_ 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. @@ -1067,8 +1082,8 @@ 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_, init_fn_, pm_, data_, config_, api_, state_, deps_, \ - node_id_, dev_id_) \ +#define Z_DEVICE_INIT(name_, init_fn_, flags_, pm_, data_, config_, api_, state_, \ + deps_, node_id_, dev_id_) \ { \ .name = name_, \ .config = (config_), \ @@ -1076,6 +1091,7 @@ device_get_dt_nodelabels(const struct device *dev) .state = (state_), \ .data = (data_), \ .init_fn = (init_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, \ @@ -1112,6 +1128,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 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. @@ -1121,14 +1138,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, init_fn, pm, data, config, level, prio, api, \ - state, deps) \ +#define Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_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, init_fn, pm, data, config, api, state, deps, node_id, dev_id) + Z_DEVICE_INIT(name, init_fn, flags, pm, data, config, api, state, deps, node_id, \ + dev_id) /** * @brief Issue an error if the given init level is not supported. @@ -1159,13 +1177,6 @@ device_get_dt_nodelabels(const struct device *dev) .dev = (const struct device *)&DEVICE_NAME_GET(dev_id) \ } -#define Z_DEFER_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id) \ - static const Z_DECL_ALIGN(struct init_entry) __used __noasan \ - __attribute__((__section__(".z_deferred_init"))) \ - Z_INIT_ENTRY_NAME(DEVICE_NAME_GET(dev_id)) = { \ - .dev = (const struct device *)&DEVICE_NAME_GET(dev_id) \ - } - /** * @brief Define a @ref device and all other required objects. * @@ -1177,6 +1188,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. @@ -1187,7 +1199,7 @@ 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, \ +#define Z_DEVICE_DEFINE(node_id, dev_id, name, init_fn, flags, pm, data, config,\ level, prio, api, state, ...) \ Z_DEVICE_NAME_CHECK(name); \ \ @@ -1198,11 +1210,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, init_fn, 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)), \ - (Z_DEVICE_INIT_ENTRY_DEFINE(node_id, dev_id, level, prio)));\ + Z_DEVICE_BASE_DEFINE(node_id, dev_id, name, init_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..7938440cfebd5 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, \ + 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..f09958fac6f22 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, \ + 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..47369aa51e5e4 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, \ + 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..f6e3985ce658c 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, \ + 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, \ + 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/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..517f7178ea524 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, \ + 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..2e9a4e70b1ae0 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, \ + 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, \ + 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/init.c b/kernel/init.c index 2c3b13deb35c6..e3f0db4d3bca3 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -361,11 +361,13 @@ 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(dev); + if ((dev->flags & DEVICE_FLAG_INIT_DEFERRED) == 0U) { + result = do_device_init(dev); + } } else { result = entry->init_fn(); } @@ -376,12 +378,13 @@ 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; - } + const struct device *devs; + size_t devc; + + devc = z_device_get_all_static(&devs); - STRUCT_SECTION_FOREACH_ALTERNATE(_deferred_init, init_entry, entry) { - if (entry->dev == dev) { + for (const struct device *dev_ = devs; dev_ < (devs + devc); dev_++) { + if ((dev_ == dev) && ((dev->flags & DEVICE_FLAG_INIT_DEFERRED) != 0U)) { return do_device_init(dev); } } From 54b33a2f1136ff5a0c2e52b93b4d90b2357314b8 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Mon, 13 Jan 2025 10:32:04 +0100 Subject: [PATCH 06/10] device: introduce struct device_ops Instead of passing a single init function, create struct device_ops with the init function inside. This allows to easily extend device's capabilities in the future without too much breakage, e.g. to add a de-init call. Signed-off-by: Gerard Marull-Paretas --- include/zephyr/device.h | 12 +++++++++--- kernel/init.c | 4 ++-- tests/lib/devicetree/devices/src/main.c | 20 ++++++++++---------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/zephyr/device.h b/include/zephyr/device.h index 9c6358c1595ba..5d764b83b592c 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -451,6 +451,12 @@ typedef uint8_t device_flags_t; /** @} */ +/** Device operations */ +struct device_ops { + /** Initialization function */ + int (*init)(const struct device *dev); +}; + /** * @brief Runtime device structure (in ROM) per driver instance */ @@ -465,8 +471,8 @@ struct device { struct device_state *state; /** Address of the device instance private data */ void *data; - /** Initialization function (optional) */ - int (*init_fn)(const struct device *); + /** Device operations */ + struct device_ops ops; /** Device flags */ device_flags_t flags; #if defined(CONFIG_DEVICE_DEPS) || defined(__DOXYGEN__) @@ -1090,7 +1096,7 @@ device_get_dt_nodelabels(const struct device *dev) .api = (api_), \ .state = (state_), \ .data = (data_), \ - .init_fn = (init_fn_), \ + .ops = { .init = (init_fn_) }, \ .flags = (flags_), \ IF_ENABLED(CONFIG_DEVICE_DEPS, (.deps = (deps_),)) /**/ \ IF_ENABLED(CONFIG_PM_DEVICE, Z_DEVICE_INIT_PM_BASE(pm_)) /**/ \ diff --git a/kernel/init.c b/kernel/init.c index e3f0db4d3bca3..066b11bbf59e7 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -306,8 +306,8 @@ static int do_device_init(const struct device *dev) { int rc = 0; - if (dev->init_fn != NULL) { - rc = dev->init_fn(dev); + if (dev->ops.init != NULL) { + rc = dev->ops.init(dev); /* Mark device initialized. If initialization * failed, record the error condition. */ diff --git a/tests/lib/devicetree/devices/src/main.c b/tests/lib/devicetree/devices/src/main.c index 6b4f700f82b15..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_DT_GET(TEST_GPIO)->init_fn, dev_init); - zassert_equal(DEVICE_DT_GET(TEST_I2C)->init_fn, dev_init); - zassert_equal(DEVICE_DT_GET(TEST_DEVA)->init_fn, dev_init); - zassert_equal(DEVICE_DT_GET(TEST_DEVB)->init_fn, dev_init); - zassert_equal(DEVICE_DT_GET(TEST_GPIOX)->init_fn, dev_init); - zassert_equal(DEVICE_DT_GET(TEST_DEVC)->init_fn, dev_init); - zassert_equal(DEVICE_DT_GET(TEST_PARTITION)->init_fn, dev_init); - zassert_equal(DEVICE_DT_GET(TEST_GPIO_INJECTED)->init_fn, dev_init); - zassert_equal(DEVICE_GET(manual_dev)->init_fn, dev_init); - zassert_equal(DEVICE_DT_GET(TEST_NOLABEL)->init_fn, 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) From c924caea5a6d1e72443a97bf8c7a9d5861ea37de Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Mon, 13 Jan 2025 13:41:56 +0100 Subject: [PATCH 07/10] device: add new device_deinit API Add a new API to 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. It is the responsability of the caller to ensure that the device is ready to be de-initialized. For now, deinit call always initializes to NULL. New macros will be introduced to not break existing device APIs. Signed-off-by: Gerard Marull-Paretas --- include/zephyr/device.h | 51 ++++++++++++++++++++++++---------- include/zephyr/drivers/can.h | 4 +-- include/zephyr/drivers/i2c.h | 4 +-- include/zephyr/drivers/smbus.h | 2 +- include/zephyr/drivers/spi.h | 4 +-- include/zephyr/net/ethernet.h | 2 +- include/zephyr/net/net_if.h | 4 +-- kernel/device.c | 33 ++++++++++++++++++++++ 8 files changed, 80 insertions(+), 24 deletions(-) diff --git a/include/zephyr/device.h b/include/zephyr/device.h index 5d764b83b592c..fec9eed8ee56e 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -167,8 +167,8 @@ typedef int16_t device_handle_t; #define DEVICE_DEFINE(dev_id, name, init_fn, pm, data, config, level, prio, \ api) \ Z_DEVICE_STATE_DEFINE(dev_id); \ - Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, name, init_fn, 0U, pm, data, \ - config, level, prio, api, \ + Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, name, init_fn, NULL, 0U, pm, \ + data, config, level, prio, api, \ &Z_DEVICE_STATE_NAME(dev_id)) /** @@ -220,7 +220,7 @@ typedef int16_t device_handle_t; ...) \ 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, \ + 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)), \ @@ -455,6 +455,8 @@ typedef uint8_t device_flags_t; struct device_ops { /** Initialization function */ int (*init)(const struct device *dev); + /** De-initialization function */ + int (*deinit)(const struct device *dev); }; /** @@ -834,6 +836,25 @@ __syscall bool device_is_ready(const struct device *dev); */ __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); + /** * @} */ @@ -1078,6 +1099,7 @@ device_get_dt_nodelabels(const struct device *dev) * * @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. @@ -1088,15 +1110,15 @@ 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_, init_fn_, flags_, 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_) }, \ + .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_)) /**/ \ @@ -1134,6 +1156,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 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). @@ -1144,15 +1167,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, init_fn, flags, 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, init_fn, flags, 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. @@ -1205,8 +1228,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, flags, 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, \ @@ -1216,8 +1239,8 @@ 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, init_fn, flags, pm, data, \ - config, level, prio, api, state, \ + 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); \ diff --git a/include/zephyr/drivers/can.h b/include/zephyr/drivers/can.h index 7938440cfebd5..edebeed3fff41 100644 --- a/include/zephyr/drivers/can.h +++ b/include/zephyr/drivers/can.h @@ -761,8 +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), \ - Z_DEVICE_DT_FLAGS(node_id), 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 f09958fac6f22..5e2fd42480ef9 100644 --- a/include/zephyr/drivers/i2c.h +++ b/include/zephyr/drivers/i2c.h @@ -668,8 +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), \ - Z_DEVICE_DT_FLAGS(node_id), 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 47369aa51e5e4..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),\ - Z_DEVICE_DT_FLAGS(node_id), 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 f6e3985ce658c..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), \ - Z_DEVICE_DT_FLAGS(node_id), 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,7 @@ 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, \ + 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)), \ diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index 517f7178ea524..7bd343ba2914b 100644 --- a/include/zephyr/net/ethernet.h +++ b/include/zephyr/net/ethernet.h @@ -1171,7 +1171,7 @@ 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, \ + 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 2e9a4e70b1ae0..6194e583428dc 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -3331,7 +3331,7 @@ 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, \ + 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)); \ @@ -3479,7 +3479,7 @@ 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, \ + 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/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, From 122f45fa71d1a06b781690a0191d87cf2b5d93db Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Mon, 24 Feb 2025 11:12:15 +0100 Subject: [PATCH 08/10] device: allow initializing any device Remove restrictions from device_init by allowing to perform device initialization if the device state flags it being not initialized. This makes the API usable in contexts where device_deinit has been called before. Signed-off-by: Gerard Marull-Paretas --- include/zephyr/device.h | 7 +++---- kernel/init.c | 13 +++---------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/include/zephyr/device.h b/include/zephyr/device.h index fec9eed8ee56e..e4c7a6e938a10 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -825,13 +825,12 @@ __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); diff --git a/kernel/init.c b/kernel/init.c index 066b11bbf59e7..564e19da1a080 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -378,18 +378,11 @@ static void z_sys_init_run_level(enum init_level level) int z_impl_device_init(const struct device *dev) { - const struct device *devs; - size_t devc; - - devc = z_device_get_all_static(&devs); - - for (const struct device *dev_ = devs; dev_ < (devs + devc); dev_++) { - if ((dev_ == dev) && ((dev->flags & DEVICE_FLAG_INIT_DEFERRED) != 0U)) { - return do_device_init(dev); - } + if (dev->state->initialized) { + return -EALREADY; } - return -ENOENT; + return do_device_init(dev); } #ifdef CONFIG_USERSPACE From 550adc0a499eb3991057c3c70e584fb435944efb Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Thu, 13 Feb 2025 14:09:51 +0100 Subject: [PATCH 09/10] device: add DEVICE[_DT[_INST]_DEINIT_DEFINE Add a new set of macros to define devices with de-init capabilities. This allows to move gradually to de-init without introducing breaking changes. Signed-off-by: Gerard Marull-Paretas --- include/zephyr/device.h | 59 ++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/include/zephyr/device.h b/include/zephyr/device.h index e4c7a6e938a10..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, NULL, 0U, 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. * @@ -203,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. @@ -216,16 +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, NULL, \ + 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. From 20103668008d222817312830c5bdd214f1234de9 Mon Sep 17 00:00:00 2001 From: Gerard Marull-Paretas Date: Tue, 25 Feb 2025 17:37:37 +0100 Subject: [PATCH 10/10] drivers: misc: devmux: fix init signature It is no longer possible to use other signatures other than `int fn(const struct device *)`. Just cast the device pointer, safe in this context because CONFIG_DEVICE_MUTABLE=y. Signed-off-by: Gerard Marull-Paretas --- drivers/misc/devmux/devmux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; }