[BACK]Return to clock.c CVS log [TXT][DIR] Up to [local] / sys / arch / sh / sh

Annotation of sys/arch/sh/sh/clock.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: clock.c,v 1.4 2007/06/21 04:43:33 miod Exp $  */
                      2: /*     $NetBSD: clock.c,v 1.32 2006/09/05 11:09:36 uwe Exp $   */
                      3:
                      4: /*-
                      5:  * Copyright (c) 2002 The NetBSD Foundation, Inc.
                      6:  * All rights reserved.
                      7:  *
                      8:  * This code is derived from software contributed to The NetBSD Foundation
                      9:  * by UCHIYAMA Yasushi.
                     10:  *
                     11:  * Redistribution and use in source and binary forms, with or without
                     12:  * modification, are permitted provided that the following conditions
                     13:  * are met:
                     14:  * 1. Redistributions of source code must retain the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer.
                     16:  * 2. Redistributions in binary form must reproduce the above copyright
                     17:  *    notice, this list of conditions and the following disclaimer in the
                     18:  *    documentation and/or other materials provided with the distribution.
                     19:  * 3. All advertising materials mentioning features or use of this software
                     20:  *    must display the following acknowledgement:
                     21:  *        This product includes software developed by the NetBSD
                     22:  *        Foundation, Inc. and its contributors.
                     23:  * 4. Neither the name of The NetBSD Foundation nor the names of its
                     24:  *    contributors may be used to endorse or promote products derived
                     25:  *    from this software without specific prior written permission.
                     26:  *
                     27:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     28:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     29:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     30:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     31:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     32:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     33:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     34:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     35:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     36:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     37:  * POSSIBILITY OF SUCH DAMAGE.
                     38:  */
                     39:
                     40: #include <sys/param.h>
                     41: #include <sys/systm.h>
                     42: #include <sys/kernel.h>
                     43: #include <sys/device.h>
                     44:
                     45: #include <dev/clock_subr.h>
                     46:
                     47: #include <sh/clock.h>
                     48: #include <sh/trap.h>
                     49: #include <sh/rtcreg.h>
                     50: #include <sh/tmureg.h>
                     51:
                     52: #include <machine/intr.h>
                     53:
                     54: #define        NWDOG 0
                     55:
                     56: #ifndef HZ
                     57: #define        HZ              64
                     58: #endif
                     59: #define        MINYEAR         2002    /* "today" */
                     60: #define        SH_RTC_CLOCK    16384   /* Hz */
                     61:
                     62: /*
                     63:  * OpenBSD/sh clock module
                     64:  *  + default 64Hz
                     65:  *  + use TMU channel 0 as clock interrupt source.
                     66:  *  + use TMU channel 1 and 2 as emulated software interrupt soruce.
                     67:  *  + If RTC module is active, TMU channel 0 input source is RTC output.
                     68:  *    (1.6384kHz)
                     69:  */
                     70: struct {
                     71:        /* Hard clock */
                     72:        uint32_t hz_cnt;        /* clock interrupt interval count */
                     73:        uint32_t cpucycle_1us;  /* calibrated loop variable (1 us) */
                     74:        uint32_t tmuclk;        /* source clock of TMU0 (Hz) */
                     75:
                     76:        /* RTC ops holder. default SH RTC module */
                     77:        struct rtc_ops rtc;
                     78:        int rtc_initialized;
                     79:
                     80:        uint32_t pclock;        /* PCLOCK */
                     81:        uint32_t cpuclock;      /* CPU clock */
                     82:        int flags;
                     83: } sh_clock = {
                     84: #ifdef PCLOCK
                     85:        .pclock = PCLOCK,
                     86: #endif
                     87:        .rtc = {
                     88:                /* SH RTC module to default RTC */
                     89:                .init   = sh_rtc_init,
                     90:                .get    = sh_rtc_get,
                     91:                .set    = sh_rtc_set
                     92:        }
                     93: };
                     94:
                     95: uint32_t maxwdog;
                     96:
                     97: /* TMU */
                     98: /* interrupt handler is timing critical. prepared for each. */
                     99: int sh3_clock_intr(void *);
                    100: int sh4_clock_intr(void *);
                    101:
                    102: /*
                    103:  * Estimate CPU and Peripheral clock.
                    104:  */
                    105: #define        TMU_START(x)                                                    \
                    106: do {                                                                   \
                    107:        _reg_bclr_1(SH_(TSTR), TSTR_STR##x);                            \
                    108:        _reg_write_4(SH_(TCNT ## x), 0xffffffff);                       \
                    109:        _reg_bset_1(SH_(TSTR), TSTR_STR##x);                            \
                    110: } while (/*CONSTCOND*/0)
                    111: #define        TMU_ELAPSED(x)                                                  \
                    112:        (0xffffffff - _reg_read_4(SH_(TCNT ## x)))
                    113:
                    114: void
                    115: sh_clock_init(int flags, struct rtc_ops *rtc)
                    116: {
                    117:        uint32_t s, t0, t1 __attribute__((__unused__));
                    118:
                    119:        sh_clock.flags = flags;
                    120:        if (rtc != NULL)
                    121:                sh_clock.rtc = *rtc;    /* structure copy */
                    122:
                    123:        /* Initialize TMU */
                    124:        _reg_write_2(SH_(TCR0), 0);
                    125:        _reg_write_2(SH_(TCR1), 0);
                    126:        _reg_write_2(SH_(TCR2), 0);
                    127:
                    128:        /* Reset RTC alarm and interrupt */
                    129:        _reg_write_1(SH_(RCR1), 0);
                    130:
                    131:        /* Stop all counter */
                    132:        _reg_write_1(SH_(TSTR), 0);
                    133:
                    134:        /*
                    135:         * Estimate CPU clock.
                    136:         */
                    137:        if (sh_clock.flags & SH_CLOCK_NORTC) {
                    138:                /* Set TMU channel 0 source to PCLOCK / 16 */
                    139:                _reg_write_2(SH_(TCR0), TCR_TPSC_P16);
                    140:                sh_clock.tmuclk = sh_clock.pclock / 16;
                    141:        } else {
                    142:                /* Set TMU channel 0 source to RTC counter clock (16.384kHz) */
                    143:                _reg_write_2(SH_(TCR0),
                    144:                    CPU_IS_SH3 ? SH3_TCR_TPSC_RTC : SH4_TCR_TPSC_RTC);
                    145:                sh_clock.tmuclk = SH_RTC_CLOCK;
                    146:        }
                    147:
                    148:        s = _cpu_exception_suspend();
                    149:        _cpu_spin(1);   /* load function on cache. */
                    150:        TMU_START(0);
                    151:        _cpu_spin(10000000);
                    152:        t0 = TMU_ELAPSED(0);
                    153:        _cpu_exception_resume(s);
                    154:
                    155:        sh_clock.cpuclock = ((10000000 * 10) / t0) * sh_clock.tmuclk;
                    156:        sh_clock.cpucycle_1us = (sh_clock.tmuclk * 10) / t0;
                    157:
                    158:        if (CPU_IS_SH4)
                    159:                sh_clock.cpuclock >>= 1;        /* two-issue */
                    160:
                    161:        /*
                    162:         * Estimate PCLOCK
                    163:         */
                    164:        if (sh_clock.pclock == 0) {
                    165:                /* set TMU channel 1 source to PCLOCK / 4 */
                    166:                _reg_write_2(SH_(TCR1), TCR_TPSC_P4);
                    167:                s = _cpu_exception_suspend();
                    168:                _cpu_spin(1);   /* load function on cache. */
                    169:                TMU_START(0);
                    170:                TMU_START(1);
                    171:                _cpu_spin(sh_clock.cpucycle_1us * 1000000);     /* 1 sec. */
                    172:                t0 = TMU_ELAPSED(0);
                    173:                t1 = TMU_ELAPSED(1);
                    174:                _cpu_exception_resume(s);
                    175:
                    176:                sh_clock.pclock = ((t1 * 4)/ t0) * SH_RTC_CLOCK;
                    177:        }
                    178:
                    179:        /* Stop all counter */
                    180:        _reg_write_1(SH_(TSTR), 0);
                    181:
                    182: #undef TMU_START
                    183: #undef TMU_ELAPSED
                    184: }
                    185:
                    186: int
                    187: sh_clock_get_cpuclock()
                    188: {
                    189:        return (sh_clock.cpuclock);
                    190: }
                    191:
                    192: int
                    193: sh_clock_get_pclock()
                    194: {
                    195:        return (sh_clock.pclock);
                    196: }
                    197:
                    198: void
                    199: setstatclockrate(int newhz)
                    200: {
                    201:        /* XXX not yet */
                    202: }
                    203:
                    204: /*
                    205:  * Return the best possible estimate of the time in the timeval to
                    206:  * which tv points.
                    207:  */
                    208: void
                    209: microtime(struct timeval *tv)
                    210: {
                    211:        static struct timeval lasttime;
                    212:        u_int32_t tcnt0;
                    213:        int s;
                    214:
                    215:        s = splclock();
                    216:        *tv = time;
                    217:        tcnt0 = _reg_read_4(SH_(TCNT0));
                    218:        splx(s);
                    219:
                    220:        tv->tv_usec += ((sh_clock.hz_cnt - tcnt0) * 1000000) / sh_clock.tmuclk;
                    221:        while (tv->tv_usec >= 1000000) {
                    222:                tv->tv_usec -= 1000000;
                    223:                tv->tv_sec++;
                    224:        }
                    225:
                    226:        if (tv->tv_sec == lasttime.tv_sec &&
                    227:            tv->tv_usec <= lasttime.tv_usec &&
                    228:            (tv->tv_usec = lasttime.tv_usec + 1) >= 1000000) {
                    229:                tv->tv_usec -= 1000000;
                    230:                tv->tv_sec++;
                    231:        }
                    232:        lasttime = *tv;
                    233: }
                    234:
                    235: /*
                    236:  *  Wait at least `n' usec.
                    237:  */
                    238: void
                    239: delay(int n)
                    240: {
                    241:        _cpu_spin(sh_clock.cpucycle_1us * n);
                    242: }
                    243:
                    244: /*
                    245:  * Start the clock interrupt.
                    246:  */
                    247: void
                    248: cpu_initclocks()
                    249: {
                    250:        if (sh_clock.pclock == 0)
                    251:                panic("No PCLOCK information.");
                    252:
                    253:        /* Set global variables. */
                    254:        hz = HZ;
                    255:        tick = 1000000 / hz;
                    256:
                    257:        /*
                    258:         * Use TMU channel 0 as hard clock
                    259:         */
                    260:        _reg_bclr_1(SH_(TSTR), TSTR_STR0);
                    261:
                    262:        if (sh_clock.flags & SH_CLOCK_NORTC) {
                    263:                /* use PCLOCK/16 as TMU0 source */
                    264:                _reg_write_2(SH_(TCR0), TCR_UNIE | TCR_TPSC_P16);
                    265:        } else {
                    266:                /* use RTC clock as TMU0 source */
                    267:                _reg_write_2(SH_(TCR0), TCR_UNIE |
                    268:                    (CPU_IS_SH3 ? SH3_TCR_TPSC_RTC : SH4_TCR_TPSC_RTC));
                    269:        }
                    270:        sh_clock.hz_cnt = sh_clock.tmuclk / hz - 1;
                    271:
                    272:        _reg_write_4(SH_(TCOR0), sh_clock.hz_cnt);
                    273:        _reg_write_4(SH_(TCNT0), sh_clock.hz_cnt);
                    274:
                    275:        intc_intr_establish(SH_INTEVT_TMU0_TUNI0, IST_LEVEL, IPL_CLOCK,
                    276:            CPU_IS_SH3 ? sh3_clock_intr : sh4_clock_intr, NULL, "clock");
                    277:        /* start hardclock */
                    278:        _reg_bset_1(SH_(TSTR), TSTR_STR0);
                    279:
                    280:        /*
                    281:         * TMU channel 1, 2 are one shot timer.
                    282:         */
                    283:        _reg_write_2(SH_(TCR1), TCR_UNIE | TCR_TPSC_P4);
                    284:        _reg_write_4(SH_(TCOR1), 0xffffffff);
                    285:        _reg_write_2(SH_(TCR2), TCR_UNIE | TCR_TPSC_P4);
                    286:        _reg_write_4(SH_(TCOR2), 0xffffffff);
                    287:
                    288:        /* Make sure to start RTC */
                    289:        if (sh_clock.rtc.init != NULL)
                    290:                sh_clock.rtc.init(sh_clock.rtc._cookie);
                    291: }
                    292:
                    293: void
                    294: inittodr(time_t base)
                    295: {
                    296:        struct clock_ymdhms dt;
                    297:        time_t rtc;
                    298:        int s;
                    299:
                    300:        if (!sh_clock.rtc_initialized)
                    301:                sh_clock.rtc_initialized = 1;
                    302:
                    303:        sh_clock.rtc.get(sh_clock.rtc._cookie, base, &dt);
                    304:        rtc = clock_ymdhms_to_secs(&dt);
                    305:
                    306: #ifdef DEBUG
                    307:        printf("inittodr: %d/%d/%d/%d/%d/%d(%d)\n", dt.dt_year,
                    308:            dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec,
                    309:            dt.dt_wday);
                    310: #endif
                    311:
                    312:        if (!(sh_clock.flags & SH_CLOCK_NOINITTODR) &&
                    313:            (rtc < base ||
                    314:                dt.dt_year < MINYEAR || dt.dt_year > 2037 ||
                    315:                dt.dt_mon < 1 || dt.dt_mon > 12 ||
                    316:                dt.dt_wday > 6 ||
                    317:                dt.dt_day < 1 || dt.dt_day > 31 ||
                    318:                dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 59)) {
                    319:                /*
                    320:                 * Believe the time in the file system for lack of
                    321:                 * anything better, resetting the RTC.
                    322:                 */
                    323:                s = splclock();
                    324:                time.tv_sec = base;
                    325:                time.tv_usec = 0;
                    326:                splx(s);
                    327:                printf("WARNING: preposterous clock chip time\n");
                    328:                resettodr();
                    329:                printf(" -- CHECK AND RESET THE DATE!\n");
                    330:                return;
                    331:        }
                    332:
                    333:        s = splclock();
                    334:        time.tv_sec = rtc;
                    335:        time.tv_usec = 0;
                    336:        splx(s);
                    337:
                    338:        return;
                    339: }
                    340:
                    341: void
                    342: resettodr()
                    343: {
                    344:        struct clock_ymdhms dt;
                    345:        int s;
                    346:
                    347:        if (!sh_clock.rtc_initialized)
                    348:                return;
                    349:
                    350:        s = splclock();
                    351:        clock_secs_to_ymdhms(time.tv_sec, &dt);
                    352:        splx(s);
                    353:
                    354:        sh_clock.rtc.set(sh_clock.rtc._cookie, &dt);
                    355: #ifdef DEBUG
                    356:         printf("%s: %d/%d/%d/%d/%d/%d(%d)\n", __FUNCTION__,
                    357:            dt.dt_year, dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec,
                    358:            dt.dt_wday);
                    359: #endif
                    360: }
                    361:
                    362: #ifdef SH3
                    363: int
                    364: sh3_clock_intr(void *arg) /* trap frame */
                    365: {
                    366: #if (NWDOG > 0)
                    367:        uint32_t i;
                    368:
                    369:        i = (uint32_t)SHREG_WTCNT_R;
                    370:        if (i > maxwdog)
                    371:                maxwdog = i;
                    372:        wdog_wr_cnt(0);                 /* reset to zero */
                    373: #endif
                    374:        /* clear underflow status */
                    375:        _reg_bclr_2(SH3_TCR0, TCR_UNF);
                    376:
                    377:        hardclock(arg);
                    378:
                    379:        return (1);
                    380: }
                    381: #endif /* SH3 */
                    382:
                    383: #ifdef SH4
                    384: int
                    385: sh4_clock_intr(void *arg) /* trap frame */
                    386: {
                    387: #if (NWDOG > 0)
                    388:        uint32_t i;
                    389:
                    390:        i = (uint32_t)SHREG_WTCNT_R;
                    391:        if (i > maxwdog)
                    392:                maxwdog = i;
                    393:        wdog_wr_cnt(0);                 /* reset to zero */
                    394: #endif
                    395:        /* clear underflow status */
                    396:        _reg_bclr_2(SH4_TCR0, TCR_UNF);
                    397:
                    398:        hardclock(arg);
                    399:
                    400:        return (1);
                    401: }
                    402: #endif /* SH4 */
                    403:
                    404: /*
                    405:  * SH3 RTC module ops.
                    406:  */
                    407:
                    408: void
                    409: sh_rtc_init(void *cookie)
                    410: {
                    411:        /* Make sure to start RTC */
                    412:        _reg_write_1(SH_(RCR2), SH_RCR2_ENABLE | SH_RCR2_START);
                    413: }
                    414:
                    415: void
                    416: sh_rtc_get(void *cookie, time_t base, struct clock_ymdhms *dt)
                    417: {
                    418:        int retry = 8;
                    419:
                    420:        /* disable carry interrupt */
                    421:        _reg_bclr_1(SH_(RCR1), SH_RCR1_CIE);
                    422:
                    423:        do {
                    424:                uint8_t r = _reg_read_1(SH_(RCR1));
                    425:                r &= ~SH_RCR1_CF;
                    426:                r |= SH_RCR1_AF; /* don't clear alarm flag */
                    427:                _reg_write_1(SH_(RCR1), r);
                    428:
                    429:                if (CPU_IS_SH3)
                    430:                        dt->dt_year = FROMBCD(_reg_read_1(SH3_RYRCNT));
                    431:                else
                    432:                        dt->dt_year = FROMBCD(_reg_read_2(SH4_RYRCNT) & 0x00ff);
                    433:
                    434:                /* read counter */
                    435: #define        RTCGET(x, y)    dt->dt_ ## x = FROMBCD(_reg_read_1(SH_(R ## y ## CNT)))
                    436:                RTCGET(mon, MON);
                    437:                RTCGET(wday, WK);
                    438:                RTCGET(day, DAY);
                    439:                RTCGET(hour, HR);
                    440:                RTCGET(min, MIN);
                    441:                RTCGET(sec, SEC);
                    442: #undef RTCGET
                    443:        } while ((_reg_read_1(SH_(RCR1)) & SH_RCR1_CF) && --retry > 0);
                    444:
                    445:        if (retry == 0) {
                    446:                printf("rtc_gettime: couldn't read RTC register.\n");
                    447:                memset(dt, 0, sizeof(*dt));
                    448:                return;
                    449:        }
                    450:
                    451:        dt->dt_year = (dt->dt_year % 100) + 1900;
                    452:        if (dt->dt_year < 1970)
                    453:                dt->dt_year += 100;
                    454: }
                    455:
                    456: void
                    457: sh_rtc_set(void *cookie, struct clock_ymdhms *dt)
                    458: {
                    459:        uint8_t r;
                    460:
                    461:        /* stop clock */
                    462:        r = _reg_read_1(SH_(RCR2));
                    463:        r |= SH_RCR2_RESET;
                    464:        r &= ~SH_RCR2_START;
                    465:        _reg_write_1(SH_(RCR2), r);
                    466:
                    467:        /* set time */
                    468:        if (CPU_IS_SH3)
                    469:                _reg_write_1(SH3_RYRCNT, TOBCD(dt->dt_year % 100));
                    470:        else
                    471:                _reg_write_2(SH4_RYRCNT, TOBCD(dt->dt_year % 100));
                    472: #define        RTCSET(x, y)    _reg_write_1(SH_(R ## x ## CNT), TOBCD(dt->dt_ ## y))
                    473:        RTCSET(MON, mon);
                    474:        RTCSET(WK, wday);
                    475:        RTCSET(DAY, day);
                    476:        RTCSET(HR, hour);
                    477:        RTCSET(MIN, min);
                    478:        RTCSET(SEC, sec);
                    479: #undef RTCSET
                    480:        /* start clock */
                    481:        _reg_write_1(SH_(RCR2), r | SH_RCR2_START);
                    482: }

CVSweb