1/* 2 * Cirrus Logic EP93xx timer support. 3 * 4 * Copyright (C) 2009, 2010 5 * Matthias Kaehlcke <matthias@kaehlcke.net> 6 * 7 * Copyright (C) 2004, 2005 8 * Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.com> 9 * 10 * Based on the original intr.c Cirrus Logic EP93xx Rev D. interrupt support, 11 * author unknown. 12 * 13 * See file CREDITS for list of people who contributed to this project. 14 * 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 2 of the License, or 18 * (at your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, but 21 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 22 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 23 * for more details. 24 * 25 * You should have received a copy of the GNU General Public License along 26 * with this program; if not, write to the Free Software Foundation, Inc., 27 * 675 Mass Ave, Cambridge, MA 02139, USA. 28 */ 29 30#include <common.h> 31#include <linux/types.h> 32#include <asm/arch/ep93xx.h> 33#include <asm/io.h> 34#include <div64.h> 35 36#define TIMER_CLKSEL (1 << 3) 37#define TIMER_ENABLE (1 << 7) 38 39#define TIMER_FREQ 508469 /* ticks / second */ 40#define TIMER_MAX_VAL 0xFFFFFFFF 41 42static struct ep93xx_timer 43{ 44 unsigned long long ticks; 45 unsigned long last_update; 46} timer; 47 48static inline unsigned long clk_to_systicks(unsigned long long clk_ticks) 49{ 50 unsigned long long sys_ticks = (clk_ticks * CONFIG_SYS_HZ); 51 do_div(sys_ticks, TIMER_FREQ); 52 53 return (unsigned long)sys_ticks; 54} 55 56static inline unsigned long long usecs_to_ticks(unsigned long usecs) 57{ 58 unsigned long long ticks = (unsigned long long)usecs * TIMER_FREQ; 59 do_div(ticks, 1000 * 1000); 60 61 return ticks; 62} 63 64static inline unsigned long read_timer(void) 65{ 66 struct timer_regs *timer = (struct timer_regs *)TIMER_BASE; 67 68 return TIMER_MAX_VAL - readl(&timer->timer3.value); 69} 70 71/* 72 * Get the number of ticks (in CONFIG_SYS_HZ resolution) 73 */ 74unsigned long long get_ticks(void) 75{ 76 const unsigned long now = read_timer(); 77 78 if (now >= timer.last_update) 79 timer.ticks += now - timer.last_update; 80 else 81 /* an overflow occurred */ 82 timer.ticks += TIMER_MAX_VAL - timer.last_update + now; 83 84 timer.last_update = now; 85 86 return clk_to_systicks(timer.ticks); 87} 88 89unsigned long get_timer_masked(void) 90{ 91 return get_ticks(); 92} 93 94unsigned long get_timer(unsigned long base) 95{ 96 return get_timer_masked() - base; 97} 98 99void reset_timer_masked(void) 100{ 101 timer.last_update = read_timer(); 102 timer.ticks = 0; 103} 104 105void reset_timer(void) 106{ 107 reset_timer_masked(); 108} 109 110void __udelay(unsigned long usec) 111{ 112 /* read the timer and update timer.ticks */ 113 get_ticks(); 114 115 const unsigned long long target = timer.ticks + usecs_to_ticks(usec); 116 117 while (timer.ticks < target) 118 get_ticks(); 119} 120 121int timer_init(void) 122{ 123 struct timer_regs *timer = (struct timer_regs *)TIMER_BASE; 124 125 /* use timer 3 with 508KHz and free running */ 126 writel(TIMER_CLKSEL, &timer->timer3.control); 127 128 /* set initial timer value 3 */ 129 writel(TIMER_MAX_VAL, &timer->timer3.load); 130 131 /* Enable the timer */ 132 writel(TIMER_ENABLE | TIMER_CLKSEL, 133 &timer->timer3.control); 134 135 reset_timer_masked(); 136 137 return 0; 138} 139 140/* 141 * This function is derived from PowerPC code (timebase clock frequency). 142 * On ARM it returns the number of timer ticks per second. 143 */ 144unsigned long get_tbclk(void) 145{ 146 return CONFIG_SYS_HZ; 147} 148