⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 timer.c

📁 6410BSP1
💻 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.
//
// Copyright (c) Samsung Electronics. Co. LTD. All rights reserved.

/*++

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:

    timer.c

Abstract:

    Interface to OAL timer services.

Functions:

    FMD_Init,

Notes:

--*/

#include <windows.h>
#include <nkintr.h>
#include <ceddk.h>
#include <oal.h>

#include <s3c6410.h>
#include <pmplatform.h>
#include "soc_cfg.h"

#define USB_PERFORMANCE_WORKAROUND  (TRUE)
#define MIN_IDLE_COUNT              20

#ifdef ENABLE_WATCH_DOG
extern void OALInitWatchDogTimer(void);
#endif
#define USE_VARIABLETICK   (FALSE)  //< This is testing on beta release

static INT32 g_PreSinceSysTick;

//------------------------------------------------------------------------------
// Local Variables
static volatile S3C6410_PWM_REG *g_pPWMReg = NULL;
static volatile S3C6410_SYSCON_REG *g_pSysConReg = NULL;
static volatile S3C6410_VIC_REG *g_pVIC0Reg = NULL;
static volatile S3C6410_VIC_REG *g_pVIC1Reg = NULL;
static volatile S3C6410_GPIO_REG *g_pGPIOReg = NULL;

// In S3C6410, We use Timer4 as System Timer
void InitSystemTimer(UINT32 countsPerSysTick)
{
    volatile S3C6410_PWM_REG *pPWMReg;

    // Initialize S3C6410 Timer
    pPWMReg = (S3C6410_PWM_REG*)OALPAtoUA(S3C6410_BASE_REG_PA_PWM);

    // Set Prescaler 1 (Timer2,3,4)
    pPWMReg->TCFG0 = (pPWMReg->TCFG0 & ~(0xff<<8)) | ((SYS_TIMER_PRESCALER-1)<<8);

    // Set Divider MUX for Timer4
    switch(SYS_TIMER_DIVIDER)
    {
    case 1:
        pPWMReg->TCFG1 = (pPWMReg->TCFG1 & ~(0xf<<16)) | (0<<16);
        break;
    case 2:
        pPWMReg->TCFG1 = (pPWMReg->TCFG1 & ~(0xf<<16)) | (1<<16);
        break;
    case 4:
        pPWMReg->TCFG1 = (pPWMReg->TCFG1 & ~(0xf<<16)) | (2<<16);
        break;
    case 8:
        pPWMReg->TCFG1 = (pPWMReg->TCFG1 & ~(0xf<<16)) | (3<<16);
        break;
    case 16:
        pPWMReg->TCFG1 = (pPWMReg->TCFG1 & ~(0xf<<16)) | (4<<16);
        break;
    default:
        pPWMReg->TCFG1 = (pPWMReg->TCFG1 & ~(0xf<<16)) | (0<<16);
        break;
    }


    // Set Timer4 Count Buffer Register
    pPWMReg->TCNTB4 = countsPerSysTick - 1;

    // Timer4 Clear Interrupt Pending
    //g_pPWMReg->TINT_CSTAT |= (1<<9);    // Do not use OR/AND operation on TINTC_CSTAT
    pPWMReg->TINT_CSTAT = TINT_CSTAT_INTMASK(pPWMReg->TINT_CSTAT) | TIMER4_PENDING_CLEAR;

    // Timer4 Interrupt Enable
    //g_pPWMReg->TINT_CSTAT |= (1<<4);    // Do not use OR/AND operation on TINTC_CSTAT
    pPWMReg->TINT_CSTAT = TINT_CSTAT_INTMASK(pPWMReg->TINT_CSTAT) | TIMER4_INTERRUPT_ENABLE;

    // Start Timer4 in Auto Reload mode (Fixed Tick!!!)
    pPWMReg->TCON &= ~(0x7<<20);

    pPWMReg->TCON |= (1<<21);            // Update TCNTB4
    pPWMReg->TCON &= ~(1<<21);

    pPWMReg->TCON |= (1<<22)|(1<<20);    // Auto-reload Mode, Timer4 Start
}


