1 /* $NetBSD: iomd_clock.c,v 1.17 2005/12/11 12:16:47 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1994-1997 Mark Brinicombe. 5 * Copyright (c) 1994 Brini. 6 * All rights reserved. 7 * 8 * This code is derived from software written for Brini by Mark Brinicombe 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by Mark Brinicombe. 21 * 4. The name of the company nor the name of the author may be used to 22 * endorse or promote products derived from this software without specific 23 * prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * RiscBSD kernel project 38 * 39 * clock.c 40 * 41 * Timer related machine specific code 42 * 43 * Created : 29/09/94 44 */ 45 46 /* Include header files */ 47 48 #include <sys/param.h> 49 50 __KERNEL_RCSID(0, "$NetBSD: iomd_clock.c,v 1.17 2005/12/11 12:16:47 christos Exp $"); 51 52 #include <sys/systm.h> 53 #include <sys/kernel.h> 54 #include <sys/time.h> 55 #include <sys/device.h> 56 57 #include <dev/clock_subr.h> 58 59 #include <machine/intr.h> 60 61 #include <arm/cpufunc.h> 62 63 #include <arm/iomd/iomdvar.h> 64 #include <arm/iomd/iomdreg.h> 65 66 struct clock_softc { 67 struct device sc_dev; 68 bus_space_tag_t sc_iot; 69 bus_space_handle_t sc_ioh; 70 }; 71 72 #define TIMER_FREQUENCY 2000000 /* 2MHz clock */ 73 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY / 1000000) 74 75 static void *clockirq; 76 static void *statclockirq; 77 static struct clock_softc *clock_sc; 78 static int timer0_count; 79 80 static int clockmatch __P((struct device *parent, struct cfdata *cf, void *aux)); 81 static void clockattach __P((struct device *parent, struct device *self, void *aux)); 82 #ifdef DIAGNOSTIC 83 static void checkdelay __P((void)); 84 #endif 85 86 int clockhandler __P((void *)); 87 int statclockhandler __P((void *)); 88 89 CFATTACH_DECL(clock, sizeof(struct clock_softc), 90 clockmatch, clockattach, NULL, NULL); 91 92 /* 93 * int clockmatch(struct device *parent, void *match, void *aux) 94 * 95 * Just return ok for this if it is device 0 96 */ 97 98 static int 99 clockmatch(parent, cf, aux) 100 struct device *parent; 101 struct cfdata *cf; 102 void *aux; 103 { 104 struct clk_attach_args *ca = aux; 105 106 if (strcmp(ca->ca_name, "clk") == 0) 107 return(1); 108 return(0); 109 } 110 111 112 /* 113 * void clockattach(struct device *parent, struct device *dev, void *aux) 114 * 115 * Map the IOMD and identify it. 116 * Then configure the child devices based on the IOMD ID. 117 */ 118 119 static void 120 clockattach(parent, self, aux) 121 struct device *parent; 122 struct device *self; 123 void *aux; 124 { 125 struct clock_softc *sc = (struct clock_softc *)self; 126 struct clk_attach_args *ca = aux; 127 128 sc->sc_iot = ca->ca_iot; 129 sc->sc_ioh = ca->ca_ioh; /* This is a handle for the whole IOMD */ 130 131 clock_sc = sc; 132 133 /* Cannot do anything until cpu_initclocks() has been called */ 134 135 printf("\n"); 136 } 137 138 139 /* 140 * int clockhandler(struct clockframe *frame) 141 * 142 * Function called by timer 0 interrupts. This just calls 143 * hardclock(). Eventually the irqhandler can call hardclock() directly 144 * but for now we use this function so that we can debug IRQ's 145 */ 146 147 int 148 clockhandler(cookie) 149 void *cookie; 150 { 151 struct clockframe *frame = cookie; 152 153 hardclock(frame); 154 return(0); /* Pass the interrupt on down the chain */ 155 } 156 157 158 /* 159 * int statclockhandler(struct clockframe *frame) 160 * 161 * Function called by timer 1 interrupts. This just calls 162 * statclock(). Eventually the irqhandler can call statclock() directly 163 * but for now we use this function so that we can debug IRQ's 164 */ 165 166 int 167 statclockhandler(cookie) 168 void *cookie; 169 { 170 struct clockframe *frame = cookie; 171 172 statclock(frame); 173 return(0); /* Pass the interrupt on down the chain */ 174 } 175 176 177 /* 178 * void setstatclockrate(int newhz) 179 * 180 * Set the stat clock rate. The stat clock uses timer1 181 */ 182 183 void 184 setstatclockrate(int newhz) 185 { 186 int count; 187 188 count = TIMER_FREQUENCY / newhz; 189 190 printf("Setting statclock to %dHz (%d ticks)\n", newhz, count); 191 192 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 193 IOMD_T1LOW, (count >> 0) & 0xff); 194 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 195 IOMD_T1HIGH, (count >> 8) & 0xff); 196 197 /* reload the counter */ 198 199 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 200 IOMD_T1GO, 0); 201 } 202 203 204 #ifdef DIAGNOSTIC 205 static void 206 checkdelay() 207 { 208 struct timeval start, end, diff; 209 210 microtime(&start); 211 delay(10000); 212 microtime(&end); 213 timersub(&end, &start, &diff); 214 if (diff.tv_sec > 0) 215 return; 216 if (diff.tv_usec > 10000) 217 return; 218 printf("WARNING: delay(10000) took %ld us\n", diff.tv_usec); 219 } 220 #endif 221 222 /* 223 * void cpu_initclocks(void) 224 * 225 * Initialise the clocks. 226 * This sets up the two timers in the IOMD and installs the IRQ handlers 227 * 228 * NOTE: Currently only timer 0 is setup and the IRQ handler is not installed 229 */ 230 231 void 232 cpu_initclocks() 233 { 234 /* 235 * Load timer 0 with count down value 236 * This timer generates 100Hz interrupts for the system clock 237 */ 238 239 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz); 240 241 timer0_count = TIMER_FREQUENCY / hz; 242 243 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 244 IOMD_T0LOW, (timer0_count >> 0) & 0xff); 245 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 246 IOMD_T0HIGH, (timer0_count >> 8) & 0xff); 247 248 /* reload the counter */ 249 250 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 251 IOMD_T0GO, 0); 252 253 clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk", 254 clockhandler, 0); 255 256 if (clockirq == NULL) 257 panic("%s: Cannot installer timer 0 IRQ handler", 258 clock_sc->sc_dev.dv_xname); 259 260 if (stathz) { 261 setstatclockrate(stathz); 262 statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK, 263 "tmr1 stat clk", statclockhandler, 0); 264 if (statclockirq == NULL) 265 panic("%s: Cannot installer timer 1 IRQ handler", 266 clock_sc->sc_dev.dv_xname); 267 } 268 #ifdef DIAGNOSTIC 269 checkdelay(); 270 #endif 271 } 272 273 274 /* 275 * void microtime(struct timeval *tvp) 276 * 277 * Fill in the specified timeval struct with the current time 278 * accurate to the microsecond. 279 */ 280 281 void 282 microtime(tvp) 283 struct timeval *tvp; 284 { 285 int s; 286 int tm; 287 int deltatm; 288 static struct timeval oldtv; 289 290 if (timer0_count == 0) 291 return; 292 293 s = splhigh(); 294 295 /* 296 * Latch the current value of the timer and then read it. 297 * This garentees an atmoic reading of the time. 298 */ 299 300 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 301 IOMD_T0LATCH, 0); 302 303 tm = bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh, 304 IOMD_T0LOW); 305 tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh, 306 IOMD_T0HIGH) << 8); 307 308 deltatm = timer0_count - tm; 309 if (deltatm < 0) 310 printf("opps deltatm < 0 tm=%d deltatm=%d\n", 311 tm, deltatm); 312 313 /* Fill in the timeval struct */ 314 *tvp = time; 315 316 tvp->tv_usec += (deltatm / TICKS_PER_MICROSECOND); 317 318 /* Make sure the micro seconds don't overflow. */ 319 while (tvp->tv_usec >= 1000000) { 320 tvp->tv_usec -= 1000000; 321 ++tvp->tv_sec; 322 } 323 324 /* Make sure the time has advanced. */ 325 if (tvp->tv_sec == oldtv.tv_sec && 326 tvp->tv_usec <= oldtv.tv_usec) { 327 tvp->tv_usec = oldtv.tv_usec + 1; 328 if (tvp->tv_usec >= 1000000) { 329 tvp->tv_usec -= 1000000; 330 ++tvp->tv_sec; 331 } 332 } 333 334 oldtv = *tvp; 335 (void)splx(s); 336 } 337 338 /* 339 * Estimated loop for n microseconds 340 */ 341 342 /* Need to re-write this to use the timers */ 343 344 /* One day soon I will actually do this */ 345 346 int delaycount = 100; 347 348 void 349 delay(n) 350 u_int n; 351 { 352 u_int i; 353 354 if (n == 0) return; 355 while (n-- > 0) { 356 if (cputype == CPU_ID_SA110) /* XXX - Seriously gross hack */ 357 for (i = delaycount; --i;); 358 else 359 for (i = 8; --i;); 360 } 361 } 362 363 todr_chip_handle_t todr_handle; 364 365 /* 366 * todr_attach: 367 * 368 * Set the specified time-of-day register as the system real-time clock. 369 */ 370 void 371 todr_attach(todr_chip_handle_t todr) 372 { 373 374 if (todr_handle) 375 panic("todr_attach: rtc already configured"); 376 todr_handle = todr; 377 } 378 379 /* 380 * inittodr: 381 * 382 * Initialize time from the time-of-day register. 383 */ 384 #define MINYEAR 2003 /* minimum plausible year */ 385 void 386 inittodr(time_t base) 387 { 388 time_t deltat; 389 int badbase; 390 391 if (base < (MINYEAR - 1970) * SECYR) { 392 printf("WARNING: preposterous time in file system"); 393 /* read the system clock anyway */ 394 base = (MINYEAR - 1970) * SECYR; 395 badbase = 1; 396 } else 397 badbase = 0; 398 399 if (todr_handle == NULL || 400 todr_gettime(todr_handle, &time) != 0 || 401 time.tv_sec == 0) { 402 /* 403 * Believe the time in the file system for lack of 404 * anything better, resetting the TODR. 405 */ 406 time.tv_sec = base; 407 time.tv_usec = 0; 408 if (todr_handle != NULL && !badbase) { 409 printf("WARNING: preposterous clock chip time\n"); 410 resettodr(); 411 } 412 goto bad; 413 } 414 415 if (!badbase) { 416 /* 417 * See if we gained/lost two or more days; if 418 * so, assume something is amiss. 419 */ 420 deltat = time.tv_sec - base; 421 if (deltat < 0) 422 deltat = -deltat; 423 if (deltat < 2 * SECDAY) 424 return; /* all is well */ 425 printf("WARNING: clock %s %ld days\n", 426 time.tv_sec < base ? "lost" : "gained", 427 (long)deltat / SECDAY); 428 } 429 bad: 430 printf("WARNING: CHECK AND RESET THE DATE!\n"); 431 } 432 433 /* 434 * resettodr: 435 * 436 * Reset the time-of-day register with the current time. 437 */ 438 void 439 resettodr(void) 440 { 441 442 if (time.tv_sec == 0) 443 return; 444 445 if (todr_handle != NULL && 446 todr_settime(todr_handle, &time) != 0) 447 printf("resettodr: failed to set time\n"); 448 } 449 450 /* End of iomd_clock.c */ 451