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

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

1.1     ! nbrk        1: /*     $OpenBSD: clock.c,v 1.13 2005/12/03 21:36:17 brad Exp $ */
        !             2: /*     $NetBSD: clock.c,v 1.20 1997/04/27 20:43:38 thorpej Exp $       */
        !             3:
        !             4: /*
        !             5:  * Copyright (c) 1988 University of Utah.
        !             6:  * Copyright (c) 1982, 1990, 1993
        !             7:  *     The Regents of the University of California.  All rights reserved.
        !             8:  *
        !             9:  * This code is derived from software contributed to Berkeley by
        !            10:  * the Systems Programming Group of the University of Utah Computer
        !            11:  * Science Department.
        !            12:  *
        !            13:  * Redistribution and use in source and binary forms, with or without
        !            14:  * modification, are permitted provided that the following conditions
        !            15:  * are met:
        !            16:  * 1. Redistributions of source code must retain the above copyright
        !            17:  *    notice, this list of conditions and the following disclaimer.
        !            18:  * 2. Redistributions in binary form must reproduce the above copyright
        !            19:  *    notice, this list of conditions and the following disclaimer in the
        !            20:  *    documentation and/or other materials provided with the distribution.
        !            21:  * 3. Neither the name of the University nor the names of its contributors
        !            22:  *    may be used to endorse or promote products derived from this software
        !            23:  *    without specific prior written permission.
        !            24:  *
        !            25:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            26:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            27:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            28:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            29:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            30:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            31:  * OR 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:  * from: Utah $Hdr: clock.c 1.18 91/01/21$
        !            38:  *
        !            39:  *     @(#)clock.c     8.2 (Berkeley) 1/12/94
        !            40:  */
        !            41:
        !            42: /*
        !            43:  * HPs use the MC6840 PTM with the following arrangement:
        !            44:  *     Timers 1 and 3 are externally driver from a 25MHz source.
        !            45:  *     Output from timer 3 is tied to the input of timer 2.
        !            46:  * The latter makes it possible to use timers 3 and 2 together to get
        !            47:  * a 32-bit countdown timer.
        !            48:  */
        !            49:
        !            50: #include <sys/param.h>
        !            51: #include <sys/systm.h>
        !            52: #include <sys/kernel.h>
        !            53: #include <sys/tty.h>
        !            54: #include <sys/evcount.h>
        !            55:
        !            56: #include <machine/psl.h>
        !            57: #include <machine/cpu.h>
        !            58: #include <machine/hp300spu.h>
        !            59:
        !            60: #include <dev/hil/hilreg.h>    /* for BBC */
        !            61: #include <hp300/hp300/clockreg.h>
        !            62:
        !            63: #ifdef GPROF
        !            64: #include <sys/gmon.h>
        !            65: #endif
        !            66:
        !            67: int    clkstd[1];
        !            68:
        !            69: static int clkint;             /* clock interval, as loaded */
        !            70: /*
        !            71:  * Statistics clock interval and variance, in usec.  Variance must be a
        !            72:  * power of two.  Since this gives us an even number, not an odd number,
        !            73:  * we discard one case and compensate.  That is, a variance of 1024 would
        !            74:  * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
        !            75:  * This is symmetric about the point 512, or statvar/2, and thus averages
        !            76:  * to that value (assuming uniform random numbers).
        !            77:  */
        !            78: static int statvar = 1024 / 4; /* {stat,prof}clock variance */
        !            79: static int statmin;            /* statclock interval - variance/2 */
        !            80: static int profmin;            /* profclock interval - variance/2 */
        !            81: static int timer3min;          /* current, from above choices */
        !            82: static int statprev;           /* previous value in stat timer */
        !            83:
        !            84: static int month_days[12] = {
        !            85:        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
        !            86: };
        !            87: u_char bbc_registers[13];
        !            88: volatile u_int8_t *bbcaddr = NULL;
        !            89:
        !            90: void   clockintr(struct clockframe *);
        !            91: void   statintr(struct clockframe *);
        !            92:
        !            93: void   hp300_calibrate_delay(void);
        !            94: struct bbc_tm *gmt_to_bbc(long);
        !            95: int    bbc_to_gmt(u_long *);
        !            96: void   read_bbc(void);
        !            97: u_char read_bbc_reg(int);
        !            98: void   send_clock_cmd(volatile u_int8_t *, u_int8_t, u_int8_t *,
        !            99:     u_int8_t, u_int8_t *);
        !           100: u_char write_bbc_reg(int, u_int);
        !           101:
        !           102: static int clock_ipl = IPL_CLOCK;
        !           103: static int stat_ipl = IPL_STATCLOCK;
        !           104: struct evcount clockcnt;
        !           105: struct evcount statcnt;
        !           106:
        !           107: /*
        !           108:  * Machine-dependent clock routines.
        !           109:  *
        !           110:  * A note on the real-time clock:
        !           111:  * We actually load the clock with interval-1 instead of interval.
        !           112:  * This is because the counter decrements to zero after N+1 enabled clock
        !           113:  * periods where N is the value loaded into the counter.
        !           114:  *
        !           115:  * The frequencies of the HP300 clocks must be a multiple of four
        !           116:  * microseconds (since the clock counts in 4 us units).
        !           117:  */
        !           118: #define        COUNTS_PER_SEC  (1000000 / CLK_RESOLUTION)
        !           119:
        !           120: /*
        !           121:  * Calibrate the delay constant, based on Chuck Cranor's
        !           122:  * mvme68k delay calibration algorithm.
        !           123:  */
        !           124: void
        !           125: hp300_calibrate_delay()
        !           126: {
        !           127:        extern int delay_divisor;
        !           128:        volatile struct clkreg *clk;
        !           129:        volatile u_char csr;
        !           130:        int intvl;
        !           131:
        !           132:        clkstd[0] = IIOV(0x5F8000);             /* XXX yuck */
        !           133:        clk = (volatile struct clkreg *)clkstd[0];
        !           134:
        !           135:        /*
        !           136:         * Calibrate delay() using the 4 usec counter.
        !           137:         * We adjust delay_divisor until we get the result we want.
        !           138:         * We assume we've been called at splhigh().
        !           139:         */
        !           140:        for (delay_divisor = 140; delay_divisor > 1; delay_divisor--) {
        !           141:                /* Reset clock chip */
        !           142:                clk->clk_cr2 = CLK_CR1;
        !           143:                clk->clk_cr1 = CLK_RESET;
        !           144:
        !           145:                /*
        !           146:                 * Prime the timer.  We're looking for
        !           147:                 * 10,000 usec (10ms).  See interval comment
        !           148:                 * above.
        !           149:                 */
        !           150:                intvl = (10000 / CLK_RESOLUTION) - 1;
        !           151:                asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
        !           152:
        !           153:                /* Enable the timer */
        !           154:                clk->clk_cr2 = CLK_CR1;
        !           155:                clk->clk_cr1 = CLK_IENAB;
        !           156:
        !           157:                delay(10000);
        !           158:
        !           159:                /* Timer1 interrupt flag high? */
        !           160:                csr = clk->clk_sr;
        !           161:                if (csr & CLK_INT1) {
        !           162:                        /*
        !           163:                         * Got it.  Clear interrupt and get outta here.
        !           164:                         */
        !           165:                        asm volatile(" movpw %0@(5),%1" : :
        !           166:                            "a" (clk), "d" (intvl));
        !           167:                        break;
        !           168:                }
        !           169:
        !           170:                /*
        !           171:                 * Nope.  Poll for completion of the interval,
        !           172:                 * clear interrupt, and try again.
        !           173:                 */
        !           174:                do {
        !           175:                        csr = clk->clk_sr;
        !           176:                } while ((csr & CLK_INT1) == 0);
        !           177:
        !           178:                asm volatile(" movpw %0@(5),%1" : : "a" (clk), "d" (intvl));
        !           179:        }
        !           180:
        !           181:        /*
        !           182:         * Make sure the clock interrupt is disabled.  Otherwise,
        !           183:         * we can end up calling hardclock() before proc0 is set up,
        !           184:         * causing a bad pointer deref.
        !           185:         */
        !           186:        clk->clk_cr2 = CLK_CR1;
        !           187:        clk->clk_cr1 = CLK_RESET;
        !           188:
        !           189:        /*
        !           190:         * Sanity check the delay_divisor value.  If we totally lost,
        !           191:         * assume a 50MHz CPU;
        !           192:         */
        !           193:        if (delay_divisor == 0)
        !           194:                delay_divisor = 2048 / 50;
        !           195:
        !           196:        /* Calculate CPU speed. */
        !           197:        cpuspeed = 2048 / delay_divisor;
        !           198: }
        !           199:
        !           200: /*
        !           201:  * Set up the real-time and statistics clocks.  Leave stathz 0 only if
        !           202:  * no alternative timer is available.
        !           203:  */
        !           204: void
        !           205: cpu_initclocks()
        !           206: {
        !           207:        volatile struct clkreg *clk;
        !           208:        int intvl, statint, profint, minint;
        !           209:
        !           210:        clkstd[0] = IIOV(0x5F8000);             /* XXX grot */
        !           211:        clk = (volatile struct clkreg *)clkstd[0];
        !           212:
        !           213:        if (COUNTS_PER_SEC % hz) {
        !           214:                printf("cannot get %d Hz clock; using 100 Hz\n", hz);
        !           215:                hz = 100;
        !           216:        }
        !           217:        /*
        !           218:         * Clock has several counters, so we can always use separate
        !           219:         * statclock.
        !           220:         */
        !           221:        if (stathz == 0)                /* XXX should be set in param.c */
        !           222:                stathz = hz;
        !           223:        else if (COUNTS_PER_SEC % stathz) {
        !           224:                printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
        !           225:                stathz = 100;
        !           226:        }
        !           227:        if (profhz == 0)                /* XXX should be set in param.c */
        !           228:                profhz = stathz * 5;
        !           229:        else if (profhz < stathz || COUNTS_PER_SEC % profhz) {
        !           230:                printf("cannot get %d Hz profclock; using %d Hz\n",
        !           231:                    profhz, stathz);
        !           232:                profhz = stathz;
        !           233:        }
        !           234:
        !           235:        intvl = COUNTS_PER_SEC / hz;
        !           236:        statint = COUNTS_PER_SEC / stathz;
        !           237:        profint = COUNTS_PER_SEC / profhz;
        !           238:        minint = statint / 2 + 100;
        !           239:        while (statvar > minint)
        !           240:                statvar >>= 1;
        !           241:
        !           242:        tick = intvl * CLK_RESOLUTION;
        !           243:
        !           244:        /* adjust interval counts, per note above */
        !           245:        intvl--;
        !           246:        statint--;
        !           247:        profint--;
        !           248:
        !           249:        /* calculate base reload values */
        !           250:        clkint = intvl;
        !           251:        statmin = statint - (statvar >> 1);
        !           252:        profmin = profint - (statvar >> 1);
        !           253:        timer3min = statmin;
        !           254:        statprev = statint;
        !           255:
        !           256:        evcount_attach(&statcnt, "stat", &stat_ipl, &evcount_intr);
        !           257:        evcount_attach(&clockcnt, "clock", &clock_ipl, &evcount_intr);
        !           258:
        !           259:        /* finally, load hardware */
        !           260:        clk->clk_cr2 = CLK_CR1;
        !           261:        clk->clk_cr1 = CLK_RESET;
        !           262:        asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
        !           263:        asm volatile(" movpw %0,%1@(9)" : : "d" (0), "a" (clk));
        !           264:        asm volatile(" movpw %0,%1@(13)" : : "d" (statint), "a" (clk));
        !           265:        clk->clk_cr2 = CLK_CR1;
        !           266:        clk->clk_cr1 = CLK_IENAB;
        !           267:        clk->clk_cr2 = CLK_CR3;
        !           268:        clk->clk_cr3 = CLK_IENAB;
        !           269: }
        !           270:
        !           271: /*
        !           272:  * We assume newhz is either stathz or profhz, and that neither will
        !           273:  * change after being set up above.  Could recalculate intervals here
        !           274:  * but that would be a drag.
        !           275:  */
        !           276: void
        !           277: setstatclockrate(newhz)
        !           278:        int newhz;
        !           279: {
        !           280:
        !           281:        if (newhz == stathz)
        !           282:                timer3min = statmin;
        !           283:        else
        !           284:                timer3min = profmin;
        !           285: }
        !           286:
        !           287: /*
        !           288:  * Timer clock interrupt.
        !           289:  */
        !           290: void
        !           291: clockintr(fp)
        !           292:        struct clockframe *fp;
        !           293: {
        !           294:        clockcnt.ec_count++;
        !           295:        hardclock(fp);
        !           296: }
        !           297:
        !           298: /*
        !           299:  * Statistics/profiling clock interrupt.  Compute a new interval.
        !           300:  * Interrupt has already been cleared.
        !           301:  */
        !           302: void
        !           303: statintr(fp)
        !           304:        struct clockframe *fp;
        !           305: {
        !           306:        volatile struct clkreg *clk;
        !           307:        int newint, r, var;
        !           308:
        !           309:        clk = (volatile struct clkreg *)clkstd[0];
        !           310:        var = statvar;
        !           311:        do {
        !           312:                r = random() & (var - 1);
        !           313:        } while (r == 0);
        !           314:        newint = timer3min + r;
        !           315:
        !           316:        /*
        !           317:         * The timer was automatically reloaded with the previous latch
        !           318:         * value at the time of the interrupt.  Compensate now for the
        !           319:         * amount of time that has run off since then (minimum of 2-12
        !           320:         * timer ticks depending on CPU type) plus one tick roundoff.
        !           321:         * This should keep us closer to the mean.
        !           322:         */
        !           323:        asm volatile(" clrl %0; movpw %1@(13),%0" : "=d" (r) : "a" (clk));
        !           324:        newint -= (statprev - r + 1);
        !           325:
        !           326:        asm volatile(" movpw %0,%1@(13)" : : "d" (newint), "a" (clk));
        !           327:        statprev = newint;
        !           328:        statcnt.ec_count++;
        !           329:        statclock(fp);
        !           330: }
        !           331:
        !           332: /*
        !           333:  * Return the best possible estimate of the current time.
        !           334:  */
        !           335: void
        !           336: microtime(tvp)
        !           337:        struct timeval *tvp;
        !           338: {
        !           339:        volatile struct clkreg *clk;
        !           340:        int s, u, t, u2, s2;
        !           341:
        !           342:        /*
        !           343:         * Read registers from slowest-changing to fastest-changing,
        !           344:         * then re-read out to slowest.  If the values read before the
        !           345:         * innermost match those read after, the innermost value is
        !           346:         * consistent with the outer values.  If not, it may not be and
        !           347:         * we must retry.  Typically this loop runs only once; occasionally
        !           348:         * it runs twice, and only rarely does it run longer.
        !           349:         *
        !           350:         * (Using this loop avoids the need to block interrupts.)
        !           351:         */
        !           352:        clk = (volatile struct clkreg *)clkstd[0];
        !           353:        do {
        !           354:                s = time.tv_sec;
        !           355:                u = time.tv_usec;
        !           356:                asm volatile (" clrl %0; movpw %1@(5),%0"
        !           357:                              : "=d" (t) : "a" (clk));
        !           358:                u2 = time.tv_usec;
        !           359:                s2 = time.tv_sec;
        !           360:        } while (u != u2 || s != s2);
        !           361:
        !           362:        u += (clkint - t) * CLK_RESOLUTION;
        !           363:        if (u >= 1000000) {             /* normalize */
        !           364:                s++;
        !           365:                u -= 1000000;
        !           366:        }
        !           367:        tvp->tv_sec = s;
        !           368:        tvp->tv_usec = u;
        !           369: }
        !           370:
        !           371: /*
        !           372:  * Initialize the time of day register, based on the time base which is, e.g.
        !           373:  * from a filesystem.
        !           374:  */
        !           375: void
        !           376: inittodr(base)
        !           377:        time_t base;
        !           378: {
        !           379:        u_long timbuf = base;   /* assume no battery clock exists */
        !           380:        static int bbcinited = 0;
        !           381:
        !           382:        /* XXX */
        !           383:        if (!bbcinited) {
        !           384:                if (machineid == HP_425 && mmuid == MMUID_425_E)
        !           385:                        bbcaddr = NULL;
        !           386:                else {
        !           387:                        if (badbaddr((caddr_t)(BBCADDR + HILP_STAT)))
        !           388:                                printf("WARNING: no battery clock\n");
        !           389:                        else
        !           390:                                bbcaddr = BBCADDR;
        !           391:                }
        !           392:                bbcinited = 1;
        !           393:        }
        !           394:
        !           395:        /*
        !           396:         * bbc_to_gmt converts and stores the gmt in timbuf.
        !           397:         * If an error is detected in bbc_to_gmt, or if the filesystem
        !           398:         * time is more recent than the gmt time in the clock,
        !           399:         * then use the filesystem time and warn the user.
        !           400:         */
        !           401:        if (bbcaddr != NULL) {
        !           402:                if (!bbc_to_gmt(&timbuf) || timbuf < base) {
        !           403:                        printf("WARNING: bad date in battery clock\n");
        !           404:                        timbuf = base;
        !           405:                }
        !           406:        }
        !           407:        if (base < 5*SECYR) {
        !           408:                printf("WARNING: preposterous time in file system");
        !           409:                timbuf = 6*SECYR + 186*SECDAY + SECDAY/2;
        !           410:                printf(" -- CHECK AND RESET THE DATE!\n");
        !           411:        }
        !           412:
        !           413:        /* Battery clock does not store usec's, so forget about it. */
        !           414:        time.tv_sec = timbuf;
        !           415: }
        !           416:
        !           417: /*
        !           418:  * Restore the time of day hardware after a time change.
        !           419:  */
        !           420: void
        !           421: resettodr()
        !           422: {
        !           423:        int i;
        !           424:        struct bbc_tm *tmptr;
        !           425:
        !           426:        if (bbcaddr == NULL)
        !           427:                return;
        !           428:
        !           429:        tmptr = gmt_to_bbc(time.tv_sec);
        !           430:
        !           431:        decimal_to_bbc(0, 1,  tmptr->tm_sec);
        !           432:        decimal_to_bbc(2, 3,  tmptr->tm_min);
        !           433:        decimal_to_bbc(4, 5,  tmptr->tm_hour);
        !           434:        decimal_to_bbc(7, 8,  tmptr->tm_mday);
        !           435:        decimal_to_bbc(9, 10, tmptr->tm_mon);
        !           436:        decimal_to_bbc(11, 12, tmptr->tm_year);
        !           437:
        !           438:        /* Some bogusness to deal with seemingly broken hardware. Nonsense */
        !           439:        bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
        !           440:
        !           441:        write_bbc_reg(15, 13);  /* reset prescalar */
        !           442:
        !           443:        for (i = 0; i <= NUM_BBC_REGS; i++)
        !           444:                if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
        !           445:                        printf("Cannot set battery backed clock\n");
        !           446:                        break;
        !           447:                }
        !           448: }
        !           449:
        !           450: struct bbc_tm *
        !           451: gmt_to_bbc(tim)
        !           452:        long tim;
        !           453: {
        !           454:        int i;
        !           455:        long hms, day;
        !           456:        static struct bbc_tm rt;
        !           457:
        !           458:        day = tim / SECDAY;
        !           459:        hms = tim % SECDAY;
        !           460:
        !           461:        /* Hours, minutes, seconds are easy */
        !           462:        rt.tm_hour = hms / 3600;
        !           463:        rt.tm_min  = (hms % 3600) / 60;
        !           464:        rt.tm_sec  = (hms % 3600) % 60;
        !           465:
        !           466:        /* Number of years in days */
        !           467:        for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
        !           468:                day -= days_in_year(i);
        !           469:        rt.tm_year = i;
        !           470:
        !           471:        /* Number of months in days left */
        !           472:        if (leapyear(rt.tm_year))
        !           473:                days_in_month(FEBRUARY) = 29;
        !           474:        for (i = 1; day >= days_in_month(i); i++)
        !           475:                day -= days_in_month(i);
        !           476:        days_in_month(FEBRUARY) = 28;
        !           477:        rt.tm_mon = i;
        !           478:
        !           479:        /* Days are what is left over (+1) from all that. */
        !           480:        rt.tm_mday = day + 1;
        !           481:
        !           482:        return(&rt);
        !           483: }
        !           484:
        !           485: int
        !           486: bbc_to_gmt(timbuf)
        !           487:        u_long *timbuf;
        !           488: {
        !           489:        int i;
        !           490:        u_long tmp;
        !           491:        int year, month, day, hour, min, sec;
        !           492:
        !           493:        read_bbc();
        !           494:
        !           495:        sec = bbc_to_decimal(1, 0);
        !           496:        min = bbc_to_decimal(3, 2);
        !           497:
        !           498:        /*
        !           499:         * Hours are different for some reason. Makes no sense really.
        !           500:         */
        !           501:        hour  = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
        !           502:        day   = bbc_to_decimal(8, 7);
        !           503:        month = bbc_to_decimal(10, 9);
        !           504:        year  = bbc_to_decimal(12, 11) + 1900;
        !           505:
        !           506:        range_test(hour, 0, 23);
        !           507:        range_test(day, 1, 31);
        !           508:        range_test(month, 1, 12);
        !           509:        range_test(year, STARTOFTIME, 2038);    /* 2038 is the end of time. */
        !           510:
        !           511:        tmp = 0;
        !           512:
        !           513:        for (i = STARTOFTIME; i < year; i++)
        !           514:                tmp += days_in_year(i);
        !           515:        if (leapyear(year) && month > FEBRUARY)
        !           516:                tmp++;
        !           517:
        !           518:        for (i = 1; i < month; i++)
        !           519:                tmp += days_in_month(i);
        !           520:
        !           521:        tmp += (day - 1);
        !           522:        tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
        !           523:
        !           524:        *timbuf = tmp;
        !           525:        return(1);
        !           526: }
        !           527:
        !           528: void
        !           529: read_bbc()
        !           530: {
        !           531:        int i, read_okay;
        !           532:
        !           533:        read_okay = 0;
        !           534:        while (!read_okay) {
        !           535:                read_okay = 1;
        !           536:                for (i = 0; i <= NUM_BBC_REGS; i++)
        !           537:                        bbc_registers[i] = read_bbc_reg(i);
        !           538:                for (i = 0; i <= NUM_BBC_REGS; i++)
        !           539:                        if (bbc_registers[i] != read_bbc_reg(i))
        !           540:                                read_okay = 0;
        !           541:        }
        !           542: }
        !           543:
        !           544: u_char
        !           545: read_bbc_reg(reg)
        !           546:        int reg;
        !           547: {
        !           548:        u_char data = reg;
        !           549:
        !           550:        if (bbcaddr != NULL) {
        !           551:                send_clock_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL);
        !           552:                send_clock_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data);
        !           553:        }
        !           554:        return(data);
        !           555: }
        !           556:
        !           557: u_char
        !           558: write_bbc_reg(reg, data)
        !           559:        int reg;
        !           560:        u_int data;
        !           561: {
        !           562:        u_char tmp;
        !           563:
        !           564:        tmp = (u_char) ((data << HIL_SSHIFT) | reg);
        !           565:
        !           566:        if (bbcaddr != NULL) {
        !           567:                send_clock_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL);
        !           568:                send_clock_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL);
        !           569:                send_clock_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp);
        !           570:        }
        !           571:        return(tmp);
        !           572: }
        !           573:
        !           574: /*
        !           575:  * Battery-backed clock command interface.
        !           576:  * The BBC appears to have an HIL-like command interface, but can not attach
        !           577:  * as a complete HIL device to an HIL controller driver.
        !           578:  * The following routine is a simplified command loop.
        !           579:  */
        !           580: void
        !           581: send_clock_cmd(volatile u_int8_t *address, u_int8_t cmd, u_int8_t *data,
        !           582:     u_int8_t dlen, u_int8_t *rdata)
        !           583: {
        !           584:        u_int8_t status;
        !           585:        int s;
        !           586:
        !           587:        s = splvm();
        !           588:
        !           589:        while ((address[HILP_STAT] & HIL_BUSY) != 0)
        !           590:                DELAY(1);
        !           591:        address[HILP_CMD] = cmd;
        !           592:        while (dlen--) {
        !           593:                while ((address[HILP_STAT] & HIL_BUSY) != 0)
        !           594:                        DELAY(1);
        !           595:                address[HILP_DATA] = *data++;
        !           596:                DELAY(1);
        !           597:        }
        !           598:        if (rdata != NULL) {
        !           599:                do {
        !           600:                        while ((address[HILP_STAT] & HIL_DATA_RDY) == 0)
        !           601:                                DELAY(1);
        !           602:                        status = address[HILP_STAT];
        !           603:                        *rdata = address[HILP_DATA];
        !           604:                        DELAY(1);
        !           605:                } while (((status >> HIL_SSHIFT) & HIL_SMASK) != HIL_68K);
        !           606:        }
        !           607:
        !           608:        splx(s);
        !           609: }

CVSweb