📄 timer.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
// Portions Copyright (c) Texas Instruments. All rights reserved.
//
//------------------------------------------------------------------------------
//
// File: timer.c
//
// The 32KHz timer (32768Hz) requires special procedure for update.
// After writting new value to TVR and setting TRB bit in CR register
// code should wait for counter harware to clear TRB bit. Then code has
// to wait until value readed from TCR register is new value minus one.
// The reason is that counter will ignore any new value set in this period
// (when TCR value is equal to value set to TVR). This is true even in case
// of autoload, so in our situation it means we can't update timer when
// OEMIdle is entered until counter value is smaller than set/autoload one.
// Another issue is related to interrupt. There is latency half or one clock
// before interrupt is seen in interrupt controller. However TCR value can
// already has been set to recharged value. This is reason why code has to
// check for pending interrupt two times.
//
#include <windows.h>
#include <nkintr.h>
#include <ceddk.h>
#include <oal.h>
#include <omap730.h>
#include <omap730_led.h>
//------------------------------------------------------------------------------
//
// External: g_oalTimerIrq
//
// This variable is defined in interrupt module and it is used in interrupt
// handler to distinguish system timer interrupt.
//
extern UINT32 g_oalTimerIrq;
//------------------------------------------------------------------------------
//
// Global: g_pOALEMIFRegs
//
// This global variable contains EMIF module virtual address. It is used
// in OALCPUIdle to move DRAM to self-refresh mode.
//
OMAP730_EMIF_REGS *g_pOALEMIFRegs;
//------------------------------------------------------------------------------
//
// Define: TIMER_PERIOD/TIMER_MARGIN/SET_COMPENSTATION
//
// This definition are used in code when timer period, timer margin (if count
// value is higher we can update timer period) or timer set compensation is
// used. However some code was optimalized for timer period 32 tick and
// it will require code modification when different period is used.
//
#define TIMER_PERIOD 32
#define TIMER_MARGIN 3
#define SET_COMPENSATION 5
#define DEEP_SLEEP_COMPENSATION 0
//------------------------------------------------------------------------------
typedef struct {
UINT32 maxIdleMSec; // maximal idle in MSec
volatile UINT64 curCounts; // counts at last system tick
OMAP730_TIMER32K_REGS *pTimerRegs; // 32K timer address
OMAP730_ULPD_REGS *pULPDRegs; // ULPD address
OMAP730_CLKM_REGS *pCLKMRegs; // CLKM address
BOOL updatePeriod; // change timer period
BOOL deepSleep; // is DEEP_SLEEP enabled?
UINT32 deepSleepSetup; // delay after deep sleep...
UINT32 deepSleepTreshold; // idle treshold for deep sleep
} OMAP730_TIMER_STATE;
//------------------------------------------------------------------------------
//
// Global: g_timer
//
// This is global instance of timer control block.
//
OMAP730_TIMER_STATE g_timer;
//------------------------------------------------------------------------------
// External functions
BOOL OALIntrIsIrqPending(UINT32 irq);
//------------------------------------------------------------------------------
//
// Function: OALTimerInit
//
// For OMAP730 32kHz timer is used to implement system timer. The code is
// optimized for this timer with period 32 timer clocks per system tick. We
// ignore all parameters.
//
BOOL OALTimerInit(
UINT32 sysTickMSec, UINT32 countsPerMSec, UINT32 countsMargin
) {
BOOL rc = FALSE;
UINT32 sysIntr;
OALMSG(OAL_TIMER&&OAL_FUNC, (
L"+OALTimerInit(%d, %d, %d)\r\n", sysTickMSec, countsPerMSec,
countsMargin
));
// 32K timer has 24 bits
g_timer.maxIdleMSec = 0x3FFFFF >> 5;
g_timer.updatePeriod = FALSE;
// Set idle conversion constant and counters
idleconv = 32768;
curridlehigh = curridlelow = 0;
g_timer.curCounts = 0;
// Set global variable to tell interrupt module about timer used
g_oalTimerIrq = IRQ_TIMER32K;
// Request SYSINTR for timer IRQ, it is done to reserve it...
sysIntr = OALIntrRequestSysIntr(1, &g_oalTimerIrq, OAL_INTR_FORCE_STATIC);
// Enable System Tick interrupt
if (!OEMInterruptEnable(sysIntr, NULL, 0)) {
OALMSG(OAL_ERROR, (
L"ERROR: OALTimerInit: Interrupt enable for system timer failed"
));
goto cleanUp;
}
// Get virtual addresses for hardware
g_timer.pTimerRegs = OALPAtoUA(OMAP730_TIMER32K_REGS_PA);
g_timer.pULPDRegs = OALPAtoUA(OMAP730_ULPD_REGS_PA);
g_timer.pCLKMRegs = OALPAtoUA(OMAP730_CLKM_REGS_PA);
g_pOALEMIFRegs = OALPAtoUA(OMAP730_EMIF_REGS_PA);
// Get deep sleep setup delay
g_timer.deepSleepSetup = INREG16(&g_timer.pULPDRegs->SLICER_SETUP);
g_timer.deepSleepSetup += INREG16(&g_timer.pULPDRegs->VTCXO_SETUP);
g_timer.deepSleepSetup += INREG16(&g_timer.pULPDRegs->RF_SETUP);
g_timer.deepSleepSetup += DEEP_SLEEP_COMPENSATION;
// Enable deep sleep by default
g_timer.deepSleep = TRUE;
// Set treshold to 2 * deep sleep setup delay
g_timer.deepSleepTreshold = g_timer.deepSleepSetup << 1;
// Start timer
OUTREG32(&g_timer.pTimerRegs->TVR, TIMER_PERIOD - 1);
SETREG32(&g_timer.pTimerRegs->CR, CR_TRB);
while ((INREG32(&g_timer.pTimerRegs->CR) & CR_TRB) != 0);
SETREG32(&g_timer.pTimerRegs->CR, CR_TSS|CR_INT_EN|CR_ARL);
// Done
rc = TRUE;
cleanUp:
OALMSG(OAL_TIMER && OAL_FUNC, (L"-OALTimerInit(rc = %d)\r\n", rc));
return rc;
}
//------------------------------------------------------------------------------
//
// Function: OALTimerIntrHandler
//
// This function implement timer interrupt handler. It is called from common
// ARM interrupt handler.
//
UINT32 OALTimerIntrHandler()
{
UINT32 sysIntr = SYSINTR_NOP;
UINT32 delta;
// Update the millisecond and high resolution counters
g_timer.curCounts += TIMER_PERIOD;
CurMSec = (UINT32)((g_timer.curCounts * 1000) >> 15);
// Update LED display (bits 0&1)
OALLED(LED_IDX_TIMERSPIN, CurMSec >> 10);
// Reschedule?
delta = dwReschedTime - CurMSec;
if ((INT32)delta <= 0) sysIntr = SYSINTR_RESCHED;
// Should we update timer period?
if (g_timer.updatePeriod) {
// Set new value
OUTREG32(&g_timer.pTimerRegs->TVR, TIMER_PERIOD - 1);
SETREG32(&g_timer.pTimerRegs->CR, CR_TRB);
// Wait while it is set
while ((INREG32(&g_timer.pTimerRegs->CR) & CR_TRB) != 0);
// Wait until one tick (any timer set in this time is ignored)
while (INREG32(&g_timer.pTimerRegs->TCR) != (TIMER_PERIOD - 2));
// We are done with period update
g_timer.updatePeriod = FALSE;
}
// Return
return sysIntr;
}
//------------------------------------------------------------------------------
//
// Function: OEMIdle
//
// This function is called by the kernel when there are no threads ready to
// run. The CPU should be put into a reduced power mode if possible and halted.
// It is important to be able to resume execution quickly upon receiving an
// interrupt.
//
// Interrupts are disabled when OEMIdle is called and when it returns.
//
// Note that system timer must be running when CPU/SoC is moved to reduced
// power mode.
//
void OEMIdle(DWORD idleParam)
{
UINT32 baseMSec, idleSysTicks;
INT32 usedCounts, idleCounts;
ULARGE_INTEGER idle;
UINT32 cnt, set, counts;
BOOL deepSleep;
OMAP730_TIMER32K_REGS *pTimerRegs = g_timer.pTimerRegs;
OMAP730_ULPD_REGS *pULPDRegs = g_timer.pULPDRegs;
OMAP730_CLKM_REGS *pCLKMRegs = g_timer.pCLKMRegs;
// Get current system timer counter
baseMSec = CurMSec;
// Compute the remaining idle time
idleSysTicks = dwReschedTime - baseMSec;
// Idle time has expired - we need to return
if ((INT32)idleSysTicks <= 0) return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -