Description
Introduction
As of today all Zephyr devices rely mostly on a set of Kconfig-defined priorities to set their initialization order. This RFC proposes alternatives to improve this mechanism.
Problem description
Hardware device initialization order depends on the final hardware layout where Zephyr runs. One board may have different initialization requirements than another one. The correct device initialization can be inferred from the devicetree nodes ordinal, an information that is already extracted and made available at build time. By using manual priorities we are in the end, replicating that information in the form of CONFIG_X_INIT_PRIORITY
, e.g.
config CAN_INIT_PRIORITY
int "CAN driver init priority"
default 80
help
CAN device driver initialization priority.
Do not mess with it unless you know what you are doing.
Note that the priority needs to be lower than the net stack
so that it can start before the networking sub-system.
This is can be error-prone and can even get tricky to get it right on complex boards (Do not mess with it unless you know what you are doing.
comment may be a result of such assumption).
There is an added challenge: all initialization hooks (DEVICE_DEFINE
, SYS_INIT
...) end up relying on Z_INIT_ENTRY_DEFINE
. This means that we have both hardware devices and "system devices" mixed together sharing the same priority set. For the sake of clarity, "system devices" may be, e.g. the init hook for the network stack.
Proposed change
DT devices should not specify a manual priority, so we could have something like:
/** Macro that returns DT device priority */
#define Z_DEVICE_DT_PRIO(node_id) DT_DEP_ORD(node_id)
#define DEVICE_DT_DEFINE(node_id, init_fn, pm_control_fn, \
data_ptr, cfg_ptr, level, \
api_ptr, ...) \
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id), \
DEVICE_DT_NAME(node_id), init_fn, \
pm_control_fn, \
data_ptr, cfg_ptr, level, Z_DEVICE_DT_PRIO(node_id), \
api_ptr, __VA_ARGS__)
or leave current macro as-is and write DEVICE_DT_PRIO()
on each driver.
In order to co-exist with other "system devices", DT devices should likely be initialized before (e.g. by means of a priority offset). This way we make sure that "system devices" relying on hardware devices are initialized in the correct order. However, I think we should first check this is a correct assumption.
In the future, we could also discuss splitting SYS_INIT
out of the Z_INIT_ENTRY_DEFINE
, so that they have no relation at all with devices. A sort of "system service" concept.
Others:
Because we have init levels (PRE_KERNEL_1
, PRE_KERNEL_2
...), it is still possible to have a device A depending on device B being set at a lower priority init level because we are partitioning the ordinals set. This is a programming error that could likely be detected by inspecting the ELF file. Note that this is possible with the current mechanism, too.
Dependencies
Modify device drivers to use the proposed mechanism.
Concerns and Unresolved Questions
- Clarify all uses of "system devices"
- Unforeseen problems of using ordinals?
Alternatives
Some inspiration from Linux:
Naturally, in each case the device which provides the interrupt or GPIO will need to be initialized before it can be found and used. It wasn't very many kernel versions ago that this was a real problem. However in the 3.4 kernel, drivers gained the ability for their initialization (or "probe") routine to return the error "EPROBE_DEFER" which would cause the initialization to be tried again later. So if a driver finds that a GPIO line is listed in the devicetree, but no driver has registered GPIOs for the target node yet, it can fail with EPROBE_DEFER and know it can try again later. This can even be used to remove the need for callbacks and delayed registration in board files, but it is really essential for devicetree, and happily it works quite well.
Metadata
Metadata
Assignees
Type
Projects
Status