blob: f120de5dda4353924d149b0835153a749f2875d0 [file] [log] [blame]
/*
* (C) Copyright 2012
* Marvell Semiconductor <www.marvell.com>
* Written-by: Neil Zhang <zhangwm@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/arch/cpu.h>
#include <asm/arch/pxa182x.h>
#define TIMER 0 /* Use TIMER 0 */
/* Each timer has 3 match registers */
#define MATCH_CMP(x) ((3 * TIMER) + x)
#define TIMER_LOAD_VAL 0xffffffff
#define COUNT_RD_REQ 0x1
DECLARE_GLOBAL_DATA_PTR;
/* Using gd->arch.tbu from timestamp and gd->arch.tbl for lastdec */
/*
* For preventing risk of instability in reading counter value,
* first set read request to register cvwr and then read same
* register after it captures counter value.
*/
ulong read_timer(void)
{
struct pxa182xtmr_registers *timers =
(struct pxa182xtmr_registers *)PXA182X_TMR1_BASE;
int loop = 100;
ulong val;
writel(COUNT_RD_REQ, &timers->cvwr);
while (loop--)
val = readl(&timers->cvwr);
/*
* This stop gcc complain and prevent loop mistake init to 0
*/
val = readl(&timers->cvwr);
return val;
}
ulong get_timer_masked(void)
{
ulong now = read_timer();
if (now >= gd->arch.tbl) {
/* normal mode */
gd->arch.tbu += now - gd->arch.tbl;
} else {
/* we have an overflow ... */
gd->arch.tbu += now + TIMER_LOAD_VAL - gd->arch.tbl;
}
gd->arch.tbl = now;
return gd->arch.tbu;
}
ulong get_timer(ulong base)
{
return (get_timer_masked() / (CONFIG_SYS_HZ_CLOCK / 1000)) - base;
}
void __udelay(unsigned long usec)
{
long delayticks = (usec * (CONFIG_SYS_HZ_CLOCK / 1000000));
ulong now, last = read_timer();
while (delayticks > 0) {
now = read_timer();
if (last > now) /* count up timer overflow */
delayticks -= TIMER_LOAD_VAL - last + now + 1;
else
delayticks -= now - last;
last = now;
}
}
/*
* init the Timer
*/
int timer_init(void)
{
struct pxa182xapbc_registers *apb1clkres =
(struct pxa182xapbc_registers *)PXA182X_APBC_BASE;
struct pxa182xtmr_registers *timers =
(struct pxa182xtmr_registers *)PXA182X_TMR1_BASE;
/* Enable Timer clock at 3.25 MHZ */
writel(APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3),
&apb1clkres->timers);
/* load value into timer */
writel(0x0, &timers->clk_ctrl);
/* Use Timer 0 Match Resiger 0 */
writel(TIMER_LOAD_VAL, &timers->match[MATCH_CMP(0)]);
/* Preload value is 0 */
writel(0x0, &timers->preload[TIMER]);
/* Enable match comparator 0 for Timer 0 */
writel(0x1, &timers->preload_ctrl[TIMER]);
/* Enable timer 0 */
writel(0x1, &timers->cer);
/* init the gd->arch.tbu and gd->arch.tbl value */
gd->arch.tbl = read_timer();
gd->arch.tbu = 0;
return 0;
}
/*
* This function is derived from PowerPC code (read timebase as long long).
* On ARM it just returns the timer value.
*/
unsigned long long get_ticks(void)
{
return get_timer(0);
}
/*
* This function is derived from PowerPC code (timebase clock frequency).
* On ARM it returns the number of timer ticks per second.
*/
ulong get_tbclk(void)
{
return (ulong)CONFIG_SYS_HZ_CLOCK;
}