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) / (CONFIG_SYS_CLK_FREQ / 2); 90 #endif 91 timestamp = 0; /* start "advancing" time stamp from 0 */ 92 93 debug("%s(): lastdec = %lx\n", __func__, lastdec); 94 } 95 96 void reset_timer(void) 97 { 98 debug("%s()\n", __func__); 99 reset_timer_masked(); 100 } 101 102 /* 103 * return timer ticks 104 */ 105 ulong get_timer_masked(void) 106 { 107 struct fttmr010 *tmr = (struct fttmr010 *)CONFIG_FTTMR010_BASE; 108 109 /* current tick value */ 110 #ifdef CONFIG_FTTMR010_EXT_CLK 111 ulong now = readl(&tmr->timer3_counter) / (TIMER_CLOCK / CONFIG_SYS_HZ); 112 #else 113 ulong now = readl(&tmr->timer3_counter) / \ 114 (CONFIG_SYS_CLK_FREQ / 2 / 1024); 115 #endif 116 117 debug("%s(): now = %lx, lastdec = %lx\n", __func__, now, lastdec); 118 119 if (lastdec >= now) { 120 /* 121 * normal mode (non roll) 122 * move stamp fordward with absoulte diff ticks 123 */ 124 timestamp += lastdec - now; 125 } else { 126 /* 127 * we have overflow of the count down timer 128 * 129 * nts = ts + ld + (TLV - now) 130 * ts=old stamp, ld=time that passed before passing through -1 131 * (TLV-now) amount of time after passing though -1 132 * nts = new "advancing time stamp"...it could also roll and 133 * cause problems. 134 */ 135 timestamp += lastdec + TIMER_LOAD_VAL - now; 136 } 137 138 lastdec = now; 139 140 debug("%s() returns %lx\n", __func__, timestamp); 141 142 return timestamp; 143 } 144 145 /* 146 * return difference between timer ticks and base 147 */ 148 ulong get_timer(ulong base) 149 { 150 debug("%s(%lx)\n", __func__, base); 151 return get_timer_masked() - base; 152 } 153 154 void set_timer(ulong t) 155 { 156 debug("%s(%lx)\n", __func__, t); 157 timestamp = t; 158 } 159 160 /* delay x useconds AND preserve advance timestamp value */ 161 void __udelay(unsigned long usec) 162 { 163 struct fttmr010 *tmr = (struct fttmr010 *)CONFIG_FTTMR010_BASE; 164 165 #ifdef CONFIG_FTTMR010_EXT_CLK 166 long tmo = usec * (TIMER_CLOCK / 1000) / 1000; 167 #else 168 long tmo = usec * ((CONFIG_SYS_CLK_FREQ / 2) / 1000) / 1000; 169 #endif 170 unsigned long now, last = readl(&tmr->timer3_counter); 171 172 debug("%s(%lu)\n", __func__, usec); 173 while (tmo > 0) { 174 now = readl(&tmr->timer3_counter); 175 if (now > last) /* count down timer overflow */ 176 tmo -= TIMER_LOAD_VAL + last - now; 177 else 178 tmo -= last - now; 179 last = now; 180 } 181 } 182 183 /* 184 * This function is derived from PowerPC code (read timebase as long long). 185 * On ARM it just returns the timer value. 186 */ 187 unsigned long long get_ticks(void) 188 { 189 debug("%s()\n", __func__); 190 return get_timer(0); 191 } 192 193 /* 194 * This function is derived from PowerPC code (timebase clock frequency). 195 * On ARM it returns the number of timer ticks per second. 196 */ 197 ulong get_tbclk(void) 198 { 199 debug("%s()\n", __func__); 200 #ifdef CONFIG_FTTMR010_EXT_CLK 201 return CONFIG_SYS_HZ; 202 #else 203 return CONFIG_SYS_CLK_FREQ; 204 #endif 205 } 206