1/* 2 * Copyright 2010-2011 Calxeda, Inc. 3 * 4 * Based on arm926ejs/mx27/timer.c 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the Free 8 * Software Foundation; either version 2 of the License, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include <common.h> 21#include <div64.h> 22#include <linux/types.h> /* for size_t */ 23#include <linux/stddef.h> /* for NULL */ 24#include <asm/io.h> 25#include <asm/arch-armv7/systimer.h> 26 27#undef SYSTIMER_BASE 28#define SYSTIMER_BASE 0xFFF34000 /* Timer 0 and 1 base */ 29#define SYSTIMER_RATE 150000000 30 31static ulong timestamp; 32static ulong lastinc; 33static struct systimer *systimer_base = (struct systimer *)SYSTIMER_BASE; 34 35/* 36 * Start the timer 37 */ 38int timer_init(void) 39{ 40 /* 41 * Setup timer0 42 */ 43 writel(SYSTIMER_RELOAD, &systimer_base->timer0load); 44 writel(SYSTIMER_RELOAD, &systimer_base->timer0value); 45 writel(SYSTIMER_EN | SYSTIMER_32BIT, &systimer_base->timer0control); 46 47 reset_timer_masked(); 48 49 return 0; 50 51} 52 53#define TICK_PER_TIME ((SYSTIMER_RATE + CONFIG_SYS_HZ / 2) / CONFIG_SYS_HZ) 54#define NS_PER_TICK (1000000000 / SYSTIMER_RATE) 55 56static inline unsigned long long tick_to_time(unsigned long long tick) 57{ 58 do_div(tick, TICK_PER_TIME); 59 return tick; 60} 61 62static inline unsigned long long time_to_tick(unsigned long long time) 63{ 64 return time * TICK_PER_TIME; 65} 66 67static inline unsigned long long us_to_tick(unsigned long long us) 68{ 69 unsigned long long tick = us * 1000; 70 tick += NS_PER_TICK - 1; 71 do_div(tick, NS_PER_TICK); 72 return tick; 73} 74 75unsigned long long get_ticks(void) 76{ 77 ulong now = ~readl(&systimer_base->timer0value); 78 79 if (now >= lastinc) /* normal mode (non roll) */ 80 /* move stamp forward with absolut diff ticks */ 81 timestamp += (now - lastinc); 82 else /* we have rollover of incrementer */ 83 timestamp += (0xFFFFFFFF - lastinc) + now; 84 lastinc = now; 85 return timestamp; 86} 87 88/* 89 * Delay x useconds AND preserve advance timstamp value 90 * assumes timer is ticking at 1 msec 91 */ 92void __udelay(ulong usec) 93{ 94 unsigned long long tmp; 95 ulong tmo; 96 97 tmo = us_to_tick(usec); 98 tmp = get_ticks() + tmo; /* get current timestamp */ 99 100 while (get_ticks() < tmp) /* loop till event */ 101 /*NOP*/; 102} 103 104ulong get_timer(ulong base) 105{ 106 return get_timer_masked() - base; 107} 108 109void reset_timer_masked(void) 110{ 111 lastinc = ~readl(&systimer_base->timer0value); 112 timestamp = 0; 113} 114 115void reset_timer(void) 116{ 117 reset_timer_masked(); 118} 119 120ulong get_timer_masked(void) 121{ 122 return tick_to_time(get_ticks()); 123} 124 125ulong get_tbclk(void) 126{ 127 return CONFIG_SYS_HZ; 128} 129