[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

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