1 /* 2 * (C) Copyright 2009 Faraday Technology 3 * Po-Yu Chuang <ratbert@faraday-tech.com> 4 * 5 * Copyright (C) 2011 Andes Technology Corporation 6 * Shawn Lin, Andes Technology Corporation <nobuhiro@andestech.com> 7 * Macpaul Lin, Andes Technology Corporation <macpaul@andestech.com> 8 * 9 * See file CREDITS for list of people who contributed to this 10 * project. 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation; either version 2 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25 */ 26 27 #include <common.h> 28 #include <asm/io.h> 29 #include <faraday/fttmr010.h> 30 31 static ulong timestamp; 32 static ulong lastdec; 33 34 int timer_init(void) 35 { 36 struct fttmr010 *tmr = (struct fttmr010 *)CONFIG_FTTMR010_BASE; 37 unsigned int cr; 38 39 debug("%s()\n", __func__); 40 41 /* disable timers */ 42 writel(0, &tmr->cr); 43 44 #ifdef CONFIG_FTTMR010_EXT_CLK 45 /* use 32768Hz oscillator for RTC, WDT, TIMER */ 46 ftpmu010_32768osc_enable(); 47 #endif 48 49 /* setup timer */ 50 writel(TIMER_LOAD_VAL, &tmr->timer3_load); 51 writel(TIMER_LOAD_VAL, &tmr->timer3_counter); 52 writel(0, &tmr->timer3_match1); 53 writel(0, &tmr->timer3_match2); 54 55 /* we don't want timer to issue interrupts */ 56 writel(FTTMR010_TM3_MATCH1 | 57 FTTMR010_TM3_MATCH2 | 58 FTTMR010_TM3_OVERFLOW, 59 &tmr->interrupt_mask); 60 61 cr = readl(&tmr->cr); 62 #ifdef CONFIG_FTTMR010_EXT_CLK 63 cr |= FTTMR010_TM3_CLOCK; /* use external clock */ 64 #endif 65 cr |= FTTMR010_TM3_ENABLE; 66 writel(cr, &tmr->cr); 67 68 /* init the timestamp and lastdec value */ 69 reset_timer_masked(); 70 71 return 0; 72 } 73 74 /* 75 * timer without interrupts 76 */ 77 78 /* 79 * reset time 80 */ 81 void reset_timer_masked(void) 82 { 83 struct fttmr010 *tmr = (struct fttmr010 *)CONFIG_FTTMR010_BASE; 84 85 /* capure current decrementer value time */ 86 #ifdef CONFIG_FTTMR010_EXT_CLK 87 lastdec = readl(&tmr->timer3_counter) / (TIMER_CLOCK / CONFIG_SYS_HZ); 88 #else 89 lastdec = readl(&tmr->timer3_counter) / 90 (CONFIG_SYS_CLK_FREQ / 2 / CONFIG_SYS_HZ); 91 #endif 92 timestamp = 0; /* start "advancing" time stamp from 0 */ 93 94 debug("%s(): lastdec = %lx\n", __func__, lastdec); 95 } 96 97 void reset_timer(void) 98 { 99 debug("%s()\n", __func__); 100 reset_timer_masked(); 101 } 102 103 /* 104 * return timer ticks 105 */ 106 ulong get_timer_masked(void) 107 { 108 struct fttmr010 *tmr = (struct fttmr010 *)CONFIG_FTTMR010_BASE; 109 110 /* current tick value */ 111 #ifdef CONFIG_FTTMR010_EXT_CLK 112 ulong now = readl(&tmr->timer3_counter) / (TIMER_CLOCK / CONFIG_SYS_HZ); 113 #else 114 ulong now = readl(&tmr->timer3_counter) / 115 (CONFIG_SYS_CLK_FREQ / 2 / CONFIG_SYS_HZ); 116 #endif 117 118 debug("%s(): now = %lx, lastdec = %lx\n", __func__, now, lastdec); 119 120 if (lastdec >= now) { 121 /* 122 * normal mode (non roll) 123 * move stamp fordward with absoulte diff ticks 124 */ 125 timestamp += lastdec - now; 126 } else { 127 /* 128 * we have overflow of the count down timer 129 * 130 * nts = ts + ld + (TLV - now) 131 * ts=old stamp, ld=time that passed before passing through -1 132 * (TLV-now) amount of time after passing though -1 133 * nts = new "advancing time stamp"...it could also roll and 134 * cause problems. 135 */ 136 timestamp += lastdec + TIMER_LOAD_VAL - now; 137 } 138 139 lastdec = now; 140 141 debug("%s() returns %lx\n", __func__, timestamp); 142 143 return timestamp; 144 } 145 146 /* 147 * return difference between timer ticks and base 148 */ 149 ulong get_timer(ulong base) 150 { 151 debug("%s(%lx)\n", __func__, base); 152 return get_timer_masked() - base; 153 } 154 155 void set_timer(ulong t) 156 { 157 debug("%s(%lx)\n", __func__, t); 158 timestamp = t; 159 } 160 161 /* delay x useconds AND preserve advance timestamp value */ 162 void __udelay(unsigned long usec) 163 { 164 struct fttmr010 *tmr = (struct fttmr010 *)CONFIG_FTTMR010_BASE; 165 166 #ifdef CONFIG_FTTMR010_EXT_CLK 167 long tmo = usec * (TIMER_CLOCK / 1000) / 1000; 168 #else 169 long tmo = usec * ((CONFIG_SYS_CLK_FREQ / 2) / 1000) / 1000; 170 #endif 171 unsigned long now, last = readl(&tmr->timer3_counter); 172 173 debug("%s(%lu)\n", __func__, usec); 174 while (tmo > 0) { 175 now = readl(&tmr->timer3_counter); 176 if (now > last) /* count down timer overflow */ 177 tmo -= TIMER_LOAD_VAL + last - now; 178 else 179 tmo -= last - now; 180 last = now; 181 } 182 } 183 184 /* 185 * This function is derived from PowerPC code (read timebase as long long). 186 * On ARM it just returns the timer value. 187 */ 188 unsigned long long get_ticks(void) 189 { 190 debug("%s()\n", __func__); 191 return get_timer(0); 192 } 193 194 /* 195 * This function is derived from PowerPC code (timebase clock frequency). 196 * On ARM it returns the number of timer ticks per second. 197 */ 198 ulong get_tbclk(void) 199 { 200 debug("%s()\n", __func__); 201 #ifdef CONFIG_FTTMR010_EXT_CLK 202 return CONFIG_SYS_HZ; 203 #else 204 return CONFIG_SYS_CLK_FREQ; 205 #endif 206 } 207