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

Annotation of sys/arch/macppc/macppc/clock.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: clock.c,v 1.19 2007/04/13 18:48:38 kettenis Exp $     */
        !             2: /*     $NetBSD: clock.c,v 1.1 1996/09/30 16:34:40 ws Exp $     */
        !             3:
        !             4: /*
        !             5:  * Copyright (C) 1995, 1996 Wolfgang Solfrank.
        !             6:  * Copyright (C) 1995, 1996 TooLs GmbH.
        !             7:  * All rights reserved.
        !             8:  *
        !             9:  * Redistribution and use in source and binary forms, with or without
        !            10:  * modification, are permitted provided that the following conditions
        !            11:  * are met:
        !            12:  * 1. Redistributions of source code must retain the above copyright
        !            13:  *    notice, this list of conditions and the following disclaimer.
        !            14:  * 2. Redistributions in binary form must reproduce the above copyright
        !            15:  *    notice, this list of conditions and the following disclaimer in the
        !            16:  *    documentation and/or other materials provided with the distribution.
        !            17:  * 3. All advertising materials mentioning features or use of this software
        !            18:  *    must display the following acknowledgement:
        !            19:  *     This product includes software developed by TooLs GmbH.
        !            20:  * 4. The name of TooLs GmbH may not be used to endorse or promote products
        !            21:  *    derived from this software without specific prior written permission.
        !            22:  *
        !            23:  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
        !            24:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
        !            25:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
        !            26:  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
        !            27:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
        !            28:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
        !            29:  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
        !            30:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
        !            31:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
        !            32:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        !            33:  */
        !            34:
        !            35: #include <sys/param.h>
        !            36: #include <sys/kernel.h>
        !            37: #include <sys/systm.h>
        !            38: #include <sys/evcount.h>
        !            39: #include <sys/timetc.h>
        !            40:
        !            41: #include <machine/autoconf.h>
        !            42: #include <machine/pio.h>
        !            43: #include <machine/intr.h>
        !            44: #include <machine/vmparam.h>
        !            45: #include <machine/powerpc.h>
        !            46: #include <dev/ofw/openfirm.h>
        !            47:
        !            48: void decr_intr(struct clockframe *frame);
        !            49: u_int tb_get_timecount(struct timecounter *);
        !            50:
        !            51: /*
        !            52:  * Initially we assume a processor with a bus frequency of 12.5 MHz.
        !            53:  */
        !            54: static u_int32_t ticks_per_sec = 3125000;
        !            55: static u_int32_t ns_per_tick = 320;
        !            56: static int32_t ticks_per_intr;
        !            57: static volatile u_int64_t lasttb;
        !            58:
        !            59: static struct timecounter tb_timecounter = {
        !            60:        tb_get_timecount, NULL, 0x7fffffff, 0, "tb", 0, NULL
        !            61: };
        !            62:
        !            63: time_read_t  *time_read;
        !            64: time_write_t *time_write;
        !            65:
        !            66: /* event tracking variables, when the next events of each time should occur */
        !            67: u_int64_t nexttimerevent, prevtb, nextstatevent;
        !            68:
        !            69: /* vars for stats */
        !            70: int statint;
        !            71: u_int32_t statvar;
        !            72: u_int32_t statmin;
        !            73:
        !            74: static struct evcount clk_count;
        !            75: static struct evcount stat_count;
        !            76: static int clk_irq = PPC_CLK_IRQ;
        !            77: static int stat_irq = PPC_STAT_IRQ;
        !            78:
        !            79: /*
        !            80:  * Set up the system's time, given a `reasonable' time value.
        !            81:  */
        !            82: void
        !            83: inittodr(time_t base)
        !            84: {
        !            85:        int badbase = 0, waszero = base == 0;
        !            86:        char *bad = NULL;
        !            87:        struct timeval tv;
        !            88:        struct timespec ts;
        !            89:
        !            90:         if (base < 5 * SECYR) {
        !            91:                 /*
        !            92:                  * If base is 0, assume filesystem time is just unknown
        !            93:                  * instead of preposterous. Don't bark.
        !            94:                  */
        !            95:                 if (base != 0)
        !            96:                         printf("WARNING: preposterous time in file system\n");
        !            97:                 /* not going to use it anyway, if the chip is readable */
        !            98:                 base = 21*SECYR + 186*SECDAY + SECDAY/2;
        !            99:                 badbase = 1;
        !           100:         }
        !           101:
        !           102:        if (time_read != NULL) {
        !           103:                u_int32_t cursec;
        !           104:                (*time_read)(&cursec);
        !           105:                tv.tv_sec = cursec;
        !           106:                tv.tv_usec = 0;
        !           107:        } else {
        !           108:                /* force failure */
        !           109:                tv.tv_sec = tv.tv_usec = 0;
        !           110:        }
        !           111:
        !           112:        if (tv.tv_sec == 0) {
        !           113:                /*
        !           114:                 * Believe the time in the file system for lack of
        !           115:                 * anything better, resetting the clock.
        !           116:                 */
        !           117:                bad = "WARNING: unable to get date/time";
        !           118:                tv.tv_sec = base;
        !           119:                tv.tv_usec = 0;
        !           120:                if (!badbase)
        !           121:                        resettodr();
        !           122:        } else {
        !           123:                int deltat;
        !           124:
        !           125:                tv.tv_sec += tz.tz_minuteswest * 60;
        !           126:                if (tz.tz_dsttime)
        !           127:                        tv.tv_sec -= 3600;
        !           128:
        !           129:                deltat = tv.tv_sec - base;
        !           130:
        !           131:                if (deltat < 0)
        !           132:                        deltat = -deltat;
        !           133:                if (!(waszero || deltat < 2 * SECDAY)) {
        !           134:                        printf("WARNING: clock %s %d days",
        !           135:                            tv.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
        !           136:                        bad = "";
        !           137:
        !           138:                        if (tv.tv_sec < base && deltat > 1000 * SECDAY) {
        !           139:                                printf(", using FS time");
        !           140:                                tv.tv_sec = base;
        !           141:                        }
        !           142:                }
        !           143:        }
        !           144:
        !           145:        ts.tv_sec = tv.tv_sec;
        !           146:        ts.tv_nsec = tv.tv_usec * 1000;
        !           147:        tc_setclock(&ts);
        !           148:
        !           149:        if (bad) {
        !           150:                printf("%s", bad);
        !           151:                printf(" -- CHECK AND RESET THE DATE!\n");
        !           152:        }
        !           153: }
        !           154:
        !           155: /*
        !           156:  * Similar to the above
        !           157:  */
        !           158: void
        !           159: resettodr(void)
        !           160: {
        !           161:        struct timeval tv;
        !           162:
        !           163:        if (time_second == 0)
        !           164:                return;
        !           165:
        !           166:        microtime(&tv);
        !           167:
        !           168:        if (time_write != NULL) {
        !           169:                tv.tv_sec -= tz.tz_minuteswest * 60;
        !           170:                if (tz.tz_dsttime) {
        !           171:                        tv.tv_sec += 3600;
        !           172:                }
        !           173:                (*time_write)(tv.tv_sec);
        !           174:        }
        !           175: }
        !           176:
        !           177: volatile int statspending;
        !           178:
        !           179: void
        !           180: decr_intr(struct clockframe *frame)
        !           181: {
        !           182:        u_int64_t tb;
        !           183:        u_int64_t nextevent;
        !           184:        int nstats;
        !           185:        int s;
        !           186:
        !           187:        /*
        !           188:         * Check whether we are initialized.
        !           189:         */
        !           190:        if (!ticks_per_intr)
        !           191:                return;
        !           192:
        !           193:
        !           194:        /*
        !           195:         * Based on the actual time delay since the last decrementer reload,
        !           196:         * we arrange for earlier interrupt next time.
        !           197:         */
        !           198:
        !           199:        tb = ppc_mftb();
        !           200:        while (nexttimerevent <= tb)
        !           201:                nexttimerevent += ticks_per_intr;
        !           202:
        !           203:        prevtb = nexttimerevent - ticks_per_intr;
        !           204:
        !           205:        for (nstats = 0; nextstatevent <= tb; nstats++) {
        !           206:                int r;
        !           207:                do {
        !           208:                        r = random() & (statvar -1);
        !           209:                } while (r == 0); /* random == 0 not allowed */
        !           210:                nextstatevent += statmin + r;
        !           211:        }
        !           212:
        !           213:        /* only count timer ticks for CLK_IRQ */
        !           214:        stat_count.ec_count += nstats;
        !           215:
        !           216:        if (nexttimerevent < nextstatevent)
        !           217:                nextevent = nexttimerevent;
        !           218:        else
        !           219:                nextevent = nextstatevent;
        !           220:
        !           221:        /*
        !           222:         * Need to work about the near constant skew this introduces???
        !           223:         * reloading tb here could cause a missed tick.
        !           224:         */
        !           225:        ppc_mtdec(nextevent - tb);
        !           226:
        !           227:        if (curcpu()->ci_cpl & SPL_CLOCK) {
        !           228:                statspending += nstats;
        !           229:        } else {
        !           230:                nstats += statspending;
        !           231:                statspending = 0;
        !           232:
        !           233:                s = splclock();
        !           234:
        !           235:                /*
        !           236:                 * Reenable interrupts
        !           237:                 */
        !           238:                ppc_intr_enable(1);
        !           239:
        !           240:                /*
        !           241:                 * Do standard timer interrupt stuff.
        !           242:                 * Do softclock stuff only on the last iteration.
        !           243:                 */
        !           244:                frame->pri = s | SINT_CLOCK;
        !           245:                while (lasttb < prevtb - ticks_per_intr) {
        !           246:                        /* sync lasttb with hardclock */
        !           247:                        lasttb += ticks_per_intr;
        !           248:                        clk_count.ec_count++;
        !           249:                        hardclock(frame);
        !           250:                }
        !           251:
        !           252:                frame->pri = s;
        !           253:                while (lasttb < prevtb) {
        !           254:                        /* sync lasttb with hardclock */
        !           255:                        lasttb += ticks_per_intr;
        !           256:                        clk_count.ec_count++;
        !           257:                        hardclock(frame);
        !           258:                }
        !           259:
        !           260:                while (nstats-- > 0)
        !           261:                        statclock(frame);
        !           262:
        !           263:                splx(s);
        !           264:                (void) ppc_intr_disable();
        !           265:
        !           266:                /* if a tick has occurred while dealing with these,
        !           267:                 * dont service it now, delay until the next tick.
        !           268:                 */
        !           269:        }
        !           270: }
        !           271:
        !           272: void
        !           273: cpu_initclocks()
        !           274: {
        !           275:        int intrstate;
        !           276:        int r;
        !           277:        int minint;
        !           278:        u_int64_t nextevent;
        !           279:
        !           280:        intrstate = ppc_intr_disable();
        !           281:
        !           282:        stathz = 100;
        !           283:        profhz = 1000; /* must be a multiple of stathz */
        !           284:
        !           285:        /* init secondary clock to stathz */
        !           286:        statint = ticks_per_sec / stathz;
        !           287:        statvar = 0x40000000; /* really big power of two */
        !           288:        /* find largest 2^n which is nearly smaller than statint/2  */
        !           289:        minint = statint / 2 + 100;
        !           290:        while (statvar > minint)
        !           291:                statvar >>= 1;
        !           292:
        !           293:        statmin = statint - (statvar >> 1);
        !           294:
        !           295:
        !           296:        lasttb = ppc_mftb();
        !           297:        nexttimerevent = lasttb + ticks_per_intr;
        !           298:        do {
        !           299:                r = random() & (statvar -1);
        !           300:        } while (r == 0); /* random == 0 not allowed */
        !           301:        nextstatevent = lasttb + statmin + r;
        !           302:
        !           303:        if (nexttimerevent < nextstatevent)
        !           304:                nextevent = nexttimerevent;
        !           305:        else
        !           306:                nextevent = nextstatevent;
        !           307:
        !           308:        evcount_attach(&clk_count, "clock", (void *)&clk_irq, &evcount_intr);
        !           309:        evcount_attach(&stat_count, "stat", (void *)&stat_irq, &evcount_intr);
        !           310:
        !           311:        tb_timecounter.tc_frequency = ticks_per_sec;
        !           312:        tc_init(&tb_timecounter);
        !           313:
        !           314:        ppc_mtdec(nextevent-lasttb);
        !           315:        ppc_intr_enable(intrstate);
        !           316: }
        !           317:
        !           318: void
        !           319: calc_delayconst(void)
        !           320: {
        !           321:        int qhandle, phandle;
        !           322:        char name[32];
        !           323:        int s;
        !           324:
        !           325:        /*
        !           326:         * Get this info during autoconf?                               XXX
        !           327:         */
        !           328:        for (qhandle = OF_peer(0); qhandle; qhandle = phandle) {
        !           329:                if (OF_getprop(qhandle, "device_type", name, sizeof name) >= 0
        !           330:                    && !strcmp(name, "cpu")
        !           331:                    && OF_getprop(qhandle, "timebase-frequency",
        !           332:                    &ticks_per_sec, sizeof ticks_per_sec) >= 0) {
        !           333:                        /*
        !           334:                         * Should check for correct CPU here?           XXX
        !           335:                         */
        !           336:                        s = ppc_intr_disable();
        !           337:                        ns_per_tick = 1000000000 / ticks_per_sec;
        !           338:                        ticks_per_intr = ticks_per_sec / hz;
        !           339:                        ppc_intr_enable(s);
        !           340:                        break;
        !           341:                }
        !           342:                if ((phandle = OF_child(qhandle)))
        !           343:                        continue;
        !           344:                while (qhandle) {
        !           345:                        if ((phandle = OF_peer(qhandle)))
        !           346:                                break;
        !           347:                        qhandle = OF_parent(qhandle);
        !           348:                }
        !           349:        }
        !           350:
        !           351:        if (!phandle)
        !           352:                panic("no cpu node");
        !           353: }
        !           354:
        !           355: /*
        !           356:  * Wait for about n microseconds (us) (at least!).
        !           357:  */
        !           358: void
        !           359: delay(unsigned n)
        !           360: {
        !           361:        u_int64_t tb;
        !           362:        u_int32_t tbh, tbl, scratch;
        !           363:
        !           364:        tb = ppc_mftb();
        !           365:        tb += (n * 1000 + ns_per_tick - 1) / ns_per_tick;
        !           366:        tbh = tb >> 32;
        !           367:        tbl = (u_int32_t)tb;
        !           368:        asm ("1: mftbu %0; cmplw %0,%1; blt 1b; bgt 2f;"
        !           369:             " mftb %0; cmplw %0,%2; blt 1b; 2:"
        !           370:             :: "r"(scratch), "r"(tbh), "r"(tbl));
        !           371: }
        !           372:
        !           373: /*
        !           374:  * Nothing to do.
        !           375:  */
        !           376: void
        !           377: setstatclockrate(int newhz)
        !           378: {
        !           379:        int minint;
        !           380:        int intrstate;
        !           381:
        !           382:        intrstate = ppc_intr_disable();
        !           383:
        !           384:        statint = ticks_per_sec / newhz;
        !           385:        statvar = 0x40000000; /* really big power of two */
        !           386:        /* find largest 2^n which is nearly smaller than statint/2 */
        !           387:        minint = statint / 2 + 100;
        !           388:        while (statvar > minint)
        !           389:                statvar >>= 1;
        !           390:
        !           391:        statmin = statint - (statvar >> 1);
        !           392:        ppc_intr_enable(intrstate);
        !           393:
        !           394:        /*
        !           395:         * XXX this allows the next stat timer to occur then it switches
        !           396:         * to the new frequency. Rather than switching instantly.
        !           397:         */
        !           398: }
        !           399:
        !           400: u_int
        !           401: tb_get_timecount(struct timecounter *tc)
        !           402: {
        !           403:        return ppc_mftbl();
        !           404: }

CVSweb