| /* |
| * (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; |
| } |