[BACK]Return to i80321_clock.c CVS log [TXT][DIR] Up to [local] / sys / arch / arm / xscale

Annotation of sys/arch/arm/xscale/i80321_clock.c, Revision 1.1.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