Real Time Clock or RTC is an essential component in all embedded systems. Though all the modern day CPUs have inbuilt RTC, designers are comfortable with external RTC for some specific reasons such as to reduce the power burden, RTC alarms to wake-up the CPU etc. The power consumption of external RTC devices are extremely low and some of them offer additional battery-backed SRAM with tamper-detection etc. Designers interface these RTC chips through a common I2C bus shared by a lot of peripherals and sensors. Therefore a common I2C bus driver is needed to the developers.
Since an I2C interface is a shared bus, synchronization is needed between the devices to access the I2C bus. Windows CE supports synchronization objects like critical sections, semaphore and mutex etc.., to share the device or bus in driver level without conflict. But RTC related functionalities are implemented in the OAL.exe. This blog depicts – how to share I2C bus between OAL and driver without conflict.
RTC related OAL functions Following functions are RTC related OAL functions.
a) BOOL OEMSetAlarmTime (LPSYSTEMTIME lpst ); b) BOOL OEMSetRealTime (LPSYSTEMTIME lpst); c) BOOL OEMGetRealTime(LPSYSTEMTIME lpst); d) Calling KernelIoControl with the IOCTL_HAL_INIT_RTC as a parameter.
These functions are called from the windows CE standard time functions. IOCTL_HAL_INIT_RTC resets the real-time clock by calling the OEMSetRealTime function during the booting. OEM is responsible for filling the stub inside these functions.
I2C Driver I2C driver is a windows CE stream driver. Since the I2C bus is shared by the devices like camera sensor, temperature sensor, battery fuel gauge, Ambient light sensor etc. Therefore I2C bus driver is implemented with synchronization objects. For example, critical section is suitable for this case. This I2C driver will be utilized by other peripheral drivers. These techniques shall be effective to avoid races within the I2C driver. But the same I2C controller is shared between the OAL RTC code and the I2C driver code. This poses a different challenge.
Issue Same I2C controller is shared between the OAL RTC and other peripheral drivers. “OAL.exe” is part of the Windows CE kernel. Device drivers are running on the top of the kernel. In order to synchronize the I2C bus access between OAL and driver, we need to maintain a sharable lock between these areas. The sharable lock can be achieved using the reserved memory region named DRVGLOB.
Solution We can reserve a memory region with the known address in the config.bib file. A structure of sharable data between the OAL and the drivers can be maintained in the DRVGLOB reserved region. The following is the example of a config.bib that contains the DRVGLOB reserved region.
EBOOT 80000000 00100000 RESERVED NK 80100000 01F00000 RAMIMAGE RAM 82000000 0DF00000 RAM ;233MB KITLUSB20 8FF00000 00010000 RESERVED ;For USB2.0 RNDIS Kitl DMA Buffer LCDBOOTFRM 8FF10000 000D0000 RESERVED ;LCD FRAME BUFFER on EBOOT DRVGLOB 8FFE0000 00010000 RESERVED ;Driver Globals
The following is a sample structure of DRVGLOB and it is declared in the DRVGLOB.H header file.
#ifndef __DRVGLOB_H #define __DRVGLOB_H typedef struct { UINT32 Timeout; // RTCTime UINT32 TouchWakeup; // Optional Wake up Source . . . UINT32 bI2CBusy; //Lock logic for sharing the I2C bus between OAL and Driver. } DRV_GLOB, *P_DRV_GLOB;
#define DRVGLOB_BASE 0x8FFE0000 #define DRVGLOB_SIZE 0x10000
#endif
“bI2CBusy” is the variable used for maintaining the synchronization between the OAL and I2CDriver. We can access this reserved region in both the OAL and the driver. Following code snippet explains – how to access the structure in OAL and driver.
• Initializing the DRVGLOB structure in OAL volatile DRV_GLOB* g_DrvGlob;
g_DrvGlob = (volatile DRV_GLOB*) OALPAtoVA(DRVGLOB_BASE, FALSE); if ( !g_DrvGlob ) { RETAILMSG(1, (_T(“RTC::DRVGLOB Mem Alloc Failedrn”))); return FALSE; }
• Initializing the DRVGLOB structure in I2C Driver volatile DRV_GLOB* g_DrvGlob; PHYSICAL_ADDRESS Drv_Glop_Base = {DRVGLOB_BASE}; g_DrvGlob = (volatile DRV_GLOB*) MmMapIoSpace(Drv_Glop_Base, sizeof(DRV_GLOB), FALSE); if ( !g_DrvGlob ) { RETAILMSG(1, (_T(“I2C::DRVGLOB Mem Alloc Failedrn”), GetLastError())); return FALSE; } • Synchronizing OAL RTC lib and I2C Driver
Following procedure explains the implementation.
I2C Read/Write Function in OAL RTC
a) Check I2C is busy with I2C driver. You can add some timeout, instead of waiting infinitely to avoid accidental hanging. If it is busy, wait in the loop until the bus is free. Following code snippet explains this.
while (g_DrvGlob->bI2CBusy) { Millisecond Delay function with 1 ms delay. } b) If it is free set the “bI2CBusy” as 1 to make it busy.
g_DrvGlob->bI2CBusy = 1;
c) Perform I2C bus access.
//I2C Read or Write operations . . d) Clear the “bI2CBusy” to assure that the bus access is finished.
g_DrvGlob->bI2CBusy = 0;
I2C Read/Write Function in Driver
a) Enter Critical section to deny other peripheral driver accessing the I2C driver.
b) Check I2C is busy with OAL RTC. You can add some timeout, instead of waiting infinitely to avoid accidental hanging. If it is busy wait, in the loop until the bus is free. Following code snippet explains this.
while (g_DrvGlob->bI2CBusy) { Millisecond Delay function with 1 ms delay. } c) If it is free set the “bI2CBusy” as 1 to make it busy.
g_DrvGlob->bI2CBusy = 1;
d) Perform I2C bus access.
//I2C Read or Write operations . . e) Clear the “bI2CBusy” to assure that the bus access is finished.
g_DrvGlob->bI2CBusy = 0;
f) Exit Critical section to allow other peripheral driver to access the I2C bus.
In this way, we can share the I2C bus with OAL part and the driver part.