1 /* $NetBSD: s3c2800_clk.c,v 1.1 2002/11/20 17:52:49 bsh 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 /* 37 * Clock & Power Management 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 #include <arm/s3c2xx0/s3c2xx0_intr.h> 51 52 53 #ifndef PCLK 54 #define PCLK (50*1000*1000) 55 #endif 56 57 #ifndef STATHZ 58 #define STATHZ 64 59 #endif 60 61 #define TIMER_FREQUENCY (PCLK/4) /* divider=1/4 */ 62 63 #define TIMER_RELOAD_VAL 1000 64 #define COUNTS_PER_USEC 100 65 66 static unsigned int timer0_reload_value; 67 static unsigned int timer0_prescaler; 68 69 #define counter_to_usec(c) (((c)*timer0_prescaler*1000)/(TIMER_FREQUENCY/1000)) 70 71 /* 72 * microtime: 73 * 74 * Fill in the specified timeval struct with the current time 75 * accurate to the microsecond. 76 */ 77 void 78 microtime(struct timeval *tvp) 79 { 80 struct s3c2800_softc *sc = (struct s3c2800_softc *) s3c2xx0_softc; 81 int save, int_pend0, int_pend1, count, delta; 82 static struct timeval last; 83 84 if( timer0_reload_value == 0 ){ 85 /* not initialized yet */ 86 tvp->tv_sec = 0; 87 tvp->tv_usec = 0; 88 return; 89 } 90 91 save = disable_interrupts(I32_bit); 92 93 again: 94 int_pend0 = S3C2800_INT_TIMER0 & 95 bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_intctl_ioh, 96 INTCTL_SRCPND); 97 count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, 98 TIMER_TMCNT); 99 100 for (;;){ 101 102 int_pend1 = S3C2800_INT_TIMER0 & 103 bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_intctl_ioh, 104 INTCTL_SRCPND); 105 if( int_pend0 == int_pend1 ) 106 break; 107 108 /* 109 * Down counter reached to zero while we were reading 110 * timer values. do it again to get consistent values. 111 */ 112 int_pend0 = int_pend1; 113 count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, 114 TIMER_TMCNT); 115 } 116 117 if( __predict_false(count > timer0_reload_value) ){ 118 /* 119 * Buggy Hardware Warning --- sometimes timer counter 120 * reads bogus value like 0xffff. I guess it happens when 121 * the timer is reloaded. 122 */ 123 #if 0 124 printf( "Bogus value from timer counter: %d\n", count ); 125 #endif 126 goto again; 127 } 128 129 /* copy system time */ 130 *tvp = time; 131 132 restore_interrupts(save); 133 134 delta = timer0_reload_value - count; 135 136 if( int_pend1 ){ 137 /* 138 * down counter underflow, but 139 * clock interrupt have not serviced yet 140 */ 141 #if 1 142 tvp->tv_usec += tick; 143 #else 144 delta = 0; 145 #endif 146 } 147 148 tvp->tv_usec += counter_to_usec(delta); 149 150 /* Make sure microseconds doesn't overflow. */ 151 tvp->tv_sec += tvp->tv_usec / 1000000; 152 tvp->tv_usec = tvp->tv_usec % 1000000; 153 154 if (last.tv_sec && 155 (tvp->tv_sec < last.tv_sec || 156 (tvp->tv_sec == last.tv_sec && 157 tvp->tv_usec < last.tv_usec) ) ){ 158 159 /* XXX: This happens very often when the kernel runs 160 under Multi-ICE */ 161 #if 0 162 printf("time reversal: %ld.%06ld(%d,%d) -> %ld.%06ld(%d,%d)\n", 163 last.tv_sec, last.tv_usec, 164 last_count, last_pend, 165 tvp->tv_sec, tvp->tv_usec, 166 count, int_pend1 ); 167 #endif 168 169 /* make sure the time has advanced. */ 170 *tvp = last; 171 tvp->tv_usec++; 172 if( tvp->tv_usec >= 1000000 ){ 173 tvp->tv_usec -= 1000000; 174 tvp->tv_sec++; 175 } 176 } 177 178 last = *tvp; 179 } 180 181 static __inline int 182 read_timer(struct s3c2800_softc *sc) 183 { 184 int count; 185 186 do { 187 count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, 188 TIMER_TMCNT); 189 } while ( __predict_false(count > timer0_reload_value) ); 190 191 return count; 192 } 193 194 /* 195 * delay: 196 * 197 * Delay for at least N microseconds. 198 */ 199 void 200 delay(u_int n) 201 { 202 struct s3c2800_softc *sc = (struct s3c2800_softc *) s3c2xx0_softc; 203 int v0, v1, delta; 204 205 if ( timer0_reload_value == 0 ){ 206 /* not initialized yet */ 207 while ( n-- > 0 ){ 208 int m; 209 210 for (m=0; m<100; ++m ) 211 ; 212 } 213 return; 214 } 215 216 /* read down counter */ 217 v0 = read_timer(sc); 218 219 for(;;){ 220 v1 = read_timer(sc); 221 delta = v0 - v1; 222 if ( delta < 0 ){ 223 delta += timer0_reload_value; 224 } 225 #ifdef DEBUG 226 if (delta < 0 || delta > timer0_reload_value) 227 panic("wrong value from timer counter"); 228 #endif 229 230 delta = counter_to_usec(delta); 231 232 if (delta >= n ) 233 return; 234 n -= delta; 235 v0 = v1; 236 } 237 /*NOTREACHED*/ 238 } 239 /* 240 * inittodr: 241 * 242 * Initialize time from the time-of-day register. 243 */ 244 void 245 inittodr(time_t base) 246 { 247 248 time.tv_sec = base; 249 time.tv_usec = 0; 250 } 251 /* 252 * resettodr: 253 * 254 * Reset the time-of-day register with the current time. 255 */ 256 void 257 resettodr(void) 258 { 259 } 260 261 void 262 setstatclockrate(hz) 263 int hz; 264 { 265 } 266 267 268 #define hardintr (int (*)(void *))hardclock 269 #define statintr (int (*)(void *))statclock 270 271 void 272 cpu_initclocks() 273 { 274 struct s3c2800_softc *sc = (struct s3c2800_softc *) s3c2xx0_softc; 275 long tc; 276 int prescaler; 277 278 stathz = STATHZ; 279 profhz = stathz; 280 281 #define calc_time_constant(hz) \ 282 do { \ 283 prescaler = 1; \ 284 do { \ 285 ++prescaler; \ 286 tc = TIMER_FREQUENCY /(hz)/ prescaler; \ 287 } while( tc > 65536 ); \ 288 } while(0) 289 290 291 292 /* Use the channels 0 and 1 for hardclock and statclock, respectively */ 293 calc_time_constant(hz); 294 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMDAT, 295 ((prescaler - 1) << 16) | (tc - 1)); 296 timer0_prescaler = prescaler; 297 timer0_reload_value = tc; 298 299 printf("clock: hz=%d stathz = %d PCLK=%d prescaler=%d tc=%ld\n", 300 hz, stathz, PCLK, prescaler, tc); 301 302 calc_time_constant(stathz); 303 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMDAT, 304 ((prescaler - 1) << 16) | (tc - 1)); 305 306 307 s3c2800_intr_establish(S3C2800_INT_TIMER0, IPL_CLOCK, hardintr, 0); 308 s3c2800_intr_establish(S3C2800_INT_TIMER1, IPL_STATCLOCK, statintr, 0); 309 310 /* start timers */ 311 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON, 312 TMCON_MUX_DIV4 | TMCON_INTENA | TMCON_ENABLE); 313 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON, 314 TMCON_MUX_DIV4 | TMCON_INTENA | TMCON_ENABLE); 315 316 /* stop timer2 */ 317 { 318 bus_space_handle_t tmp_ioh; 319 320 bus_space_map(sc->sc_sx.sc_iot, S3C2800_TIMER2_BASE, 321 S3C2800_TIMER_SIZE, 0, &tmp_ioh); 322 323 bus_space_write_4(sc->sc_sx.sc_iot, tmp_ioh, 324 TIMER_TMCON, 0); 325 326 bus_space_unmap(sc->sc_sx.sc_iot, tmp_ioh, 327 S3C2800_TIMER_SIZE); 328 329 } 330 } 331