//------------------------------------------------------------------------------
//
//  Function: OALTimerInit
//
//  This function is typically called from the OEMInit to initialize
//  Windows CE system timer. The tickMSec parameter determine timer
//  period in milliseconds. On most platform timer period will be
//  1 ms, but it can be usefull to use higher value for some
//  specific (low-power) devices.
//
//  Implementation for S3C6410 is using timer 4 as system timer.
//
BOOL OALTimerInit(UINT32 msecPerSysTick, UINT32 countsPerMSec, UINT32 countsMargin)
{
    BOOL rc = FALSE;
    UINT32 countsPerSysTick;
    UINT32 SysIntr, Irq;

    OALMSG(OAL_TIMER&&OAL_FUNC, (
        L"+OALTimerInit( %d, %d, %d )\r\n",
        msecPerSysTick, countsPerMSec, countsMargin    ));
    OALMSG(1, (
        L"+OALTimerInit( %d, %d, %d )\r\n",
        msecPerSysTick, countsPerMSec, countsMargin    ));


    // Initialize High Resolution Timer function pointers
    pQueryPerformanceFrequency = OALTimerQueryPerformanceFrequency;
    pQueryPerformanceCounter = OALTimerQueryPerformanceCounter;

    countsPerSysTick = countsPerMSec * msecPerSysTick;

    // Validate Input parameters
    if ((msecPerSysTick < 1)
        ||(msecPerSysTick > 1000)
        ||(countsPerSysTick < 1)
        ||(countsPerSysTick > 0xFFFF) )
    {
        OALMSG(OAL_ERROR, (L"ERROR: OALTimerInit: System tick period out of range..."));
        goto cleanUp;
    }

    // Initialize Timer State Global variable (OAL_TIMER_STATE)
    g_oalTimer.msecPerSysTick = msecPerSysTick;
    g_oalTimer.countsPerMSec = countsPerMSec;
    g_oalTimer.countsMargin = countsMargin;
    g_oalTimer.countsPerSysTick = countsPerSysTick;
    g_oalTimer.curCounts = 0;
    g_oalTimer.maxPeriodMSec = 0xFFFF/g_oalTimer.countsPerMSec;

    g_oalTimer.actualMSecPerSysTick = g_oalTimer.msecPerSysTick;
    g_oalTimer.actualCountsPerSysTick = g_oalTimer.countsPerSysTick;

    // Set Kernel Exported Globals to Initial values
    idleconv = countsPerMSec;
    curridlehigh = 0;
    curridlelow = 0;

    // Create SYSINTR for timer
    Irq = IRQ_TIMER4;
    SysIntr = OALIntrRequestSysIntr(1, &Irq, OAL_INTR_FORCE_STATIC);

    g_pPWMReg = (S3C6410_PWM_REG*)OALPAtoUA(S3C6410_BASE_REG_PA_PWM);
    g_pSysConReg = (S3C6410_SYSCON_REG *)OALPAtoVA(S3C6410_BASE_REG_PA_SYSCON, FALSE);
    g_pVIC0Reg = (S3C6410_VIC_REG *)OALPAtoVA(S3C6410_BASE_REG_PA_VIC0, FALSE);
    g_pVIC1Reg = (S3C6410_VIC_REG *)OALPAtoVA(S3C6410_BASE_REG_PA_VIC1, FALSE);
    g_pGPIOReg = (S3C6410_GPIO_REG *)OALPAtoVA(S3C6410_BASE_REG_PA_GPIO, FALSE);

    //---------------------
    // Initialize System Timer
    //---------------------
    InitSystemTimer(g_oalTimer.countsPerSysTick);

    // Enable System Tick Interrupt
    if (!OEMInterruptEnable(SysIntr, NULL, 0))
    {
        OALMSG(OAL_ERROR, (L"ERROR: OALTimerInit: Interrupt enable for system timer failed"));
        goto cleanUp;
    }

    InitializeDVS();

    //
    // Define ENABLE_WATCH_DOG to enable watchdog timer support.
    // NOTE: When watchdog is enabled, the device will reset itself if watchdog timer is not refreshed within ~4.5 second.
    //       Therefore it should not be enabled when kernel debugger is connected, as the watchdog timer will not be refreshed.
    //
#ifdef ENABLE_WATCH_DOG
    OALInitWatchDogTimer ();
#endif

    // 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;
    g_PreSinceSysTick = g_oalTimer.countsPerSysTick - (g_pPWMReg->TCNTO4);

    // Timer4 Clear Interrupt Pending
    g_pPWMReg->TINT_CSTAT = TINT_CSTAT_INTMASK(g_pPWMReg->TINT_CSTAT) | TIMER4_PENDING_CLEAR;

    // Update high resolution counter
    g_oalTimer.curCounts += g_oalTimer.countsPerSysTick;

    // Update the millisecond counter
    CurMSec += g_oalTimer.msecPerSysTick;

    // Reschedule?
    if ((int)(CurMSec - dwReschedTime) >= 0) SysIntr = SYSINTR_RESCHED;

    UpdateDVS();

#ifdef OAL_ILTIMING
    if (g_oalILT.active)
    {
        if (--g_oalILT.counter == 0)
        {
            SysIntr = SYSINTR_TIMING;
            g_oalILT.counter = g_oalILT.counterSet;
            g_oalILT.isrTime2 = OALTimerCountsSinceSysTick();
        }
    }
#endif

    return SysIntr;
}


