NAME | SYNOPSIS | API RESTRICTIONS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | Allowed Calling Contexts | ATTRIBUTES
#include <dki/dki.h>int svDkiInitLevel(void);
The function or functions documented here may not be used safely in all application contexts with all APIs provided in the ChorusOS 5.0 product.
See API(5FEA) for details.
DKI
This function allows a driver to detect whether it is executing at microkernel initialization time or at runtime. In addition, the function allows a driver to determine at which initialization step it is running: critical or normal. This makes possible to adapt the driver behavior according to the execution environment (critical/normal initialization or runtime).
The microkernel initialization process is split into two steps. The first step is done inside the critical section with interrupts masked at CPU level. The second step is executed outside of the critical section with interrupts enabled at CPU level. So, the BSP drivers may be split into two groups. The first group of drivers are initialized within the critical section while the second group of drivers are initialized later on with interrupts enabled at CPU level.
Such an initialization schema allows the majority of the BSP drivers to be moved out from the critical section. Basically, only the host bus driver (which typically manages the master interrupt controller) should be initialized within the critical section in order to allow interrupts to be enabled at CPU level prior to proceed to the second initialization step.
This is extremely important for a platform where the control of a critical device should be taken by a driver as soon as possible. For example, this is the case on a platform where a watchdog timer is enabled only once by the boot code and the microkernel watchdog driver has to be started before the watchdog timer interval expires. Note that the driver of such a watchdog timer device typically needs to have microkernel timeout services available in order to reset (or pat) the watchdog device periodically.
In the ChorusOS system, the microkernel initialization process goes through the following steps:
Microkernel module initialization (including DATE and TICK).
Critical device initialization.
Interrupt enabling at CPU level.
Normal device initialization.
A BSP developer should split all platform devices into two grpoups. The first group of (critical) devices is initialized at step 2, that is, inside the critical section with interrupts masked at CPU level. The second group of (normal) devices is initialized at step 4, that is, outside of the critical section with interrupts enabled at CPU level. Note that the host bus bridge should take a part in the critical group in order to disable interrupts at the bus interrupt controller level. Otherwise, the system behavior may be unpredictable once interrupts are enabled at CPU level (spurious interrupts may occur).
Note that the TICK and DATE modules are initialized at step 1. These modules use the svDeviceNewNotify() DKI service in order to be notified when a new device is registered in the device registry. So, the DATE and TICK modules are initialized prior to the device drivers initialization but they become effectively operational only when associated device drivers are started (registered) and a connection is established between the module and underlying device driver.
A list of typical critical devices which should be initialized at step 2 is given below:
Host bus bridge (and master interrupt controller).
Watchdog timer.
System tick timer.
The host bus driver should disable interrupts at bus (master) interrupt controller level in order to allow microkernel to enable interrupts at CPU level.
The watchdog timer is typically initialized by the boot program. The microkernel watchdog timer driver should take the device control before the watchdog timer interval expires.
In addition, the watchdog timer driver typically needs to reset (pat) the device periodically (at least during the system initialization phase) using the timeout DKI services. So, the system tick timer device should be initialized at this phase as well. Note that the timeout interrupt is not available immediately at step 2. The tick interrupt is only enabled at step 3. So when developing the watchdog timer driver you should be aware of this timing window when calibrating the timeout period. On the other hand, only the BSP code is executed at step 2. So, the BSP developer normally has a clear idea about amount of executed code and therefore about period of such a timing window.
The svDkiInitLevel() microkernel call is implemented by the DKI module. This function returns an integer value specifying the current initialization level. Basically, the function returns the current value of a global variable. The variable is initialized to zero (DKI_INIT_LEVEL_CRITICAL). Then, it is incremented after step 2 and after step 4. So, svDkiInitLevel() returns 0 (DKI_INIT_LEVEL_CRITICAL) at step 2, 1 (DKI_INIT_LEVEL_NORMAL) at step 4, and 2 (DKI_INIT_LEVEL_RUNTIME) otherwise.
The PROP_INIT_LEVEL property is used to specify whether a device is critical or normal, that is, whether a device should be initialized at step 2 or step 4. The PROP_INIT_LEVEL property value is an integer value specifying the allowed device initialization level. Attached to a device node, such a property specifies at which step an associated device should be initialized.
A nexus (bus) driver should launch a child driver on a device node only if the current initialization level returned by svDkiInitLevel() is greater than or equal to the node initialization level specified by the PROP_INIT_LEVEL property. In addition, a nexus (bus) driver should not perform any enumeration (probing) on the bus inside the critical initialization section.
Note that if the PROP_INIT_LEVEL property is not present in the device node, the zero (critical) initialization level should be assigned by a nexus (bus) driver by default. This ensures a one shot initialization for legacy BSP's which do not split devices on critical and normal. Indeed, because such a device tree does not containt the PROP_INIT_LEVEL property, all devices are initialized in the critical section at step 2.
The DKI module starts the drivers initialization process. Basically, the DKI module launches low level drivers on device nodes attached to the root node. Then, the initialization process is continued downstream by BSP nexus (bus) drivers. Thus, the DKI module is invoked twice at steps 2 and 4 in order to start such an intitialization process.
At step 2, the DKI module launches a driver on a node if and only if the node initialization level is DKI_INIT_LEVEL_CRITICAL.
At step 4, the DKI module invokes load handlers for already started (at step 2) drivers. The load handler allows a nexus (bus) driver to continue initialization of non-critical child devices (that is, devices with initialization level DKI_INIT_LEVEL_NORMAL) which have not been initializated at step 2. In addition, the DKI module launches a driver on an inactive node if the node initialization level is less than or equal to DKI_INIT_LEVEL_NORMAL.
Note that the DKI module acts like a root nexus driver. So, in order to support the two steps initialization mechanism, a BSP nexus (bus) driver should follow the initialization schema described above. In other words, making decision to launch a driver on a child node, a BSP bus (nexus) driver should take into account the current initialization level and the node initialization level. In addition, a BSP bus (nexus) driver should not perform an enumeration (probing) on the bus if the current initialization level is DKI_INIT_LEVEL_CRITICAL.
The following table specifies the contexts in which a caller is allowed to invoke each service:
Services | Base level | DKI thread | Interrupt | Blocking |
---|---|---|---|---|
svDkiInitLevel | + | + | + | - |
See attributes(5) for descriptions of the following attributes:
ATTRIBUTE TYPE | ATTRIBUTE VALUE |
---|---|
Interface Stability | Evolving |
NAME | SYNOPSIS | API RESTRICTIONS | FEATURES | DESCRIPTION | EXTENDED DESCRIPTION | Allowed Calling Contexts | ATTRIBUTES