From c14ee1646370995c362bf744c116c47ce3c0da11 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Mon, 12 May 2025 12:41:34 +0200 Subject: [PATCH] device: Provide de-init device function only if requested This feature, fixing a specific corner case, is unilateraly growing all struct device with a pointer that 99% of the time is not used. Thus uselessly utilizing ROM. Making the feature Kconfig controlled. Signed-off-by: Tomasz Bursztyka --- include/zephyr/device.h | 26 ++++++++++++++++++++++++-- kernel/Kconfig.device | 10 ++++++++++ kernel/device.c | 5 +++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/include/zephyr/device.h b/include/zephyr/device.h index df2d47250c7..07fea3c2d1f 100644 --- a/include/zephyr/device.h +++ b/include/zephyr/device.h @@ -144,6 +144,8 @@ typedef int16_t device_handle_t; * device from a devicetree node, use DEVICE_DT_DEINIT_DEFINE() or * DEVICE_DT_INST_DEINIT_DEFINE() instead. * + * Note: deinit_fn will only be used if CONFIG_DEVICE_DEINIT_SUPPORT is enabled. + * * @param dev_id A unique token which is used in the name of the global device * structure as a C identifier. * @param name A string name for the device, which will be stored in @@ -214,6 +216,8 @@ typedef int16_t device_handle_t; * @kconfig{CONFIG_LLEXT_EXPORT_DEVICES} is enabled). Before using the * pointer, the referenced object should be checked using device_is_ready(). * + * Note: deinit_fn will only be used if CONFIG_DEVICE_DEINIT_SUPPORT is enabled. + * * @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`. @@ -494,8 +498,10 @@ typedef uint8_t device_flags_t; struct device_ops { /** Initialization function */ int (*init)(const struct device *dev); +#ifdef CONFIG_DEVICE_DEINIT_SUPPORT /** De-initialization function */ int (*deinit)(const struct device *dev); +#endif /* CONFIG_DEVICE_DEINIT_SUPPORT */ }; /** @@ -881,6 +887,8 @@ __syscall int device_init(const struct device *dev); * acquired (e.g. pins, memory, clocks, DMA channels, etc.) and its status will * be left as in its reset state. * + * Note: this will be available if CONFIG_DEVICE_DEINIT_SUPPORT is enabled. + * * @warning It is the responsibility of the caller to ensure that the device is * ready to be de-initialized. * @@ -888,7 +896,8 @@ __syscall int device_init(const struct device *dev); * * @retval 0 If successful * @retval -EPERM If device has not been initialized. - * @retval -ENOTSUP If device does not support de-initialization. + * @retval -ENOTSUP If device does not support de-initialization, or if the + * feature is not enabled (see CONFIG_DEVICE_DEINIT_SUPPORT) * @retval -errno For any other errors. */ __syscall int device_deinit(const struct device *dev); @@ -1132,6 +1141,19 @@ device_get_dt_nodelabels(const struct device *dev) BUILD_ASSERT(sizeof(Z_STRINGIFY(name)) <= Z_DEVICE_MAX_NAME_LEN, \ Z_STRINGIFY(name) " too long") +/** + * @brief Fill in the struct device_ops + * + * @param init_fn_ Initialization function + * @param deinit_fn_ De-initialization function + */ +#define Z_DEVICE_OPS(init_fn_, deinit_fn_) \ + { \ + .init = (init_fn_), \ + IF_ENABLED(CONFIG_DEVICE_DEINIT_SUPPORT, \ + (.deinit = (deinit_fn_),)) \ + } + /** * @brief Initializer for @ref device. * @@ -1156,7 +1178,7 @@ device_get_dt_nodelabels(const struct device *dev) .api = (api_), \ .state = (state_), \ .data = (data_), \ - .ops = { .init = (init_fn_), .deinit = (deinit_fn_) }, \ + .ops = Z_DEVICE_OPS(init_fn_, deinit_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/Kconfig.device b/kernel/Kconfig.device index 5692236a006..94d2107f8ab 100644 --- a/kernel/Kconfig.device +++ b/kernel/Kconfig.device @@ -33,6 +33,16 @@ config DEVICE_DT_METADATA each device. This allows you to use device_get_by_dt_nodelabel(), device_get_dt_metadata(), etc. +config DEVICE_DEINIT_SUPPORT + bool "Support device de-initialization" + default y + help + In very specific case, it might be necessary to de-initialize + a device at runtime. This is possible by providing a function + to do so. Note, that this will grow every struct device by a + function pointer. All device drivers that use the relevant + macros and provide such function should select this option. + endmenu menu "Initialization Priorities" diff --git a/kernel/device.c b/kernel/device.c index ad97b0b0409..88e1d074ab1 100644 --- a/kernel/device.c +++ b/kernel/device.c @@ -145,6 +145,7 @@ bool z_impl_device_is_ready(const struct device *dev) int z_impl_device_deinit(const struct device *dev) { +#ifdef CONFIG_DEVICE_DEINIT_SUPPORT int ret; if (!dev->state->initialized) { @@ -163,6 +164,10 @@ int z_impl_device_deinit(const struct device *dev) dev->state->initialized = false; return 0; +#else + ARG_UNUSED(dev); + return -ENOTSUP; +#endif /* CONFIG_DEVICE_DEINIT_SUPPORT */ } #ifdef CONFIG_USERSPACE