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 * See file CREDITS for list of people who contributed to this 20 * project. 21 * 22 * This program is free software; you can redistribute it and/or 23 * modify it under the terms of the GNU General Public License as 24 * published by the Free Software Foundation; either version 2 of 25 * the License, or (at your option) any later version. 26 * 27 * This program is distributed in the hope that it will be useful, 28 * but WITHOUT ANY WARRANTY; without even the implied warranty of 29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 * GNU General Public License for more details. 31 * 32 * You should have received a copy of the GNU General Public License 33 * along with this program; if not, write to the Free Software 34 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 35 * MA 02111-1307 USA 36 */ 37 38#include <common.h> 39 40#define TIMER_LOAD_VAL 0xffffffff 41 42/* macro to read the 32 bit timer */ 43#define READ_TIMER (*(volatile ulong *)(CONFIG_SYS_TIMERBASE+4)) 44 45DECLARE_GLOBAL_DATA_PTR; 46 47#define timestamp gd->tbl 48#define lastdec gd->lastinc 49 50#define TIMER_ENABLE (1 << 7) 51#define TIMER_MODE_MSK (1 << 6) 52#define TIMER_MODE_FR (0 << 6) 53#define TIMER_MODE_PD (1 << 6) 54 55#define TIMER_INT_EN (1 << 5) 56#define TIMER_PRS_MSK (3 << 2) 57#define TIMER_PRS_8S (1 << 3) 58#define TIMER_SIZE_MSK (1 << 2) 59#define TIMER_ONE_SHT (1 << 0) 60 61int timer_init (void) 62{ 63 ulong tmr_ctrl_val; 64 65 /* 1st disable the Timer */ 66 tmr_ctrl_val = *(volatile ulong *)(CONFIG_SYS_TIMERBASE + 8); 67 tmr_ctrl_val &= ~TIMER_ENABLE; 68 *(volatile ulong *)(CONFIG_SYS_TIMERBASE + 8) = tmr_ctrl_val; 69 70 /* 71 * The Timer Control Register has one Undefined/Shouldn't Use Bit 72 * So we should do read/modify/write Operation 73 */ 74 75 /* 76 * Timer Mode : Free Running 77 * Interrupt : Disabled 78 * Prescale : 8 Stage, Clk/256 79 * Tmr Siz : 16 Bit Counter 80 * Tmr in Wrapping Mode 81 */ 82 tmr_ctrl_val = *(volatile ulong *)(CONFIG_SYS_TIMERBASE + 8); 83 tmr_ctrl_val &= ~(TIMER_MODE_MSK | TIMER_INT_EN | TIMER_PRS_MSK | TIMER_SIZE_MSK | TIMER_ONE_SHT ); 84 tmr_ctrl_val |= (TIMER_ENABLE | TIMER_PRS_8S); 85 86 *(volatile ulong *)(CONFIG_SYS_TIMERBASE + 8) = tmr_ctrl_val; 87 88 /* init the timestamp and lastdec value */ 89 reset_timer_masked(); 90 91 return 0; 92} 93 94/* 95 * timer without interrupts 96 */ 97ulong get_timer (ulong base) 98{ 99 return get_timer_masked () - base; 100} 101 102/* delay x useconds AND preserve advance timestamp value */ 103void __udelay (unsigned long usec) 104{ 105 ulong tmo, tmp; 106 107 if(usec >= 1000){ /* if "big" number, spread normalization to seconds */ 108 tmo = usec / 1000; /* start to normalize for usec to ticks per sec */ 109 tmo *= CONFIG_SYS_HZ; /* find number of "ticks" to wait to achieve target */ 110 tmo /= 1000; /* finish normalize. */ 111 }else{ /* else small number, don't kill it prior to HZ multiply */ 112 tmo = usec * CONFIG_SYS_HZ; 113 tmo /= (1000*1000); 114 } 115 116 tmp = get_timer (0); /* get current timestamp */ 117 if( (tmo + tmp + 1) < tmp ) /* if setting this fordward will roll time stamp */ 118 reset_timer_masked (); /* reset "advancing" timestamp to 0, set lastdec value */ 119 else 120 tmo += tmp; /* else, set advancing stamp wake up time */ 121 122 while (get_timer_masked () < tmo)/* loop till event */ 123 /*NOP*/; 124} 125 126void reset_timer_masked (void) 127{ 128 /* reset time */ 129 lastdec = READ_TIMER; /* capure current decrementer value time */ 130 timestamp = 0; /* start "advancing" time stamp from 0 */ 131} 132 133ulong get_timer_masked (void) 134{ 135 ulong now = READ_TIMER; /* current tick value */ 136 137 if (lastdec >= now) { /* normal mode (non roll) */ 138 /* normal mode */ 139 timestamp += lastdec - now; /* move stamp fordward with absoulte diff ticks */ 140 } else { /* we have overflow of the count down timer */ 141 /* nts = ts + ld + (TLV - now) 142 * ts=old stamp, ld=time that passed before passing through -1 143 * (TLV-now) amount of time after passing though -1 144 * nts = new "advancing time stamp"...it could also roll and cause problems. 145 */ 146 timestamp += lastdec + TIMER_LOAD_VAL - now; 147 } 148 lastdec = now; 149 150 return timestamp; 151} 152 153/* waits specified delay value and resets timestamp */ 154void udelay_masked (unsigned long usec) 155{ 156 ulong tmo; 157 ulong endtime; 158 signed long diff; 159 160 if (usec >= 1000) { /* if "big" number, spread normalization to seconds */ 161 tmo = usec / 1000; /* start to normalize for usec to ticks per sec */ 162 tmo *= CONFIG_SYS_HZ; /* find number of "ticks" to wait to achieve target */ 163 tmo /= 1000; /* finish normalize. */ 164 } else { /* else small number, don't kill it prior to HZ multiply */ 165 tmo = usec * CONFIG_SYS_HZ; 166 tmo /= (1000*1000); 167 } 168 169 endtime = get_timer_masked () + tmo; 170 171 do { 172 ulong now = get_timer_masked (); 173 diff = endtime - now; 174 } while (diff >= 0); 175} 176 177/* 178 * This function is derived from PowerPC code (read timebase as long long). 179 * On ARM it just returns the timer value. 180 */ 181unsigned long long get_ticks(void) 182{ 183 return get_timer(0); 184} 185 186/* 187 * This function is derived from PowerPC code (timebase clock frequency). 188 * On ARM it returns the number of timer ticks per second. 189 */ 190ulong get_tbclk (void) 191{ 192 ulong tbclk; 193 194 tbclk = CONFIG_SYS_HZ; 195 return tbclk; 196} 197