1/* 2 * (C) Copyright 2003 3 * Texas Instruments <www.ti.com> 4 * 5 * (C) Copyright 2002 6 * Sysgo Real-Time Solutions, GmbH <www.elinos.com> 7 * Marius Groeger <mgroeger@sysgo.de> 8 * 9 * (C) Copyright 2002 10 * Sysgo Real-Time Solutions, GmbH <www.elinos.com> 11 * Alex Zuepke <azu@sysgo.de> 12 * 13 * (C) Copyright 2002-2004 14 * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de> 15 * 16 * (C) Copyright 2004 17 * Philippe Robin, ARM Ltd. <philippe.robin@arm.com> 18 * 19 * (C) Copyright 2008 20 * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> 21 * 22 * See file CREDITS for list of people who contributed to this 23 * project. 24 * 25 * This program is free software; you can redistribute it and/or 26 * modify it under the terms of the GNU General Public License as 27 * published by the Free Software Foundation; either version 2 of 28 * the License, or (at your option) any later version. 29 * 30 * This program is distributed in the hope that it will be useful, 31 * but WITHOUT ANY WARRANTY; without even the implied warranty of 32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 * GNU General Public License for more details. 34 * 35 * You should have received a copy of the GNU General Public License 36 * along with this program; if not, write to the Free Software 37 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 38 * MA 02111-1307 USA 39 */ 40 41#include <common.h> 42#include <asm/proc-armv/ptrace.h> 43#include <s3c6400.h> 44#include <div64.h> 45 46static ulong timer_load_val; 47 48#define PRESCALER 167 49 50static s3c64xx_timers *s3c64xx_get_base_timers(void) 51{ 52 return (s3c64xx_timers *)ELFIN_TIMER_BASE; 53} 54 55/* macro to read the 16 bit timer */ 56static inline ulong read_timer(void) 57{ 58 s3c64xx_timers *const timers = s3c64xx_get_base_timers(); 59 60 return timers->TCNTO4; 61} 62 63/* Internal tick units */ 64/* Last decremneter snapshot */ 65static unsigned long lastdec; 66/* Monotonic incrementing timer */ 67static unsigned long long timestamp; 68 69int timer_init(void) 70{ 71 s3c64xx_timers *const timers = s3c64xx_get_base_timers(); 72 73 /* use PWM Timer 4 because it has no output */ 74 /* 75 * We use the following scheme for the timer: 76 * Prescaler is hard fixed at 167, divider at 1/4. 77 * This gives at PCLK frequency 66MHz approx. 10us ticks 78 * The timer is set to wrap after 100s, at 66MHz this obviously 79 * happens after 10,000,000 ticks. A long variable can thus 80 * keep values up to 40,000s, i.e., 11 hours. This should be 81 * enough for most uses:-) Possible optimizations: select a 82 * binary-friendly frequency, e.g., 1ms / 128. Also calculate 83 * the prescaler automatically for other PCLK frequencies. 84 */ 85 timers->TCFG0 = PRESCALER << 8; 86 if (timer_load_val == 0) { 87 timer_load_val = get_PCLK() / PRESCALER * (100 / 4); /* 100s */ 88 timers->TCFG1 = (timers->TCFG1 & ~0xf0000) | 0x20000; 89 } 90 91 /* load value for 10 ms timeout */ 92 lastdec = timers->TCNTB4 = timer_load_val; 93 /* auto load, manual update of Timer 4 */ 94 timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | 95 TCON_4_UPDATE; 96 97 /* auto load, start Timer 4 */ 98 timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | COUNT_4_ON; 99 timestamp = 0; 100 101 return 0; 102} 103 104/* 105 * timer without interrupts 106 */ 107 108/* 109 * This function is derived from PowerPC code (read timebase as long long). 110 * On ARM it just returns the timer value. 111 */ 112unsigned long long get_ticks(void) 113{ 114 ulong now = read_timer(); 115 116 if (lastdec >= now) { 117 /* normal mode */ 118 timestamp += lastdec - now; 119 } else { 120 /* we have an overflow ... */ 121 timestamp += lastdec + timer_load_val - now; 122 } 123 lastdec = now; 124 125 return timestamp; 126} 127 128/* 129 * This function is derived from PowerPC code (timebase clock frequency). 130 * On ARM it returns the number of timer ticks per second. 131 */ 132ulong get_tbclk(void) 133{ 134 /* We overrun in 100s */ 135 return (ulong)(timer_load_val / 100); 136} 137 138void reset_timer_masked(void) 139{ 140 /* reset time */ 141 lastdec = read_timer(); 142 timestamp = 0; 143} 144 145void reset_timer(void) 146{ 147 reset_timer_masked(); 148} 149 150ulong get_timer_masked(void) 151{ 152 unsigned long long res = get_ticks(); 153 do_div (res, (timer_load_val / (100 * CONFIG_SYS_HZ))); 154 return res; 155} 156 157ulong get_timer(ulong base) 158{ 159 return get_timer_masked() - base; 160} 161 162void set_timer(ulong t) 163{ 164 timestamp = t * (timer_load_val / (100 * CONFIG_SYS_HZ)); 165} 166 167void udelay(unsigned long usec) 168{ 169 unsigned long long tmp; 170 ulong tmo; 171 172 tmo = (usec + 9) / 10; 173 tmp = get_ticks() + tmo; /* get current timestamp */ 174 175 while (get_ticks() < tmp)/* loop till event */ 176 /*NOP*/; 177} 178