You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1291 lines
32 KiB
1291 lines
32 KiB
/* vxbArmv7AuxTimer.c - ARMV7 Auxeric Timer driver for VxBus */
|
|
|
|
/*
|
|
*
|
|
* This program is OPEN SOURCE software: you can redistribute it and/or modify it;
|
|
* This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
*/
|
|
|
|
/* includes */
|
|
|
|
#include <vxWorks.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <intLib.h>
|
|
#include <vsbConfig.h>
|
|
#include <hwif/vxbus/vxBus.h>
|
|
#include <vxbTimerLib.h>
|
|
#include <hwif/util/hwMemLib.h>
|
|
#include <hwif/vxbus/hwConf.h>
|
|
|
|
#ifdef _WRS_CONFIG_SMP
|
|
#include <cpuset.h>
|
|
#include <taskLib.h>
|
|
#include <private/cpcLibP.h>
|
|
#endif /* _WRS_CONFIG_SMP */
|
|
|
|
IMPORT int uartf(const char * fmt, ...);
|
|
|
|
|
|
/* defines */
|
|
|
|
#ifdef ARMBE8
|
|
# define SWAP32 vxbSwap32
|
|
#else
|
|
# define SWAP32
|
|
#endif /* ARMBE8 */
|
|
|
|
/* debug macro */
|
|
|
|
#undef AUX_TIMER_DBG_ON
|
|
#ifdef AUX_TIMER_DBG_ON
|
|
|
|
/* turning local symbols into global symbols */
|
|
|
|
#ifdef LOCAL
|
|
#undef LOCAL
|
|
#define LOCAL
|
|
#endif
|
|
|
|
#include <private/kwriteLibP.h> /* _func_kprintf */
|
|
#define AUX_TIMER_DBG_OFF 0x00000000
|
|
#define AUX_TIMER_DBG_ISR 0x00000001
|
|
#define AUX_TIMER_DBG_ERR 0x00000002
|
|
#define AUX_TIMER_DBG_INFO 0x00000004
|
|
#define AUX_TIMER_DBG_ALL 0xffffffff
|
|
|
|
LOCAL UINT32 armv7AuxTimerDbgMask = AUX_TIMER_DBG_ALL;
|
|
|
|
#define AUX_TIMER_DBG(mask, ...) \
|
|
do \
|
|
{ \
|
|
if ((armv7AuxTimerDbgMask & (mask)) || \
|
|
((mask) == AUX_TIMER_DBG_ALL)) \
|
|
{ \
|
|
uartf(__VA_ARGS__); \
|
|
} \
|
|
} \
|
|
while ((FALSE))
|
|
#else
|
|
#define AUX_TIMER_DBG(...)
|
|
#endif /* AUX_TIMER_DBG_ON */
|
|
|
|
#undef TIMERFUNC_TO_TIMERDATA
|
|
#define TIMERFUNC_TO_TIMERDATA(pTimerFunc) \
|
|
(ARMV7_AUX_TIMER_DATA *)((ULONG)(pTimerFunc) - \
|
|
OFFSET (ARMV7_AUX_TIMER_DATA, timerFunc))
|
|
|
|
#define ARMV7_AUX_TIMER_NAME "armv7AuxTimer"
|
|
#define ARMV7_AUX_TIMER_MAX_COUNT 0x7fffffff
|
|
#define ARMV7_AUX_TIMER_DEFAULT_TPS 60 /*default*/
|
|
#define ARMV7_AUX_TIMER_DEFAULT_MIN_FREQ 10 /*default*/
|
|
/*#define ARMV7_AUX_TIMER_DEFAULT_MAX_FREQ 5000*/
|
|
#define ARMV7_AUX_TIMER_DEFAULT_MAX_FREQ 1000000
|
|
#define ARMV7_AUX_TIMER_MAX_CLK_FREQ 50000000
|
|
#define ARMV7_AUX_TIMER_MIN_CLK_FREQ 1000000
|
|
|
|
#define AUX_TIMER_REG_CTL 0
|
|
#define AUX_TIMER_REG_TVAL 1
|
|
|
|
#define AUX_TIMER_CTRL_ENABLE (1 << 0)
|
|
#define AUX_TIMER_CTRL_IMASK (1 << 1)
|
|
#define AUX_TIMER_CTRL_ISTATUS (1 << 2)
|
|
|
|
#define AUX_TIMER_SECURE_PHY_PPI (0)
|
|
#define AUX_TIMER_NON_SECURE_PHY_PPI (1)
|
|
#define AUX_TIMER_VIRTURE_PPI (2)
|
|
#define AUX_TIMER_HYP_PPI (3)
|
|
#define AUX_TIMER_MAX_PPI (4)
|
|
|
|
/* externs */
|
|
|
|
/* globals */
|
|
|
|
/* structure to store the timer information */
|
|
|
|
typedef struct armv7AuxTimerData
|
|
{
|
|
VXB_DEVICE_ID pInst;
|
|
struct vxbTimerFunctionality timerFunc;
|
|
void (*pIsrFunc)(int);
|
|
int arg;
|
|
spinlockIsr_t spinLock;
|
|
UINT32 maxCount;
|
|
UINT32 flags;
|
|
BOOL isEnabled;
|
|
|
|
BOOL isVirtual;
|
|
BOOL secPhyAvail;
|
|
BOOL nonSecPhyAvail;
|
|
|
|
BOOL autoReload;
|
|
} ARMV7_AUX_TIMER_DATA;
|
|
|
|
ARMV7_AUX_TIMER_DATA * pArmv7AuxTimer = NULL;
|
|
|
|
/* function declarations */
|
|
|
|
|
|
LOCAL STATUS armv7AuxTimerAttach (VXB_DEVICE_ID pDev);
|
|
LOCAL void armv7AuxTimerInt (ARMV7_AUX_TIMER_DATA * pTimer);
|
|
|
|
LOCAL STATUS armv7AuxTimerAllocate (
|
|
VXB_DEVICE_ID pInst,
|
|
UINT32 flags,
|
|
void ** pCookie,
|
|
UINT32 timerNo
|
|
);
|
|
LOCAL STATUS armv7AuxTimerRelease (
|
|
VXB_DEVICE_ID pInst,
|
|
void* pCookie
|
|
);
|
|
LOCAL STATUS armv7AuxTimerRolloverGet (void * pCookie, UINT32 * count);
|
|
LOCAL STATUS armv7AuxTimerCountGet (void * pCookie, UINT32 * count);
|
|
LOCAL STATUS armv7AuxTimerCountGet64 (void * pCookie, UINT64 * count64);
|
|
LOCAL STATUS armv7AuxTimerDisable (void * pCookie);
|
|
LOCAL STATUS armv7AuxTimerEnable (void * pCookie, UINT32 maxTimerCount);
|
|
LOCAL STATUS armv7AuxTimerISRSet (void * pCookie, void (*pFunc)(int), int arg);
|
|
IMPORT volatile UINT32 __inline__GetCntFreq (void);
|
|
IMPORT volatile UINT32 __inline__GetVirtTimerValue (void);
|
|
IMPORT volatile UINT32 __inline__GetVirtTimerCtrl (void);
|
|
IMPORT volatile void __inline__SetVirtTimerValue (UINT32 val);
|
|
IMPORT volatile void __inline__SetVirtTimerCtrl (UINT32 val);
|
|
IMPORT volatile UINT32 __inline__GetPhyTimerValue (void);
|
|
IMPORT volatile UINT32 __inline__GetPhyTimerCtrl (void);
|
|
IMPORT volatile void __inline__SetPhyTimerValue (UINT32 val);
|
|
IMPORT volatile void __inline__SetPhyTimerCtrl (UINT32 val);
|
|
IMPORT volatile UINT32 __inline__GetVirtTimerCnt(void);
|
|
IMPORT volatile UINT32 __inline__GetPhyTimerCnt(void);
|
|
|
|
LOCAL void armv7AuxTimerInstInit
|
|
(
|
|
struct vxbDev * pDev
|
|
);
|
|
LOCAL void armv7AuxTimerInstInit2
|
|
(
|
|
struct vxbDev * pDev
|
|
);
|
|
LOCAL void armv7AuxTimerInstConnect
|
|
(
|
|
struct vxbDev * pDev
|
|
);
|
|
|
|
/* locals */
|
|
LOCAL struct drvBusFuncs armv7AuxTimerFuncs =
|
|
{
|
|
armv7AuxTimerInstInit, /* devInstanceInit */
|
|
armv7AuxTimerInstInit2, /* devInstanceInit2 */
|
|
armv7AuxTimerInstConnect /* devConnect */
|
|
};
|
|
|
|
|
|
LOCAL STATUS armv7AuxTimerFuncGet
|
|
(
|
|
VXB_DEVICE_ID pInst,
|
|
struct vxbTimerFunctionality** pTimerFunc,
|
|
int timerNo
|
|
);
|
|
|
|
/* driver methods */
|
|
|
|
LOCAL device_method_t davinciTimerDrv_methods[] =
|
|
{
|
|
DEVMETHOD(vxbTimerFuncGet,armv7AuxTimerFuncGet),
|
|
{0, NULL}
|
|
};
|
|
|
|
|
|
LOCAL struct vxbDevRegInfo armv7AuxTimerRegistration =
|
|
{
|
|
NULL, /* pNext */
|
|
VXB_DEVID_DEVICE, /* devID */
|
|
VXB_BUSID_PLB, /* busID = PLB */
|
|
VXB_VER_4_0_0, /* vxbVersion */
|
|
"armAuxTimer", /* drvName */
|
|
&armv7AuxTimerFuncs, /* pDrvBusFuncs */
|
|
NULL,
|
|
NULL, /* devProbe */
|
|
NULL /* pParamDefaults */
|
|
};
|
|
|
|
|
|
void armv7AuxTimerRegister(void)
|
|
{
|
|
vxbDevRegister((struct vxbDevRegInfo *)&armv7AuxTimerRegistration);
|
|
}
|
|
|
|
|
|
|
|
LOCAL void armv7AuxTimerInstInit
|
|
(
|
|
struct vxbDev * pDev
|
|
)
|
|
{
|
|
armv7AuxTimerAttach(pDev);
|
|
return;
|
|
}
|
|
LOCAL void armv7AuxTimerInstInit2
|
|
(
|
|
struct vxbDev * pDev
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
LOCAL void armv7AuxTimerInstConnect
|
|
(
|
|
struct vxbDev * pDev
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerWriteReg - write the timer register
|
|
*
|
|
* This routine writes value to the generic timer register.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
_WRS_INLINE void armv7AuxTimerWriteReg
|
|
(
|
|
BOOL isVirtual,
|
|
UINT32 reg,
|
|
UINT32 val
|
|
)
|
|
{
|
|
if (isVirtual)
|
|
{
|
|
if (reg == AUX_TIMER_REG_CTL)
|
|
{
|
|
__inline__SetVirtTimerCtrl (val);
|
|
}
|
|
else
|
|
{
|
|
__inline__SetVirtTimerValue (val);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (reg == AUX_TIMER_REG_CTL)
|
|
{
|
|
__inline__SetPhyTimerCtrl (val);
|
|
}
|
|
else
|
|
{
|
|
__inline__SetPhyTimerValue (val);
|
|
}
|
|
}
|
|
|
|
WRS_ASM ("ISB");
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerReadReg - read the timer register
|
|
*
|
|
* This routine reads the generic timer register value.
|
|
*
|
|
* RETURNS: timer register value.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
_WRS_INLINE UINT32 armv7AuxTimerReadReg
|
|
(
|
|
BOOL isVirtual,
|
|
UINT32 reg
|
|
)
|
|
{
|
|
UINT32 val = 0;
|
|
|
|
if (isVirtual)
|
|
{
|
|
if (reg == AUX_TIMER_REG_CTL)
|
|
{
|
|
val = __inline__GetVirtTimerCtrl ();
|
|
}
|
|
else
|
|
{
|
|
val = __inline__GetVirtTimerValue ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (reg == AUX_TIMER_REG_CTL)
|
|
{
|
|
val = __inline__GetPhyTimerCtrl ();
|
|
}
|
|
else
|
|
{
|
|
val = __inline__GetPhyTimerValue ();
|
|
}
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerGetTimerCnt - get the generic timer count
|
|
*
|
|
* This routine gets the generic timer counter register value.
|
|
*
|
|
* RETURNS: timer counter.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
_WRS_INLINE UINT64 armv7AuxTimerGetTimerCnt
|
|
(
|
|
BOOL isVirtual
|
|
)
|
|
{
|
|
UINT64 val = 0;
|
|
|
|
WRS_ASM ("ISB");
|
|
|
|
if (isVirtual)
|
|
{
|
|
val = __inline__GetVirtTimerCnt ();
|
|
}
|
|
else
|
|
{
|
|
val = __inline__GetPhyTimerCnt ();
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerAllocate - allocate resources for a timer
|
|
*
|
|
* This is the function called to allocate a timer for usage by the
|
|
* Timer Abstraction Layer.
|
|
*
|
|
* RETURNS: OK or ERROR if timer allocation failed.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS armv7AuxTimerAllocate
|
|
(
|
|
VXB_DEVICE_ID pInst,
|
|
UINT32 flags,
|
|
void ** pCookie,
|
|
UINT32 timerNo
|
|
)
|
|
{
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO,
|
|
"%s:Line:%d\r\n", (int)__FUNCTION__, __LINE__);
|
|
VXB_ASSERT_NONNULL (pCookie, ERROR);
|
|
|
|
|
|
|
|
pTimer = (ARMV7_AUX_TIMER_DATA *)(pInst->pDrvCtrl);
|
|
|
|
|
|
/* check whether 'pTimerData' is valid or not */
|
|
|
|
if (pTimer != NULL)
|
|
{
|
|
/* if the timer is already allocated, return ERROR */
|
|
|
|
if ((pTimer->pInst != NULL) || (pTimer->timerFunc.allocated))
|
|
{
|
|
uartf("%s,%d: pTimer->pInst = %p . This is error!\r\n",__FILE__,__LINE__, pTimer->pInst);
|
|
return ERROR;
|
|
}
|
|
|
|
/* set the auto-reload flag */
|
|
|
|
if ((flags & VXB_TIMER_AUTO_RELOAD) != 0)
|
|
pTimer->autoReload = TRUE;
|
|
|
|
/* copy the instance pointer */
|
|
|
|
pTimer->pInst = pInst;
|
|
|
|
/* set the allocated flag */
|
|
|
|
pTimer->timerFunc.allocated = TRUE;
|
|
}
|
|
else
|
|
return ERROR;
|
|
|
|
/* store the timer information in the pCookie */
|
|
|
|
*pCookie = pInst->pDrvCtrl;
|
|
|
|
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "%s:Line:%d\r\n",
|
|
(int)__FUNCTION__, __LINE__);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerDisableInternal - disable the timer without spinlock
|
|
*
|
|
* This routine stops the timer and disables interrupt generation for the
|
|
* requested hardware timer without spinlock.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO: N/A
|
|
*
|
|
*/
|
|
|
|
LOCAL void armv7AuxTimerDisableInternal
|
|
(
|
|
ARMV7_AUX_TIMER_DATA * pTimer
|
|
)
|
|
{
|
|
VXB_DEVICE_ID pInst;
|
|
|
|
if (!pTimer->isEnabled)
|
|
return;
|
|
|
|
/* disable the timer */
|
|
|
|
armv7AuxTimerWriteReg (pTimer->isVirtual, AUX_TIMER_REG_CTL,
|
|
AUX_TIMER_CTRL_IMASK);
|
|
|
|
/* disable the timer interrupt */
|
|
|
|
pInst = pTimer->pInst;
|
|
|
|
vxbIntDisable (pInst, 0, armv7AuxTimerInt, pTimer);
|
|
|
|
pTimer->maxCount = ARMV7_AUX_TIMER_MAX_COUNT;
|
|
pTimer->isEnabled = FALSE;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerRelease - release the timer resource
|
|
*
|
|
* This is the function called to release a timer device.
|
|
*
|
|
* RETURNS: OK or ERROR if parameter is not valid.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS armv7AuxTimerRelease
|
|
(
|
|
VXB_DEVICE_ID pInst,
|
|
void* pCookie
|
|
)
|
|
{
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
struct vxbTimerFunctionality * pTimerFunc;
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "%s:Line:%d\r\n",
|
|
(int)__FUNCTION__, __LINE__);
|
|
|
|
|
|
pTimer = (ARMV7_AUX_TIMER_DATA *)pCookie;
|
|
if ((pTimer == NULL) ||
|
|
(pTimer->pInst != pInst) ||
|
|
(!pTimer->timerFunc.allocated))
|
|
{
|
|
uartf("%s,%d: pTimer is bad!\r\n",__FILE__,__LINE__);
|
|
return ERROR;
|
|
}
|
|
|
|
pTimerFunc = &(pTimer->timerFunc);
|
|
|
|
SPIN_LOCK_ISR_TAKE (&pTimer->spinLock);
|
|
|
|
/* check that timer is allocated */
|
|
|
|
if (!pTimerFunc->allocated)
|
|
{
|
|
SPIN_LOCK_ISR_GIVE (&pTimer->spinLock);
|
|
return ERROR;
|
|
}
|
|
|
|
/* disable the timer */
|
|
|
|
(void)armv7AuxTimerDisableInternal (pTimer);
|
|
|
|
/* reset the autoReload flag */
|
|
|
|
if (pTimer->autoReload)
|
|
{
|
|
pTimer->autoReload = FALSE;
|
|
}
|
|
|
|
pTimer->pIsrFunc = NULL;
|
|
pTimer->arg = 0;
|
|
pTimer->flags = 0;
|
|
|
|
/* reset the timer allocated flag */
|
|
|
|
pTimerFunc->allocated = FALSE;
|
|
pTimer->pInst = NULL;
|
|
|
|
SPIN_LOCK_ISR_GIVE (&pTimer->spinLock);
|
|
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "%s:Line:%d\r\n",
|
|
(int)__FUNCTION__, __LINE__);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerRolloverGet - retrieve the maximum value of the counter
|
|
*
|
|
* This is the function called to retrieve the maximum value of the counter.
|
|
* The maximum value is returned in 'pCount' parameter.
|
|
*
|
|
* RETURNS: OK or ERROR if the parameter is invalid.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS armv7AuxTimerRolloverGet
|
|
(
|
|
void * pCookie,
|
|
UINT32 * pCount
|
|
)
|
|
{
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
|
|
VXB_ASSERT_NONNULL (pCount, ERROR);
|
|
|
|
/* free run counter, always get the MAX count for timestamp */
|
|
|
|
pTimer = (ARMV7_AUX_TIMER_DATA *)pCookie;
|
|
if (pTimer == NULL)
|
|
{
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_ERR,"%s,%d: pTimer is bad!\r\n",__FILE__,__LINE__);
|
|
return ERROR;
|
|
}
|
|
*pCount = pTimer->maxCount;
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO,
|
|
"armv7AuxTimerRolloverGet: %u. for vx68\r\n", pTimer->maxCount);
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO,
|
|
"armv7AuxTimerRolloverGet: %u\r\n", ARMV7_AUX_TIMER_MAX_COUNT);
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerCountGet - retrieve the current value of the counter
|
|
*
|
|
* This function is used to retrieve the current value of the counter.
|
|
* The current value is returned in 'pCount' parameter.
|
|
*
|
|
* RETURNS: OK or ERROR if the parameter is invalid.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS armv7AuxTimerCountGet
|
|
(
|
|
void * pCookie,
|
|
UINT32 * pCount
|
|
)
|
|
{
|
|
UINT64 cnt64;
|
|
UINT32 cnt32;
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "armv7AuxTimerCountGet\r\n");
|
|
VXB_ASSERT_NONNULL (pCookie, ERROR);
|
|
VXB_ASSERT_NONNULL (pCount, ERROR);
|
|
|
|
|
|
pTimer = (ARMV7_AUX_TIMER_DATA *)pCookie;
|
|
|
|
/* the timer counter is from the lower 32 bits of the 64 bits register */
|
|
|
|
cnt64 = armv7AuxTimerGetTimerCnt (pTimer->isVirtual);
|
|
cnt32 = (UINT32)(cnt64 % ((UINT32)ARMV7_AUX_TIMER_MAX_COUNT + 1));
|
|
*pCount = cnt32;
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO,
|
|
"armv7AuxTimerCountGet: cnt32=%u\r\n", cnt32);
|
|
return OK;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerCountGet64 - retrieve the current 64bit value of the counter
|
|
*
|
|
* This function is used to retrieve the current 64bit value of the counter.
|
|
* The current value is returned in 'pCount' parameter.
|
|
*
|
|
* RETURNS: OK or ERROR if the parameter is invalid.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
/*
|
|
Note-20201112: verified bits[63-31] are sign bits:
|
|
-> testtmcount
|
|
v_val=0xffffffff_f859af64; p_val=0xffffffff_f859af66
|
|
-> testtmcount
|
|
v_val=0xffffffff_fd1b6b3d; p_val=0xffffffff_fd1b6b3f
|
|
-> testtmcount
|
|
v_val=0x0_02d50ffb; p_val=0x0_02d50ffe
|
|
|
|
64bit is NOT supported! This func is only a placeholder.
|
|
*/
|
|
LOCAL STATUS armv7AuxTimerCountGet64
|
|
(
|
|
void * pCookie,
|
|
UINT64 * pCount64
|
|
)
|
|
{
|
|
UINT64 cnt64;
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "armv7AuxTimerCountGet64\r\n");
|
|
VXB_ASSERT_NONNULL (pCookie, ERROR);
|
|
VXB_ASSERT_NONNULL (pCount64, ERROR);
|
|
|
|
pTimer = (ARMV7_AUX_TIMER_DATA *)pCookie;
|
|
|
|
/* the timer counter is from the lower 32 bits of the 64 bits register */
|
|
cnt64 = armv7AuxTimerGetTimerCnt (pTimer->isVirtual);
|
|
*pCount64 = cnt64;
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO,
|
|
"armv7AuxTimerCountGet64:cnt64=%x_%08x\r\n", (UINT32)(cnt64>>32), (UINT32)(cnt64&0xFFFFFFFF));
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerISRSet - set a function to be called on the timer interrupt
|
|
*
|
|
* This function is called to set a function which can be called whenever
|
|
* the timer interrupt occurs.
|
|
*
|
|
* RETURNS: OK or ERROR if the parameter is invalid.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS armv7AuxTimerISRSet
|
|
(
|
|
void * pCookie,
|
|
void (*pFunc)(int),
|
|
int arg
|
|
)
|
|
{
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "armv7AuxTimerISRSet\r\n");
|
|
VXB_ASSERT_NONNULL (pCookie, ERROR);
|
|
|
|
pTimer = (ARMV7_AUX_TIMER_DATA *)pCookie;
|
|
|
|
|
|
/* take the spinlock to update pIsrFunc and arg atomically */
|
|
|
|
SPIN_LOCK_ISR_TAKE (&pTimer->spinLock);
|
|
|
|
/* store the interrupt routine and argument information */
|
|
|
|
pTimer->pIsrFunc = pFunc;
|
|
pTimer->arg = arg;
|
|
|
|
/* release the spinlock */
|
|
|
|
SPIN_LOCK_ISR_GIVE (&pTimer->spinLock);
|
|
|
|
return OK;
|
|
}
|
|
|
|
#ifdef _WRS_CONFIG_SMP
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armGenTimerExecOnCpu - execute a function on the specified CPU
|
|
*
|
|
* This routine is called to execute a function on the specified CPU.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
LOCAL void armGenTimerExecOnCpu
|
|
(
|
|
ULONG * pParams,
|
|
size_t paramSize
|
|
)
|
|
{
|
|
void * func = (void *)pParams[0];
|
|
void * pCookie = (void *)pParams[1];
|
|
UINT32 maxTimerCount;
|
|
|
|
/* Execute the specified function with parameters */
|
|
|
|
if ((STATUS (*)(void *))func == armv7AuxTimerDisable)
|
|
{
|
|
(void)armv7AuxTimerDisable (pCookie);
|
|
}
|
|
else if ((STATUS (*)(void *, UINT32))func == armv7AuxTimerEnable)
|
|
{
|
|
maxTimerCount = (UINT32)pParams[2];
|
|
(void)armv7AuxTimerEnable (pCookie, maxTimerCount);
|
|
}
|
|
}
|
|
|
|
#endif /* _WRS_CONFIG_SMP */
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerDisable - disable the timer interrupt
|
|
*
|
|
* This function is called to disable the timer interrupt.
|
|
*
|
|
* RETURNS: OK or ERROR if timer is not disabled
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
LOCAL STATUS armv7AuxTimerDisable
|
|
(
|
|
void * pCookie
|
|
)
|
|
{
|
|
#ifdef _WRS_CONFIG_SMP
|
|
cpuset_t cpuSet;
|
|
ULONG cpcParam[2];
|
|
#endif /* _WRS_CONFIG_SMP */
|
|
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "armv7AuxTimerDisable\r\n");
|
|
VXB_ASSERT_NONNULL (pCookie, ERROR);
|
|
|
|
/* retrieve the pTimer */
|
|
|
|
pTimer = (ARMV7_AUX_TIMER_DATA *)pCookie;
|
|
if (pTimer == NULL)
|
|
return ERROR;
|
|
|
|
#ifdef _WRS_CONFIG_SMP
|
|
|
|
/* if not being executed on the boot processor ... */
|
|
|
|
if (vxCpuIndexGet () != 0)
|
|
{
|
|
/* send CPC to boot processor only - CPU 0 */
|
|
|
|
CPUSET_ZERO (cpuSet);
|
|
CPUSET_SET (cpuSet, 0);
|
|
|
|
cpcParam[0] = (ULONG) armv7AuxTimerDisable;
|
|
cpcParam[1] = (ULONG) pCookie;
|
|
|
|
/* if not in interrupt context, taskCpuLock */
|
|
|
|
(void) taskCpuLock ();
|
|
|
|
/* execute on boot processor */
|
|
|
|
cpcInvoke (cpuSet,
|
|
(CPC_FUNC) armGenTimerExecOnCpu,
|
|
cpcParam,
|
|
sizeof (cpcParam),
|
|
VX_CPC_SYNC);
|
|
|
|
/* if not in interrupt context, taskCpuUnlock */
|
|
|
|
(void) taskCpuUnlock ();
|
|
|
|
return OK;
|
|
}
|
|
#endif /* _WRS_CONFIG_SMP */
|
|
|
|
SPIN_LOCK_ISR_TAKE (&pTimer->spinLock);
|
|
armv7AuxTimerDisableInternal (pTimer); /* close interrupt */
|
|
SPIN_LOCK_ISR_GIVE (&pTimer->spinLock);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerEnable - enable the timer interrupt
|
|
*
|
|
* This function enables the timer interrupt.
|
|
*
|
|
* RETURNS: OK or ERROR if timer is not enabled
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
LOCAL STATUS armv7AuxTimerEnable
|
|
(
|
|
void * pCookie,
|
|
UINT32 maxTimerCount
|
|
)
|
|
{
|
|
#ifdef _WRS_CONFIG_SMP
|
|
cpuset_t cpuSet;
|
|
ULONG cpcParam[3];
|
|
#endif /* _WRS_CONFIG_SMP */
|
|
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
struct vxbTimerFunctionality * pTimerFunc;
|
|
VXB_DEVICE_ID pInst;
|
|
|
|
VXB_ASSERT_NONNULL (pCookie, ERROR);
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO,
|
|
"armv7AuxTimerEnable: New TimerCount: %u (0x%x)\r\n", maxTimerCount,maxTimerCount);
|
|
|
|
if ((maxTimerCount > ARMV7_AUX_TIMER_MAX_COUNT) || (maxTimerCount == 0))
|
|
{
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO,
|
|
"armv7AuxTimerEnable: This TimerCount is invalid!\r\n");
|
|
return ERROR;
|
|
}
|
|
|
|
/* retrieve the pTimer */
|
|
|
|
pTimer = (ARMV7_AUX_TIMER_DATA *)pCookie;
|
|
pTimerFunc = &(pTimer->timerFunc);
|
|
pInst = pTimer->pInst;
|
|
|
|
#ifdef _WRS_CONFIG_SMP
|
|
|
|
/* if not being executed on the boot processor */
|
|
|
|
if (vxCpuIndexGet() != 0)
|
|
{
|
|
/* send CPC to boot processor only - CPU 0 */
|
|
|
|
CPUSET_ZERO (cpuSet);
|
|
CPUSET_SET (cpuSet, 0);
|
|
|
|
cpcParam[0] = (ULONG) armv7AuxTimerEnable;
|
|
cpcParam[1] = (ULONG) pCookie;
|
|
cpcParam[2] = (ULONG) maxTimerCount;
|
|
|
|
/* if not in interrupt context, taskCpuLock */
|
|
|
|
(void) taskCpuLock ();
|
|
|
|
/* execute on boot processor */
|
|
|
|
cpcInvoke (cpuSet,
|
|
(CPC_FUNC) armGenTimerExecOnCpu,
|
|
cpcParam,
|
|
sizeof(cpcParam),
|
|
VX_CPC_SYNC);
|
|
|
|
/* if not in interrupt context, taskCpuUnlock */
|
|
|
|
(void) taskCpuUnlock ();
|
|
|
|
return OK;
|
|
}
|
|
#endif /* _WRS_CONFIG_SMP */
|
|
|
|
|
|
SPIN_LOCK_ISR_TAKE (&pTimer->spinLock);
|
|
|
|
/* if the timer is already running, stop it before making adjustments */
|
|
|
|
if (pTimer->isEnabled)
|
|
{
|
|
(void)armv7AuxTimerDisableInternal (pTimer);
|
|
}
|
|
|
|
pTimer->maxCount = maxTimerCount;
|
|
|
|
/* recalculate ticksPerSecond */
|
|
|
|
pTimerFunc->ticksPerSecond = pTimerFunc->clkFrequency / pTimer->maxCount;
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "armv7AuxTimerEnable New ticksPerSecond = %d.(should be 60) pTimer->isVirtual %d\r\n",
|
|
pTimerFunc->ticksPerSecond ,pTimer->isVirtual);
|
|
|
|
/* update the timer value register with maxTimerCount */
|
|
|
|
armv7AuxTimerWriteReg (pTimer->isVirtual, AUX_TIMER_REG_TVAL,
|
|
maxTimerCount);
|
|
|
|
/* set up the timer control register */
|
|
|
|
armv7AuxTimerWriteReg (pTimer->isVirtual, AUX_TIMER_REG_CTL,
|
|
AUX_TIMER_CTRL_ENABLE);
|
|
|
|
pTimer->isEnabled = TRUE;
|
|
SPIN_LOCK_ISR_GIVE (&pTimer->spinLock);
|
|
|
|
if (pTimer->isVirtual)
|
|
{
|
|
if (vxbIntEnable (pInst, 0,armv7AuxTimerInt, pTimer) != OK)
|
|
{
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_ERR,
|
|
"Interrupt enable failed for virtual timer\n");
|
|
/*SPIN_LOCK_ISR_GIVE (&pTimer->spinLock);*/
|
|
return ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pTimer->secPhyAvail &&
|
|
(vxbIntEnable (pInst, 0,armv7AuxTimerInt, pTimer) != OK))
|
|
{
|
|
pTimer->secPhyAvail = FALSE;
|
|
}
|
|
if (pTimer->nonSecPhyAvail &&
|
|
(vxbIntEnable (pInst, 0,armv7AuxTimerInt, pTimer) != OK))
|
|
{
|
|
pTimer->nonSecPhyAvail = FALSE;
|
|
}
|
|
if (!pTimer->secPhyAvail && !pTimer->nonSecPhyAvail)
|
|
{
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_ERR,
|
|
"Interrupt enable failed for physical timer\n");
|
|
/*SPIN_LOCK_ISR_GIVE (&pTimer->spinLock);*/
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "armv7AuxTimerEnable OK\r\n");
|
|
return OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerInt - ISR for the ARM Auxeric Timer
|
|
*
|
|
* This routine handles the ARM Auxeric Timer interrupt.
|
|
*
|
|
* RETURNS : N/A
|
|
*/
|
|
|
|
LOCAL void armv7AuxTimerInt
|
|
(
|
|
ARMV7_AUX_TIMER_DATA * pTimer
|
|
)
|
|
{
|
|
UINT32 ctl;
|
|
|
|
|
|
ctl = armv7AuxTimerReadReg (pTimer->isVirtual, AUX_TIMER_REG_CTL);
|
|
if ((ctl & AUX_TIMER_CTRL_ISTATUS) == 0)
|
|
{
|
|
uartf("\033[34m%s,%d: ctl=0x%x. pTimer->maxCount=0x%x. No TimerInt. Return directly!\033[m\r\n",__FILE__,__LINE__,ctl, pTimer->maxCount);
|
|
return;
|
|
}
|
|
|
|
/* auto reload */
|
|
|
|
if ((pTimer->timerFunc.features & VXB_TIMER_AUTO_RELOAD) != 0)
|
|
{
|
|
armv7AuxTimerWriteReg (pTimer->isVirtual, AUX_TIMER_REG_TVAL,
|
|
pTimer->maxCount);
|
|
}
|
|
else
|
|
{
|
|
armv7AuxTimerWriteReg (pTimer->isVirtual, AUX_TIMER_REG_CTL,
|
|
AUX_TIMER_CTRL_IMASK);
|
|
}
|
|
|
|
/* call the ISR hooked by armv7AuxTimerISRSet() */
|
|
|
|
if (pTimer->pIsrFunc != NULL)
|
|
pTimer->pIsrFunc (pTimer->arg);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* armv7AuxTimerAttach - attach arm generic timer device
|
|
*
|
|
* This is the arm generic timer initialization routine.
|
|
*
|
|
* RETURNS: OK, or ERROR if initialization failed.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
LOCAL STATUS armv7AuxTimerAttach
|
|
(
|
|
VXB_DEVICE_ID pDev
|
|
)
|
|
{
|
|
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
struct vxbTimerFunctionality * pTimerFunc;
|
|
HCF_DEVICE * pHcf = NULL;
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "Enter armv7AuxTimerAttach\r\n");
|
|
|
|
/* check for valid parameter */
|
|
|
|
VXB_ASSERT_NONNULL (pDev, ERROR);
|
|
|
|
/* get the HCF_DEVICE address */
|
|
|
|
pHcf = hcfDeviceGet(pDev);
|
|
if (pHcf == NULL)
|
|
{
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_ERR,
|
|
"There is no HCF for this device.\r\n");
|
|
return ERROR;
|
|
}
|
|
|
|
/* allocate the memory for the timer structure */
|
|
|
|
pTimer = (ARMV7_AUX_TIMER_DATA *)hwMemAlloc (sizeof(ARMV7_AUX_TIMER_DATA));
|
|
if (pTimer == NULL)
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
pArmv7AuxTimer = pTimer;
|
|
|
|
/*
|
|
* Use virtual timer by default, but if it's not available,
|
|
* use physical timer.
|
|
*/
|
|
|
|
pTimer->secPhyAvail = FALSE;
|
|
pTimer->isVirtual = TRUE;
|
|
pTimer->nonSecPhyAvail = FALSE;
|
|
|
|
if (vxbIntConnect (pDev, 0, /*pTimer->intRes[AUX_TIMER_SECURE_PHY_PPI],*/
|
|
armv7AuxTimerInt, pTimer) != OK)
|
|
{
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_ERR,
|
|
"Secure physical timer is not available\r\n");
|
|
pTimer->isVirtual = FALSE;
|
|
}
|
|
|
|
/* locate the timer functionality data structure */
|
|
|
|
pTimerFunc = &(pTimer->timerFunc);
|
|
|
|
if (devResourceGet(pHcf,"minClkRate",HCF_RES_INT,
|
|
(void *)&pTimerFunc->minFrequency) != OK)
|
|
{
|
|
/*default value. */
|
|
pTimerFunc->minFrequency = ARMV7_AUX_TIMER_DEFAULT_MIN_FREQ;
|
|
}
|
|
|
|
if (devResourceGet(pHcf,"maxClkRate",HCF_RES_INT,
|
|
(void *)&pTimerFunc->maxFrequency) != OK)
|
|
{
|
|
/*default value*/
|
|
pTimerFunc->maxFrequency = ARMV7_AUX_TIMER_DEFAULT_MAX_FREQ;
|
|
}
|
|
|
|
pTimerFunc->clkFrequency = __inline__GetCntFreq();
|
|
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO,
|
|
"Clock Frequency: %d\r\n", pTimerFunc->clkFrequency);
|
|
|
|
if ((pTimerFunc->clkFrequency < ARMV7_AUX_TIMER_MIN_CLK_FREQ) ||
|
|
(pTimerFunc->clkFrequency > ARMV7_AUX_TIMER_MAX_CLK_FREQ))
|
|
{
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_ERR,
|
|
"clkFrequency wrong for ARM generic timer\r\n");
|
|
goto errOut;
|
|
}
|
|
|
|
/* store the feature provided by the timer */
|
|
|
|
pTimerFunc->features = VXB_TIMER_CAN_INTERRUPT |
|
|
VXB_TIMER_INTERMEDIATE_COUNT |
|
|
VXB_TIMER_SIZE_32 |
|
|
VXB_TIMER_SIZE_64 |
|
|
VXB_TIMER_AUTO_RELOAD ;
|
|
|
|
/* set the default ticks per second */
|
|
|
|
pTimerFunc->ticksPerSecond = ARMV7_AUX_TIMER_DEFAULT_TPS;
|
|
|
|
/*
|
|
* Requires to initialize maxCount field for timestamp timer.
|
|
* It will be used as a return value for sysTimestampPeriod().
|
|
*/
|
|
|
|
pTimer->maxCount = pTimerFunc->clkFrequency/ARMV7_AUX_TIMER_DEFAULT_TPS;
|
|
|
|
pTimerFunc->rolloverPeriod = pTimer->maxCount / pTimerFunc->clkFrequency;
|
|
|
|
(void)strncpy (pTimerFunc->timerName,
|
|
ARMV7_AUX_TIMER_NAME, MAX_DRV_NAME_LEN);
|
|
|
|
|
|
/* populate the function pointers */
|
|
|
|
pTimerFunc->timerAllocate = armv7AuxTimerAllocate;
|
|
pTimerFunc->timerRelease = armv7AuxTimerRelease;
|
|
pTimerFunc->timerRolloverGet = armv7AuxTimerRolloverGet;
|
|
pTimerFunc->timerCountGet = armv7AuxTimerCountGet;
|
|
pTimerFunc->timerCountGet64 = armv7AuxTimerCountGet64;
|
|
pTimerFunc->timerDisable = armv7AuxTimerDisable;
|
|
pTimerFunc->timerEnable = armv7AuxTimerEnable;
|
|
pTimerFunc->timerISRSet = armv7AuxTimerISRSet;
|
|
|
|
/* initialize the spinlock */
|
|
|
|
SPIN_LOCK_ISR_INIT (&pTimer->spinLock, 0);
|
|
|
|
/* publish methods */
|
|
|
|
pDev->pMethods = davinciTimerDrv_methods;
|
|
pDev->pDrvCtrl = pTimer;
|
|
AUX_TIMER_DBG (AUX_TIMER_DBG_INFO, "armv7AuxTimerAttach OK\r\n");
|
|
|
|
return OK;
|
|
|
|
errOut:
|
|
|
|
hwMemFree ((char * )pTimer);
|
|
return ERROR;
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* armv7AuxTimerFuncGet - method to retrieve the timer functionality
|
|
*
|
|
* This function is the driver method used to retrieve the timer functionality.
|
|
*
|
|
* RETURNS: OK or ERROR if functionality is not retrieved.
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
LOCAL STATUS armv7AuxTimerFuncGet
|
|
(
|
|
VXB_DEVICE_ID pInst,
|
|
struct vxbTimerFunctionality** pTimerFunc,
|
|
int timerNo
|
|
)
|
|
{
|
|
ARMV7_AUX_TIMER_DATA * pTimerData;
|
|
|
|
/* since the device supports only a single timer functionality */
|
|
|
|
if ( timerNo != 0)
|
|
return ERROR;
|
|
|
|
/* check the validity of parameters */
|
|
|
|
if ( (pInst == NULL) || (pInst->pDrvCtrl == NULL) || (pTimerFunc == NULL) )
|
|
{
|
|
return ERROR;
|
|
}
|
|
|
|
/* retrieve the timer specific data */
|
|
|
|
pTimerData = (ARMV7_AUX_TIMER_DATA *)(pInst->pDrvCtrl);
|
|
|
|
/* update the timer functionality pointer */
|
|
|
|
*pTimerFunc = &(pTimerData->timerFunc);
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
/* delay 1us */
|
|
void armV7AuxUsDelay
|
|
(
|
|
int delay /* length of time in US to delay */
|
|
)
|
|
{
|
|
ARMV7_AUX_TIMER_DATA * pTimer;
|
|
struct vxbTimerFunctionality * pTimerFunc;
|
|
|
|
volatile register UINT32 oldVal;
|
|
volatile register UINT32 newVal;
|
|
volatile register UINT32 decElapsed = 0;
|
|
register UINT32 totalDelta;
|
|
UINT32 maxTickCount;
|
|
/*UINT32 minTickCount = 0;*/
|
|
UINT32 ticksPerUs = 0;
|
|
|
|
int loopcount = 0;
|
|
|
|
UINT64 cnt64;
|
|
|
|
if(delay == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pTimer = pArmv7AuxTimer;
|
|
|
|
if(pTimer == NULL)
|
|
{
|
|
uartf("No valid armv7 gen timer!\r\n");
|
|
return;
|
|
}
|
|
pTimerFunc = &(pTimer->timerFunc);
|
|
|
|
ticksPerUs = pTimerFunc->clkFrequency / 1000000;
|
|
if(ticksPerUs != 50)
|
|
{
|
|
uartf("ticksPerUs-%d should be 50 for this board, please check it!\r\n", ticksPerUs);
|
|
}
|
|
|
|
maxTickCount = pTimer->maxCount;
|
|
totalDelta = delay * (ticksPerUs); /* total clock ticks for long delay */
|
|
|
|
cnt64 = armv7AuxTimerGetTimerCnt (pTimer->isVirtual);
|
|
oldVal = (UINT32)(cnt64 % ((UINT32)ARMV7_AUX_TIMER_MAX_COUNT + 1));
|
|
|
|
|
|
while (decElapsed < totalDelta)
|
|
{
|
|
loopcount++;
|
|
|
|
cnt64 = armv7AuxTimerGetTimerCnt (pTimer->isVirtual);
|
|
newVal = (UINT32)(cnt64 % ((UINT32)ARMV7_AUX_TIMER_MAX_COUNT + 1));
|
|
|
|
if(newVal == oldVal)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* no rollover. count down... */
|
|
|
|
if (newVal < oldVal)
|
|
{
|
|
decElapsed += (oldVal - newVal);
|
|
}
|
|
/* rollover */
|
|
else
|
|
{
|
|
decElapsed += ((maxTickCount - newVal) + oldVal);
|
|
}
|
|
|
|
oldVal = newVal;
|
|
}
|
|
}
|
|
|
|
|