//------------------------------------------------------------------------------
//
//  Function: OALTimerCountsSinceSysTick
//
//  This function return count of hi res ticks since system tick.
//
//  Timer 4 counts down, so we should substract actual value from
//  system tick period.
//
INT32 OALTimerCountsSinceSysTick()
{
    INT32 TimerCountsSinceSysTick = (g_oalTimer.countsPerSysTick - (g_pPWMReg->TCNTO4)) - g_PreSinceSysTick;
    if (TimerCountsSinceSysTick < 0)
        TimerCountsSinceSysTick += g_oalTimer.countsPerSysTick;
    return TimerCountsSinceSysTick;
}

//------------------------------------------------------------------------------
//
//  Function: OALTimerUpdate
//
//  This function is called to change length of actual system timer period.
//  If end of actual period is closer than margin period isn't changed (so
//  original period elapse). Function returns time which already expires
//  in new period length units. If end of new period is closer to actual time
//  than margin period end is shifted by margin (but next period should fix
//  this shift - this is reason why OALTimerRecharge doesn't read back
//  compare register and it uses saved value instead).
//
UINT32 OALTimerUpdate(UINT32 period, UINT32 margin)
{
#if USE_VARIABLETICK
    UINT32 tcon, ret;

    ret = OALTimerCountsSinceSysTick();

    OUTREG32(&g_pPWMRegs->TCNTB4, period);
    tcon = INREG32(&g_pPWMRegs->TCON) & ~(0x0F << 20);
    OUTREG32(&g_pPWMRegs->TCON, tcon | (0x2 << 20) );
    OUTREG32(&g_pPWMRegs->TCON, tcon | (0x5 << 20) );

    return (ret);
#else
    // Fixed Tick do not Update Timer
    return 0;
#endif
}

//------------------------------------------------------------------------------
//
//  Function:   OEMIdle
//
//
void OEMIdle(DWORD idleParam)
{
    UINT32 baseMSec;
    INT32 usedCounts, idleCounts;
    ULARGE_INTEGER idle;

    // Get current system timer counter
    baseMSec = CurMSec;

    // Find how many hi-res ticks was already used
    usedCounts = OALTimerCountsSinceSysTick();

    if (usedCounts + MIN_IDLE_COUNT >= (INT32)g_oalTimer.countsPerSysTick)
    {
        // Abandon Idle
        return;
    }

    // Move SoC/CPU to idle mode
    OALCPUIdle();

    // Get real idle value. If result is negative we didn't idle at all.
    idleCounts = OALTimerCountsSinceSysTick() - usedCounts;

    // Get real idle value. If result is negative, idle period is laid across the Time quantum
    idleCounts = (idleCounts >= 0) ? (idleCounts) : (idleCounts + g_oalTimer.countsPerSysTick);

    // Update idle counters
    idle.LowPart = curridlelow;
    idle.HighPart = curridlehigh;
    idle.QuadPart += idleCounts;
    curridlelow  = idle.LowPart;
    curridlehigh = idle.HighPart;

    SetCurrentIdleTime((DWORD)(idle.QuadPart/idleconv));
}

//------------------------------------------------------------------------------
//
//  Function:   OALCPUIdle
//
VOID OALCPUIdle()
{
    //OEMWriteDebugLED(-1, MAKELONG(0x8,0x8));

    g_pSysConReg->PWR_CFG = (g_pSysConReg->PWR_CFG & ~(0x3<<5)) | (0x1<<5);    // Enter IDLE mode

    System_WaitForInterrupt();    // Enter ARM core to WaitForInterrupt

    g_pSysConReg->PWR_CFG &= ~(0x1<<5);    // Clear IDLE mode

    //OEMWriteDebugLED(-1, MAKELONG(0x0,0x8));
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -