Skip to content

Add device init/de-init functionality #84394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions cmake/linker_script/common/common-rom.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions drivers/misc/devmux/devmux.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ int z_vrfy_devmux_select_set(struct device *dev, size_t index)
#include <zephyr/syscalls/devmux_select_set_mrsh.c>
#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;
Expand All @@ -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;
}
Expand Down
200 changes: 132 additions & 68 deletions include/zephyr/device.h

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion include/zephyr/drivers/can.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down
3 changes: 2 additions & 1 deletion include/zephyr/drivers/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down
2 changes: 1 addition & 1 deletion include/zephyr/drivers/smbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
5 changes: 3 additions & 2 deletions include/zephyr/drivers/spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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), \
Expand Down Expand Up @@ -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__)
Expand Down
87 changes: 7 additions & 80 deletions include/zephyr/init.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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 */
Expand Down Expand Up @@ -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 */

/**
Expand Down Expand Up @@ -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_)} \

/** @} */

Expand Down
3 changes: 0 additions & 3 deletions include/zephyr/linker/common-rom/common-rom-kernel-devices.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion include/zephyr/net/ethernet.h
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down
6 changes: 4 additions & 2 deletions include/zephyr/net/net_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -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); \
Expand Down Expand Up @@ -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)
Expand Down
33 changes: 33 additions & 0 deletions kernel/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <zephyr/device.h>
Expand Down Expand Up @@ -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 <zephyr/syscalls/device_deinit_mrsh.c>
#endif

#ifdef CONFIG_DEVICE_DEPS

static int device_visitor(const device_handle_t *handles,
Expand Down
27 changes: 11 additions & 16 deletions kernel/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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);
}
Expand All @@ -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
Expand Down
20 changes: 10 additions & 10 deletions tests/lib/devicetree/devices/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading