Zephyr OS Init Levels: Check Which Services a Driver May Use Before the Kernel Starts
Moving a driver initialization function earlier can make a dependency available sooner, but it can also place that function in a context where the services it calls are unavailable. Review both ordering and execution context before changing an initialization priority to fix a startup failure.
This article uses Zephyr 4.4.0 source. It concerns code executed during system initialization, not the runtime scheduling priority of a thread. The examples describe proposed investigations and do not report a tested board or driver migration.

Identify the registered initialization context
Find the macro or registration that places the relevant initialization function into the boot sequence. Record its level and priority, plus the actual revision of the driver. Do not infer the context from the function name or from where a diagnostic appears in the console output.
The Zephyr 4.4.0 initialization header describes the ordered initialization levels. In particular, the pre-kernel levels run before kernel services are available, while POST_KERNEL occurs after the kernel is alive. That distinction matters more than whether two functions have nearby numeric priorities.
A runtime thread priority and an initialization priority solve different problems. Changing one does not automatically repair a mistake in the other. Keep them separate in design notes and code review so a startup-order fix is not presented as a scheduler optimization.
Inventory every service used by initialization
Review the initialization function and the helper calls it reaches. Include indirect dependencies such as a bus operation, memory allocation, synchronization, logging or waiting for another component. A short wrapper can hide a substantial chain of service requirements.
For each dependency, ask whether it is available in the selected context and whether its own device or subsystem has completed initialization. Having a pointer to a dependency is not proof that it can already be used.
A hypothetical driver may begin using a helper that waits on a kernel object after a refactor. If the driver still runs in a pre-kernel context, the new helper changes the validity of the existing initialization level even though the registration line did not change. The review must follow behavior, not only the edited lines.
Separate ordering from context legality
An ordering dependency asks whether one component runs before another. A context requirement asks whether the operation is permitted at that stage at all. Raising or lowering a numeric priority within a pre-kernel level cannot make unavailable kernel services appear.
Conversely, moving a function to POST_KERNEL does not prove that every driver it needs has already initialized. The later context makes a class of operations possible, but the actual dependency ordering still needs review.
Use a small dependency sketch that records both facts. Identify the provider, the service required and the stage where the consumer calls it. This helps expose circular assumptions, such as two drivers each expecting the other to be usable during their own initialization.
Keep early initialization narrowly focused
Where the hardware requires an early operation, consider separating that minimal action from work that can occur later. The early portion should have an explicit reason to run before other services are available, and its dependencies should be correspondingly limited.
For example, an illustrative design might need an early hardware setup step before a later subsystem can operate. The later configuration or application policy need not automatically share the same early context. Whether such a split is valid depends on the actual hardware and driver design; it is not a universal instruction to divide every driver.
Avoid compensating for an invalid wait with an arbitrary busy loop. That can change the symptom while preserving a hidden dependency and making startup timing less predictable. Establish the provider's readiness condition and the supported place to wait for it.
Inspect return handling and readiness evidence
The Zephyr 4.4.0 device implementation records initialization attempts and their results. Preserve the original return path and error information during diagnosis rather than reducing every startup issue to a later generic unready message.
A driver can fail because a dependency is unavailable, because hardware did not respond or because configuration is invalid. Those observations require different corrections. An earlier registration time is only justified when the evidence shows that ordering is the actual issue.
When adding diagnostics, check that the diagnostic mechanism itself is appropriate for the initialization stage. A logging change that depends on a later service can complicate the fault being investigated. Use supported early diagnostics or a debugger when necessary, and describe any limits in the collected evidence.
Test startup paths that exercise the dependency
After changing an initialization level or splitting the work, test the relevant startup conditions. A warm restart can leave hardware in a different state from a power-on reset. A configuration that disables an optional provider can expose a dependency that the normal development build always satisfies.
Design tests around the actual dependency rather than a single successful boot message. Record whether the provider initialized, whether the consumer attempted its operation in the intended stage and whether the required function became usable.
Do not claim that one successful startup proves all board variants or power sequences. Identify the configurations and reset paths actually checked. If a failure cannot be reproduced, preserve the uncertainty instead of presenting a priority adjustment as a demonstrated root-cause fix.
Document the reason for the chosen level
Leave a concise explanation of why the code runs at its selected level and which dependencies constrain that choice. Include the reason for any unusual priority relationship so future changes do not accidentally invalidate it.
A sound initialization design aligns three things: the hardware operation that must occur, the services available in that context and the providers that must already be ready. Numeric ordering is one part of that design. Reviewing the full relationship produces a more durable fix than moving code earlier until one development board happens to boot.