1 /* $NetBSD: s3c2800_clk.c,v 1.9 2005/12/24 20:06:52 perry Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Fujitsu Component Limited 5 * Copyright (c) 2002 Genetec Corporation 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of The Fujitsu Component Limited nor the name of 17 * Genetec corporation may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY FUJITSU COMPONENT LIMITED AND GENETEC 21 * CORPORATION ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 22 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 * DISCLAIMED. IN NO EVENT SHALL FUJITSU COMPONENT LIMITED OR GENETEC 25 * CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: s3c2800_clk.c,v 1.9 2005/12/24 20:06:52 perry Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/kernel.h> 42 #include <sys/time.h> 43 44 #include <machine/bus.h> 45 #include <machine/intr.h> 46 #include <arm/cpufunc.h> 47 48 #include <arm/s3c2xx0/s3c2800reg.h> 49 #include <arm/s3c2xx0/s3c2800var.h> 50 51 52 #ifndef STATHZ 53 #define STATHZ 64 54 #endif 55 56 #define TIMER_FREQUENCY(pclk) ((pclk)/32) /* divider=1/32 */ 57 58 static unsigned int timer0_reload_value; 59 static unsigned int timer0_prescaler; 60 static unsigned int timer0_mseccount; 61 62 #define usec_to_counter(t) \ 63 ((timer0_mseccount*(t))/1000) 64 65 #define counter_to_usec(c,pclk) \ 66 (((c)*timer0_prescaler*1000)/(TIMER_FREQUENCY(pclk)/1000)) 67 68 /* 69 * microtime: 70 * 71 * Fill in the specified timeval struct with the current time 72 * accurate to the microsecond. 73 */ 74 void 75 microtime(struct timeval *tvp) 76 { 77 struct s3c2800_softc *sc = (struct s3c2800_softc *) s3c2xx0_softc; 78 int save, int_pend0, int_pend1, count, delta; 79 static struct timeval last; 80 int pclk = s3c2xx0_softc->sc_pclk; 81 82 if( timer0_reload_value == 0 ){ 83 /* not initialized yet */ 84 tvp->tv_sec = 0; 85 tvp->tv_usec = 0; 86 return; 87 } 88 89 save = disable_interrupts(I32_bit); 90 91 again: 92 int_pend0 = S3C2800_INT_TIMER0 & 93 bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_intctl_ioh, 94 INTCTL_SRCPND); 95 count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, 96 TIMER_TMCNT); 97 98 for (;;){ 99 100 int_pend1 = S3C2800_INT_TIMER0 & 101 bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_intctl_ioh, 102 INTCTL_SRCPND); 103 if( int_pend0 == int_pend1 ) 104 break; 105 106 /* 107 * Down counter reached to zero while we were reading 108 * timer values. do it again to get consistent values. 109 */ 110 int_pend0 = int_pend1; 111 count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, 112 TIMER_TMCNT); 113 } 114 115 if( __predict_false(count > timer0_reload_value) ){ 116 /* 117 * Buggy Hardware Warning --- sometimes timer counter 118 * reads bogus value like 0xffff. I guess it happens when 119 * the timer is reloaded. 120 */ 121 #if 0 122 printf( "Bogus value from timer counter: %d\n", count ); 123 #endif 124 goto again; 125 } 126 127 /* copy system time */ 128 *tvp = time; 129 130 restore_interrupts(save); 131 132 delta = timer0_reload_value - count; 133 134 if( int_pend1 ){ 135 /* 136 * down counter underflow, but 137 * clock interrupt have not serviced yet 138 */ 139 #if 1 140 tvp->tv_usec += tick; 141 #else 142 delta = 0; 143 #endif 144 } 145 146 tvp->tv_usec += counter_to_usec(delta, pclk); 147 148 /* Make sure microseconds doesn't overflow. */ 149 tvp->tv_sec += tvp->tv_usec / 1000000; 150 tvp->tv_usec = tvp->tv_usec % 1000000; 151 152 if (last.tv_sec && 153 (tvp->tv_sec < last.tv_sec || 154 (tvp->tv_sec == last.tv_sec && 155 tvp->tv_usec < last.tv_usec) ) ){ 156 157 /* XXX: This happens very often when the kernel runs 158 under Multi-ICE */ 159 #if 0 160 printf("time reversal: %ld.%06ld(%d,%d) -> %ld.%06ld(%d,%d)\n", 161 last.tv_sec, last.tv_usec, 162 last_count, last_pend, 163 tvp->tv_sec, tvp->tv_usec, 164 count, int_pend1 ); 165 #endif 166 167 /* make sure the time has advanced. */ 168 *tvp = last; 169 tvp->tv_usec++; 170 if( tvp->tv_usec >= 1000000 ){ 171 tvp->tv_usec -= 1000000; 172 tvp->tv_sec++; 173 } 174 } 175 176 last = *tvp; 177 } 178 179 static inline int 180 read_timer(struct s3c2800_softc *sc) 181 { 182 int count; 183 184 do { 185 count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, 186 TIMER_TMCNT); 187 } while ( __predict_false(count > timer0_reload_value) ); 188 189 return count; 190 } 191 192 /* 193 * delay: 194 * 195 * Delay for at least N microseconds. 196 */ 197 void 198 delay(u_int n) 199 { 200 struct s3c2800_softc *sc = (struct s3c2800_softc *) s3c2xx0_softc; 201 int v0, v1, delta; 202 u_int ucnt; 203 204 if ( timer0_reload_value == 0 ){ 205 /* not initialized yet */ 206 while ( n-- > 0 ){ 207 int m; 208 209 for (m=0; m<100; ++m ) 210 ; 211 } 212 return; 213 } 214 215 /* read down counter */ 216 v0 = read_timer(sc); 217 218 ucnt = usec_to_counter(n); 219 220 while( ucnt > 0 ) { 221 v1 = read_timer(sc); 222 delta = v0 - v1; 223 if ( delta < 0 ) 224 delta += timer0_reload_value; 225 #ifdef DEBUG 226 if (delta < 0 || delta > timer0_reload_value) 227 panic("wrong value from timer counter"); 228 #endif 229 230 if((u_int)delta < ucnt){ 231 ucnt -= (u_int)delta; 232 v0 = v1; 233 } 234 else { 235 ucnt = 0; 236 } 237 } 238 /*NOTREACHED*/ 239 } 240 241 /* 242 * inittodr: 243 * 244 * Initialize time from the time-of-day register. 245 */ 246 void 247 inittodr(time_t base) 248 { 249 250 time.tv_sec = base; 251 time.tv_usec = 0; 252 } 253 254 /* 255 * resettodr: 256 * 257 * Reset the time-of-day register with the current time. 258 */ 259 void 260 resettodr(void) 261 { 262 } 263 264 void 265 setstatclockrate(int newhz) 266 { 267 } 268 269 270 #define hardintr (int (*)(void *))hardclock 271 #define statintr (int (*)(void *))statclock 272 273 void 274 cpu_initclocks() 275 { 276 struct s3c2800_softc *sc = (struct s3c2800_softc *)s3c2xx0_softc; 277 long tc; 278 int prescaler; 279 int pclk = s3c2xx0_softc->sc_pclk; 280 281 stathz = STATHZ; 282 profhz = stathz; 283 284 #define calc_time_constant(hz) \ 285 do { \ 286 prescaler = 1; \ 287 do { \ 288 ++prescaler; \ 289 tc = TIMER_FREQUENCY(pclk) /(hz)/ prescaler; \ 290 } while( tc > 65536 ); \ 291 } while(0) 292 293 294 295 /* Use the channels 0 and 1 for hardclock and statclock, respectively */ 296 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON, 0); 297 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON, 0); 298 299 calc_time_constant(hz); 300 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMDAT, 301 ((prescaler - 1) << 16) | (tc - 1)); 302 timer0_prescaler = prescaler; 303 timer0_reload_value = tc; 304 timer0_mseccount = TIMER_FREQUENCY(pclk)/timer0_prescaler/1000 ; 305 306 printf("clock: hz=%d stathz = %d PCLK=%d prescaler=%d tc=%ld\n", 307 hz, stathz, pclk, prescaler, tc); 308 309 calc_time_constant(stathz); 310 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMDAT, 311 ((prescaler - 1) << 16) | (tc - 1)); 312 313 314 s3c2800_intr_establish(S3C2800_INT_TIMER0, IPL_CLOCK, 315 IST_NONE, hardintr, 0); 316 s3c2800_intr_establish(S3C2800_INT_TIMER1, IPL_STATCLOCK, 317 IST_NONE, statintr, 0); 318 319 /* start timers */ 320 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON, 321 TMCON_MUX_DIV32|TMCON_INTENA|TMCON_ENABLE); 322 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON, 323 TMCON_MUX_DIV4|TMCON_INTENA|TMCON_ENABLE); 324 325 /* stop timer2 */ 326 { 327 bus_space_handle_t tmp_ioh; 328 329 bus_space_map(sc->sc_sx.sc_iot, S3C2800_TIMER2_BASE, 330 S3C2800_TIMER_SIZE, 0, &tmp_ioh); 331 332 bus_space_write_4(sc->sc_sx.sc_iot, tmp_ioh, 333 TIMER_TMCON, 0); 334 335 bus_space_unmap(sc->sc_sx.sc_iot, tmp_ioh, 336 S3C2800_TIMER_SIZE); 337 338 } 339 } 340