Annotation of sys/arch/arm/xscale/i80321_clock.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: i80321_clock.c,v 1.7 2007/05/21 14:54:35 drahn Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2006 Dale Rahn <drahn@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/types.h>
! 20: #include <sys/param.h>
! 21: #include <sys/systm.h>
! 22: #include <sys/kernel.h>
! 23: #include <sys/time.h>
! 24: #include <sys/device.h>
! 25: #include <sys/timetc.h>
! 26: #include <dev/clock_subr.h>
! 27:
! 28: #include <machine/bus.h>
! 29: #include <machine/intr.h>
! 30:
! 31: #include <arm/cpufunc.h>
! 32:
! 33: #include <arm/xscale/i80321reg.h>
! 34: #include <arm/xscale/i80321var.h>
! 35:
! 36: #define TIMER_FREQUENCY 200000000 /* 200MHz */
! 37:
! 38: static struct evcount clk_count;
! 39: static struct evcount stat_count;
! 40: static int clk_irq = 129; /* XXX */
! 41: static int stat_irq = 130; /* XXX */
! 42:
! 43: uint32_t nextstatevent;
! 44: uint32_t nexttickevent;
! 45: uint32_t ticks_per_intr;
! 46: uint32_t ticks_per_second;
! 47: uint32_t lastnow;
! 48: uint32_t statvar, statmin;
! 49: int i80321_timer_inited;
! 50: static inline u_int32_t tmr0_read(void);
! 51: static inline void tmr0_write(u_int32_t val);
! 52: static inline u_int32_t tcr0_read(void);
! 53: static inline void tcr0_write(u_int32_t val);
! 54: static inline u_int32_t trr0_read(void);
! 55: static inline void trr0_write(u_int32_t val);
! 56: static inline u_int32_t tmr1_read(void);
! 57: static inline void tmr1_write(u_int32_t val);
! 58: static inline u_int32_t tcr1_read(void);
! 59: static inline void tcr1_write(u_int32_t val);
! 60: static inline u_int32_t trr1_read(void);
! 61: static inline void trr1_write(u_int32_t val);
! 62: static inline u_int32_t tisr_read(void);
! 63: static inline void tisr_write(u_int32_t val);
! 64: int i80321_intr(void *frame);
! 65:
! 66: u_int tcr1_get_timecount(struct timecounter *tc);
! 67:
! 68: static struct timecounter tcr1_timecounter = {
! 69: tcr1_get_timecount, NULL, 0xffffffff, 0, "tcr1", 0, NULL
! 70: };
! 71:
! 72:
! 73: /*
! 74: * TMR0 is used in non-reload mode as it is used for both the clock
! 75: * timer and sched timer.
! 76: *
! 77: * The counters on 80321 are count down interrupt on 0, not match
! 78: * register based, so it is not possible to find out how much
! 79: * many interrupts passed while irqs were blocked.
! 80: * also it is not possible to atomically add to the register
! 81: * get get it to precisely fire at a non-fixed interval.
! 82: *
! 83: * To work around this both timers are used, TMR1 is used as a reference
! 84: * clock set to auto reload with 0xffffffff, however we just ignore the
! 85: * interrupt it would generate. NOTE: does this drop one tick
! 86: * ever wrap? Could the reference timer be used in non-reload mode,
! 87: * where it would just keep counting, and not stop at 0 ?
! 88: *
! 89: * Internally this keeps track of when the next timer should fire
! 90: * and based on that time and the current value of the reference
! 91: * clock a number is written into the timer count register to schedule
! 92: * the next event.
! 93: */
! 94:
! 95:
! 96: static inline u_int32_t
! 97: tmr0_read(void)
! 98: {
! 99: u_int32_t ret;
! 100: __asm volatile ("mrc p6, 0, %0, c0, c1, 0" : "=r" (ret));
! 101: return ret;
! 102: }
! 103:
! 104: static inline void
! 105: tmr0_write(u_int32_t val)
! 106: {
! 107: __asm volatile ("mcr p6, 0, %0, c0, c1, 0" :: "r" (val));
! 108: }
! 109:
! 110: static inline u_int32_t
! 111: tcr0_read(void)
! 112: {
! 113: u_int32_t ret;
! 114: __asm volatile ("mrc p6, 0, %0, c2, c1, 0" : "=r" (ret));
! 115: return ret;
! 116: }
! 117:
! 118: static inline void
! 119: tcr0_write(u_int32_t val)
! 120: {
! 121: __asm volatile ("mcr p6, 0, %0, c2, c1, 0" :: "r" (val));
! 122: }
! 123:
! 124: static inline u_int32_t
! 125: trr0_read(void)
! 126: {
! 127: u_int32_t ret;
! 128: __asm volatile ("mrc p6, 0, %0, c4, c1, 0" : "=r" (ret));
! 129: return ret;
! 130: }
! 131:
! 132: static inline void
! 133: trr0_write(u_int32_t val)
! 134: {
! 135: __asm volatile ("mcr p6, 0, %0, c4, c1, 0" :: "r" (val));
! 136: }
! 137:
! 138: static inline u_int32_t
! 139: tmr1_read(void)
! 140: {
! 141: u_int32_t ret;
! 142: __asm volatile ("mrc p6, 0, %0, c1, c1, 0" : "=r" (ret));
! 143: return ret;
! 144: }
! 145:
! 146: static inline void
! 147: tmr1_write(u_int32_t val)
! 148: {
! 149: __asm volatile ("mcr p6, 0, %0, c1, c1, 0" :: "r" (val));
! 150: }
! 151:
! 152: inline u_int32_t
! 153: tcr1_read(void)
! 154: {
! 155: u_int32_t ret;
! 156: __asm volatile ("mrc p6, 0, %0, c3, c1, 0" : "=r" (ret));
! 157: return ret;
! 158: }
! 159:
! 160: static inline void
! 161: tcr1_write(u_int32_t val)
! 162: {
! 163: __asm volatile ("mcr p6, 0, %0, c3, c1, 0" :: "r" (val));
! 164: }
! 165:
! 166: static inline u_int32_t
! 167: trr1_read(void)
! 168: {
! 169: u_int32_t ret;
! 170: __asm volatile ("mrc p6, 0, %0, c5, c1, 0" : "=r" (ret));
! 171: return ret;
! 172: }
! 173:
! 174: static inline void
! 175: trr1_write(u_int32_t val)
! 176: {
! 177: __asm volatile ("mcr p6, 0, %0, c5, c1, 0" :: "r" (val));
! 178: }
! 179:
! 180: static inline u_int32_t
! 181: tisr_read()
! 182: {
! 183: u_int32_t ret;
! 184: __asm volatile ("mrc p6, 0, %0, c6, c1, 0" : "=r" (ret));
! 185: return ret;
! 186: }
! 187:
! 188: static inline void
! 189: tisr_write(u_int32_t val)
! 190: {
! 191: __asm volatile ("mcr p6, 0, %0, c6, c1, 0" :: "r" (val));
! 192: }
! 193:
! 194: /* counter counts down not up, so reverse the results by subtracting. */
! 195: u_int
! 196: tcr1_get_timecount(struct timecounter *tc)
! 197: {
! 198: return UINT_MAX - tcr1_read();
! 199: }
! 200:
! 201: /*
! 202: * timer 1 is running a timebase counter,
! 203: * ie reload 0xffffffff, reload, interrupt ignored
! 204: * timer 0 will be programmed with the delay until the next
! 205: * event. this is not set for reload
! 206: */
! 207: int
! 208: i80321_intr(void *frame)
! 209: {
! 210: uint32_t now, r;
! 211: uint32_t nextevent;
! 212:
! 213: tisr_write(TISR_TMR0);
! 214: now = tcr1_read();
! 215:
! 216: #if 0
! 217: if (lastnow < now) {
! 218: /* rollover, remove the missing 'tick'; 1-0xffffffff, not 0- */
! 219: nextstatevent -=1;
! 220: nexttickevent -=1;
! 221: }
! 222: #endif
! 223: while ((int32_t) (now - nexttickevent) < 0) {
! 224: nexttickevent -= ticks_per_intr;
! 225: /* XXX - correct nexttickevent? */
! 226: clk_count.ec_count++;
! 227: hardclock(frame);
! 228: }
! 229: while ((int32_t) (now - nextstatevent) < 0) {
! 230: do {
! 231: r = random() & (statvar -1);
! 232: } while (r == 0); /* random == 0 not allowed */
! 233: nextstatevent -= statmin + r;
! 234: /* XXX - correct nextstatevent? */
! 235: stat_count.ec_count++;
! 236: statclock(frame);
! 237: }
! 238: if ((now - nexttickevent) < (now - nextstatevent))
! 239: nextevent = now - nexttickevent;
! 240: else
! 241: nextevent = now - nextstatevent;
! 242: if (nextevent < 10 /* XXX */)
! 243: nextevent = 10;
! 244: if (nextevent > ticks_per_intr) {
! 245: /*
! 246: * If interrupts are blocked too long, like during
! 247: * the root prompt or ddb, the timer can roll over,
! 248: * this will allow the system to continue to run
! 249: * even if time is lost.
! 250: */
! 251: nextevent = ticks_per_intr;
! 252: nexttickevent = now;
! 253: nextstatevent = now;
! 254: }
! 255:
! 256:
! 257: tcr0_write(nextevent);
! 258: tmr0_write(TMRx_ENABLE|TMRx_PRIV|TMRx_CSEL_CORE);
! 259:
! 260: lastnow = now;
! 261:
! 262: return 1;
! 263: }
! 264:
! 265: void
! 266: cpu_initclocks()
! 267: {
! 268: uint32_t now;
! 269:
! 270: /* would it make sense to have this be 100/1000 to round nicely? */
! 271: /* 100/1000 or 128/1024 ? */
! 272: stathz = 100;
! 273: profhz = 1000;
! 274:
! 275: ticks_per_second = 200 * 1000000; /* 200 MHz */
! 276:
! 277: setstatclockrate(stathz);
! 278:
! 279: ticks_per_intr = ticks_per_second / hz;
! 280:
! 281: evcount_attach(&clk_count, "clock", (void *)&clk_irq, &evcount_intr);
! 282: evcount_attach(&stat_count, "stat", (void *)&stat_irq, &evcount_intr);
! 283:
! 284: (void) i80321_intr_establish(ICU_INT_TMR0, IPL_CLOCK, i80321_intr,
! 285: NULL, NULL);
! 286:
! 287: now = 0xffffffff;
! 288: nextstatevent = now - ticks_per_intr;
! 289: nexttickevent = now - ticks_per_intr;
! 290:
! 291: tcr1_write(now);
! 292: trr1_write(now);
! 293: tmr1_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_PRIV|TMRx_CSEL_CORE);
! 294:
! 295: tcr0_write(now); /* known big value */
! 296: tmr0_write(TMRx_ENABLE|TMRx_PRIV|TMRx_CSEL_CORE);
! 297: tcr0_write(ticks_per_intr);
! 298:
! 299: tcr1_timecounter.tc_frequency = ticks_per_second;
! 300: tc_init(&tcr1_timecounter);
! 301:
! 302:
! 303: i80321_timer_inited = 1;
! 304: }
! 305:
! 306: void
! 307: delay(u_int usecs)
! 308: {
! 309: u_int32_t clock, oclock, delta, delaycnt;
! 310: volatile int j;
! 311: int csec, usec;
! 312:
! 313: csec = usecs / 10000;
! 314: usec = usecs % 10000;
! 315:
! 316: delaycnt = (TIMER_FREQUENCY / 100) * csec +
! 317: (TIMER_FREQUENCY / 100) * usec / 10000;
! 318:
! 319: if (delaycnt <= 1) /* delay too short spin for a bit */
! 320: for (j = 100; j > 0; j--)
! 321: ;
! 322:
! 323: if (i80321_timer_inited == 0) {
! 324: /* clock isn't initialized yet */
! 325: for (; usecs > 0; usecs--)
! 326: for (j = 100; j > 0; j--)
! 327: ;
! 328: return;
! 329: }
! 330:
! 331: oclock = tcr1_read();
! 332:
! 333: while(1) {
! 334: clock = tcr1_read();
! 335: /* timer counts down, not up so old - new */
! 336: delta = oclock - clock;
! 337: if (delta > delaycnt)
! 338: break;
! 339: }
! 340: }
! 341:
! 342: void
! 343: setstatclockrate(int newhz)
! 344: {
! 345: int minint, statint;
! 346: int s;
! 347:
! 348: s = splclock();
! 349:
! 350: statint = ticks_per_second / newhz;
! 351: /* calculate largest 2^n which is smaller that just over half statint */
! 352: statvar = 0x40000000; /* really big power of two */
! 353: minint = statint / 2 + 100;
! 354: while (statvar > minint)
! 355: statvar >>= 1;
! 356:
! 357: statmin = statint - (statvar >> 1);
! 358:
! 359: splx(s);
! 360:
! 361: /*
! 362: * XXX this allows the next stat timer to occur then it switches
! 363: * to the new frequency. Rather than switching instantly.
! 364: */
! 365: }
! 366:
! 367: void
! 368: i80321_calibrate_delay(void)
! 369: {
! 370:
! 371: tmr1_write(0); /* stop timer */
! 372: tisr_write(TISR_TMR1); /* clear interrupt */
! 373: trr1_write(0xffffffff); /* reload value */
! 374: tcr1_write(0xffffffff); /* current value */
! 375:
! 376: tmr1_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_PRIV|TMRx_CSEL_CORE);
! 377: }
! 378:
! 379: todr_chip_handle_t todr_handle;
! 380:
! 381: /*
! 382: * inittodr:
! 383: *
! 384: * Initialize time from the time-of-day register.
! 385: */
! 386: #define MINYEAR 2003 /* minimum plausible year */
! 387: void
! 388: inittodr(time_t base)
! 389: {
! 390: time_t deltat;
! 391: struct timeval rtctime;
! 392: struct timespec ts;
! 393: int badbase;
! 394:
! 395: if (base < (MINYEAR - 1970) * SECYR) {
! 396: printf("WARNING: preposterous time in file system\n");
! 397: /* read the system clock anyway */
! 398: base = (MINYEAR - 1970) * SECYR;
! 399: badbase = 1;
! 400: } else
! 401: badbase = 0;
! 402:
! 403: if (todr_handle == NULL ||
! 404: todr_gettime(todr_handle, &rtctime) != 0 ||
! 405: rtctime.tv_sec == 0) {
! 406: /*
! 407: * Believe the time in the file system for lack of
! 408: * anything better, resetting the TODR.
! 409: */
! 410: rtctime.tv_sec = base;
! 411: rtctime.tv_usec = 0;
! 412: if (todr_handle != NULL && !badbase) {
! 413: printf("WARNING: preposterous clock chip time\n");
! 414: resettodr();
! 415: }
! 416: goto bad;
! 417: }
! 418:
! 419: ts.tv_sec = rtctime.tv_sec;
! 420: ts.tv_nsec = rtctime.tv_usec * 1000;
! 421: tc_setclock(&ts);
! 422:
! 423: if (!badbase) {
! 424: /*
! 425: * See if we gained/lost two or more days; if
! 426: * so, assume something is amiss.
! 427: */
! 428: deltat = rtctime.tv_sec - base;
! 429: if (deltat < 0)
! 430: deltat = -deltat;
! 431: if (deltat < 2 * SECDAY)
! 432: return; /* all is well */
! 433:
! 434: printf("WARNING: clock %s %ld days\n",
! 435: rtctime.tv_sec < base ? "lost" : "gained",
! 436: (long)deltat / SECDAY);
! 437: }
! 438: bad:
! 439: printf("WARNING: CHECK AND RESET THE DATE!\n");
! 440: }
! 441:
! 442: /*
! 443: * resettodr:
! 444: *
! 445: * Reset the time-of-day register with the current time.
! 446: */
! 447: void
! 448: resettodr(void)
! 449: {
! 450: struct timeval rtctime;
! 451:
! 452: if (time_second == 0)
! 453: return;
! 454:
! 455: microtime(&rtctime);
! 456:
! 457: if (todr_handle != NULL &&
! 458: todr_settime(todr_handle, &rtctime) != 0)
! 459: printf("resettodr: failed to set time\n");
! 460: }
! 461:
CVSweb