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

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

1.1     ! nbrk        1: /*     $OpenBSD: clock.c,v 1.24 2006/12/10 16:12:41 miod Exp $ */
        !             2: /*     $NetBSD: clock.c,v 1.52 1997/05/24 20:16:05 pk Exp $ */
        !             3:
        !             4: /*
        !             5:  * Copyright (c) 1992, 1993
        !             6:  *     The Regents of the University of California.  All rights reserved.
        !             7:  * Copyright (c) 1994 Gordon W. Ross
        !             8:  * Copyright (c) 1993 Adam Glass
        !             9:  * Copyright (c) 1996 Paul Kranenburg
        !            10:  * Copyright (c) 1996
        !            11:  *     The President and Fellows of Harvard College. All rights reserved.
        !            12:  *
        !            13:  * This software was developed by the Computer Systems Engineering group
        !            14:  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
        !            15:  * contributed to Berkeley.
        !            16:  *
        !            17:  * All advertising materials mentioning features or use of this software
        !            18:  * must display the following acknowledgement:
        !            19:  *     This product includes software developed by Harvard University.
        !            20:  *     This product includes software developed by the University of
        !            21:  *     California, Lawrence Berkeley Laboratory.
        !            22:  *
        !            23:  * Redistribution and use in source and binary forms, with or without
        !            24:  * modification, are permitted provided that the following conditions
        !            25:  * are met:
        !            26:  *
        !            27:  * 1. Redistributions of source code must retain the above copyright
        !            28:  *    notice, this list of conditions and the following disclaimer.
        !            29:  * 2. Redistributions in binary form must reproduce the above copyright
        !            30:  *    notice, this list of conditions and the following disclaimer in the
        !            31:  *    documentation and/or other materials provided with the distribution.
        !            32:  * 3. All advertising materials mentioning features or use of this software
        !            33:  *    must display the following acknowledgement:
        !            34:  *     This product includes software developed by the University of
        !            35:  *     California, Berkeley and its contributors.
        !            36:  *     This product includes software developed by Paul Kranenburg.
        !            37:  *     This product includes software developed by Harvard University.
        !            38:  * 4. Neither the name of the University nor the names of its contributors
        !            39:  *    may be used to endorse or promote products derived from this software
        !            40:  *    without specific prior written permission.
        !            41:  *
        !            42:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            43:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            44:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            45:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            46:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            47:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            48:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            49:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            50:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            51:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            52:  * SUCH DAMAGE.
        !            53:  *
        !            54:  *     @(#)clock.c     8.1 (Berkeley) 6/11/93
        !            55:  *
        !            56:  */
        !            57:
        !            58: /*
        !            59:  * Clock driver.  This is the id prom and eeprom driver as well
        !            60:  * and includes the timer register functions too.
        !            61:  */
        !            62:
        !            63: #include <sys/param.h>
        !            64: #include <sys/kernel.h>
        !            65: #include <sys/device.h>
        !            66: #include <sys/proc.h>
        !            67: #include <sys/resourcevar.h>
        !            68: #include <sys/malloc.h>
        !            69: #include <sys/systm.h>
        !            70: #ifdef GPROF
        !            71: #include <sys/gmon.h>
        !            72: #endif
        !            73:
        !            74: #include <uvm/uvm_extern.h>
        !            75:
        !            76: #include <machine/autoconf.h>
        !            77: #include <machine/eeprom.h>
        !            78: #include <machine/cpu.h>
        !            79:
        !            80: #include <sparc/sparc/vaddrs.h>
        !            81: #include <sparc/sparc/cpuvar.h>
        !            82: #include <sparc/sparc/clockreg.h>
        !            83: #include <sparc/sparc/intreg.h>
        !            84: #include <sparc/sparc/timerreg.h>
        !            85:
        !            86: /*
        !            87:  * Statistics clock interval and variance, in usec.  Variance must be a
        !            88:  * power of two.  Since this gives us an even number, not an odd number,
        !            89:  * we discard one case and compensate.  That is, a variance of 1024 would
        !            90:  * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
        !            91:  * This is symmetric about the point 512, or statvar/2, and thus averages
        !            92:  * to that value (assuming uniform random numbers).
        !            93:  */
        !            94: /* XXX fix comment to match value */
        !            95: int statvar = 8192;
        !            96: int statmin;                   /* statclock interval - 1/2*variance */
        !            97: int timerok;
        !            98:
        !            99: #include <dev/ic/intersil7170.h>
        !           100:
        !           101: extern struct idprom idprom;
        !           102:
        !           103: #define intersil_command(run, interrupt) \
        !           104:     (run | interrupt | INTERSIL_CMD_FREQ_32K | INTERSIL_CMD_24HR_MODE | \
        !           105:      INTERSIL_CMD_NORMAL_MODE)
        !           106:
        !           107: #define intersil_disable(CLOCK) \
        !           108:     CLOCK->clk_cmd_reg = \
        !           109:     intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE)
        !           110:
        !           111: #define intersil_enable(CLOCK) \
        !           112:     CLOCK->clk_cmd_reg = \
        !           113:     intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE)
        !           114:
        !           115: #define intersil_clear(CLOCK) CLOCK->clk_intr_reg
        !           116:
        !           117: #if defined(SUN4)
        !           118: /*
        !           119:  * OCLOCK support: 4/100's and 4/200's have the old clock.
        !           120:  */
        !           121: static int oldclk = 0;
        !           122: struct intersil7170 *i7;
        !           123:
        !           124: long   oclk_get_secs(void);
        !           125: void   oclk_get_dt(struct intersil_dt *);
        !           126: void   oclk_set_dt(struct intersil_dt *);
        !           127: void   oclk_set_secs(long);
        !           128: #endif
        !           129:
        !           130: int    oclockmatch(struct device *, void *, void *);
        !           131: void   oclockattach(struct device *, struct device *, void *);
        !           132:
        !           133: struct cfattach oclock_ca = {
        !           134:        sizeof(struct device), oclockmatch, oclockattach
        !           135: };
        !           136:
        !           137: struct cfdriver oclock_cd = {
        !           138:        NULL, "oclock", DV_DULL
        !           139: };
        !           140:
        !           141: /*
        !           142:  * Sun 4 machines use the old-style (a'la Sun 3) EEPROM.  On the
        !           143:  * 4/100's and 4/200's, this is at a separate obio space.  On the
        !           144:  * 4/300's and 4/400's, however, it is the cl_nvram[] chunk of the
        !           145:  * Mostek chip.  Therefore, eeprom_match will only return true on
        !           146:  * the 100/200 models, and the eeprom will be attached separately.
        !           147:  * On the 300/400 models, the eeprom will be dealt with when the clock is
        !           148:  * attached.
        !           149:  */
        !           150: char           *eeprom_va = NULL;
        !           151: #if defined(SUN4)
        !           152: static int     eeprom_busy = 0;
        !           153: static int     eeprom_wanted = 0;
        !           154: static int     eeprom_nvram = 0;       /* non-zero if eeprom is on Mostek */
        !           155: int    eeprom_take(void);
        !           156: void   eeprom_give(void);
        !           157: int    eeprom_update(char *, int, int);
        !           158: #endif
        !           159:
        !           160: int    eeprom_match(struct device *, void *, void *);
        !           161: void   eeprom_attach(struct device *, struct device *, void *);
        !           162:
        !           163: struct cfattach eeprom_ca = {
        !           164:        sizeof(struct device), eeprom_match, eeprom_attach
        !           165: };
        !           166:
        !           167: struct cfdriver eeprom_cd = {
        !           168:        NULL, "eeprom", DV_DULL
        !           169: };
        !           170:
        !           171: int    clockmatch(struct device *, void *, void *);
        !           172: void   clockattach(struct device *, struct device *, void *);
        !           173:
        !           174: struct cfattach clock_ca = {
        !           175:        sizeof(struct device), clockmatch, clockattach
        !           176: };
        !           177:
        !           178: struct cfdriver clock_cd = {
        !           179:        NULL, "clock", DV_DULL
        !           180: };
        !           181:
        !           182: int    timermatch(struct device *, void *, void *);
        !           183: void   timerattach(struct device *, struct device *, void *);
        !           184:
        !           185: struct timer_4m        *timerreg_4m;   /* XXX - need more cleanup */
        !           186: struct counter_4m      *counterreg_4m;
        !           187: #define        timerreg4               ((struct timerreg_4 *)TIMERREG_VA)
        !           188:
        !           189: struct cfattach timer_ca = {
        !           190:        sizeof(struct device), timermatch, timerattach
        !           191: };
        !           192:
        !           193: struct cfdriver timer_cd = {
        !           194:        NULL, "timer", DV_DULL
        !           195: };
        !           196:
        !           197: void clk_wenable(int);
        !           198: void myetheraddr(u_char *);
        !           199:
        !           200: int timerblurb = 10; /* Guess a value; used before clock is attached */
        !           201:
        !           202: /*
        !           203:  * old clock match routine
        !           204:  */
        !           205: int
        !           206: oclockmatch(parent, vcf, aux)
        !           207:        struct device *parent;
        !           208:        void *vcf, *aux;
        !           209: {
        !           210:        struct confargs *ca = aux;
        !           211:
        !           212:        /* Only these sun4s have oclock */
        !           213:        if (!CPU_ISSUN4 ||
        !           214:            (cpuinfo.cpu_type != CPUTYP_4_100 &&
        !           215:             cpuinfo.cpu_type != CPUTYP_4_200))
        !           216:                return (0);
        !           217:
        !           218:        /* Check configuration name */
        !           219:        if (strcmp(oclock_cd.cd_name, ca->ca_ra.ra_name) != 0)
        !           220:                return (0);
        !           221:
        !           222:        /* Make sure there is something there */
        !           223:        if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
        !           224:                return (0);
        !           225:
        !           226:        return (1);
        !           227: }
        !           228:
        !           229: /* ARGSUSED */
        !           230: void
        !           231: oclockattach(parent, self, aux)
        !           232:        struct device *parent, *self;
        !           233:        void *aux;
        !           234: {
        !           235: #if defined(SUN4)
        !           236:        struct confargs *ca = aux;
        !           237:        struct romaux *ra = &ca->ca_ra;
        !           238:        struct idprom *idp;
        !           239:        int h;
        !           240:
        !           241:        oldclk = 1;  /* we've got an oldie! */
        !           242:
        !           243:        i7 = (struct intersil7170 *) mapiodev(ra->ra_reg, 0, sizeof(*i7));
        !           244:
        !           245:        idp = &idprom;
        !           246:        h = idp->id_machine << 24;
        !           247:        h |= idp->id_hostid[0] << 16;
        !           248:        h |= idp->id_hostid[1] << 8;
        !           249:        h |= idp->id_hostid[2];
        !           250:        hostid = h;
        !           251:
        !           252:        /*
        !           253:         * calibrate delay()
        !           254:         */
        !           255:        ienab_bic(IE_L14 | IE_L10);     /* disable all clock intrs */
        !           256:        for (timerblurb = 1; ; timerblurb++) {
        !           257:                volatile register char *ireg = &i7->clk_intr_reg;
        !           258:                int ival;
        !           259:                *ireg = INTERSIL_INTER_CSECONDS; /* 1/100 sec */
        !           260:                intersil_enable(i7);             /* enable clock */
        !           261:                while ((*ireg & INTERSIL_INTER_PENDING) == 0)
        !           262:                        /* sync with interrupt */;
        !           263:                while ((*ireg & INTERSIL_INTER_PENDING) == 0)
        !           264:                        /* XXX: do it again, seems to need it */;
        !           265:                delay(10000);                   /* Probe 1/100 sec delay */
        !           266:                ival = *ireg;                   /* clear, save value */
        !           267:                intersil_disable(i7);           /* disable clock */
        !           268:                if (ival & INTERSIL_INTER_PENDING) {
        !           269:                        printf(" delay constant %d%s\n", timerblurb,
        !           270:                                (timerblurb == 1) ? " [TOO SMALL?]" : "");
        !           271:                        break;
        !           272:                }
        !           273:                if (timerblurb > 10) {
        !           274:                        printf("\noclock: calibration failing; clamped at %d\n",
        !           275:                               timerblurb);
        !           276:                        break;
        !           277:                }
        !           278:        }
        !           279: #endif /* SUN4 */
        !           280: }
        !           281:
        !           282: /*
        !           283:  * Sun 4/100, 4/200 EEPROM match routine.
        !           284:  */
        !           285: int
        !           286: eeprom_match(parent, vcf, aux)
        !           287:        struct device *parent;
        !           288:        void *vcf, *aux;
        !           289: {
        !           290:        struct cfdata *cf = vcf;
        !           291:        struct confargs *ca = aux;
        !           292:
        !           293:        if (!CPU_ISSUN4)
        !           294:                return (0);
        !           295:
        !           296:        if (cf->cf_unit != 0)
        !           297:                return (0);
        !           298:
        !           299:        if (cpuinfo.cpu_type != CPUTYP_4_100 &&
        !           300:            cpuinfo.cpu_type != CPUTYP_4_200)
        !           301:                return (0);
        !           302:
        !           303:        if (strcmp(eeprom_cd.cd_name, ca->ca_ra.ra_name) != 0)
        !           304:                return (0);
        !           305:
        !           306:        /*
        !           307:         * Make sure there's something there...
        !           308:         * This is especially important if we want to
        !           309:         * use the same kernel on a 4/100 as a 4/200.
        !           310:         */
        !           311:        if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
        !           312:                return (0);
        !           313:
        !           314:        /* Passed all tests */
        !           315:        return (1);
        !           316: }
        !           317:
        !           318: void
        !           319: eeprom_attach(parent, self, aux)
        !           320:        struct device *parent, *self;
        !           321:        void *aux;
        !           322: {
        !           323: #if defined(SUN4)
        !           324:        struct confargs *ca = aux;
        !           325:        struct romaux *ra = &ca->ca_ra;
        !           326:
        !           327:        printf("\n");
        !           328:
        !           329:        eeprom_va = (char *)mapiodev(ra->ra_reg, 0, EEPROM_SIZE);
        !           330:
        !           331:        eeprom_nvram = 0;
        !           332: #endif /* SUN4 */
        !           333: }
        !           334:
        !           335: /*
        !           336:  * The OPENPROM calls the clock the "eeprom", so we have to have our
        !           337:  * own special match function to call it the "clock".
        !           338:  */
        !           339: int
        !           340: clockmatch(parent, vcf, aux)
        !           341:        struct device *parent;
        !           342:        void *vcf, *aux;
        !           343: {
        !           344:        struct confargs *ca = aux;
        !           345:
        !           346:        if (CPU_ISSUN4) {
        !           347:                /* Only these sun4s have "clock" (others have "oclock") */
        !           348:                if (cpuinfo.cpu_type != CPUTYP_4_300 &&
        !           349:                    cpuinfo.cpu_type != CPUTYP_4_400)
        !           350:                        return (0);
        !           351:
        !           352:                if (strcmp(clock_cd.cd_name, ca->ca_ra.ra_name) != 0)
        !           353:                        return (0);
        !           354:
        !           355:                /* Make sure there is something there */
        !           356:                if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
        !           357:                        return (0);
        !           358:
        !           359:                return (1);
        !           360:        }
        !           361:
        !           362:        return (strcmp("eeprom", ca->ca_ra.ra_name) == 0);
        !           363: }
        !           364:
        !           365: /* ARGSUSED */
        !           366: void
        !           367: clockattach(parent, self, aux)
        !           368:        struct device *parent, *self;
        !           369:        void *aux;
        !           370: {
        !           371:        int h;
        !           372:        struct clockreg *cl;
        !           373:        struct idprom *idp;
        !           374:        struct confargs *ca = aux;
        !           375:        struct romaux *ra = &ca->ca_ra;
        !           376:        char *prop = NULL;
        !           377:
        !           378:        if (CPU_ISSUN4)
        !           379:                prop = "mk48t02";
        !           380:
        !           381:        else if (CPU_ISSUN4COR4M)
        !           382:                prop = getpropstring(ra->ra_node, "model");
        !           383:
        !           384:        printf(": %s (eeprom)\n", prop);
        !           385:
        !           386:        /*
        !           387:         * We ignore any existing virtual address as we need to map
        !           388:         * this read-only and make it read-write only temporarily,
        !           389:         * whenever we read or write the clock chip.  The clock also
        !           390:         * contains the ID ``PROM'', and I have already had the pleasure
        !           391:         * of reloading the cpu type, Ethernet address, etc, by hand from
        !           392:         * the console FORTH interpreter.  I intend not to enjoy it again.
        !           393:         */
        !           394:        if (strcmp(prop, "mk48t08") == 0) {
        !           395:                /*
        !           396:                 * the MK48T08 is 8K
        !           397:                 */
        !           398:                cl = (struct clockreg *)mapiodev(ra->ra_reg, 0, 8192);
        !           399:                pmap_changeprot(pmap_kernel(), (vaddr_t)cl, VM_PROT_READ, 1);
        !           400:                pmap_changeprot(pmap_kernel(), (vaddr_t)cl + 4096,
        !           401:                                VM_PROT_READ, 1);
        !           402:                cl = (struct clockreg *)((int)cl + CLK_MK48T08_OFF);
        !           403:        } else {
        !           404:                /*
        !           405:                 * the MK48T02 is 2K
        !           406:                 */
        !           407:                cl = (struct clockreg *)mapiodev(ra->ra_reg, 0,
        !           408:                                                 sizeof *clockreg);
        !           409:                pmap_changeprot(pmap_kernel(), (vaddr_t)cl, VM_PROT_READ, 1);
        !           410:        }
        !           411:        idp = &cl->cl_idprom;
        !           412:
        !           413: #if defined(SUN4)
        !           414:        if (CPU_ISSUN4) {
        !           415:                idp = &idprom;
        !           416:
        !           417:                if (cpuinfo.cpu_type == CPUTYP_4_300 ||
        !           418:                    cpuinfo.cpu_type == CPUTYP_4_400) {
        !           419:                        eeprom_va = (char *)cl->cl_nvram;
        !           420:                        eeprom_nvram = 1;
        !           421:                }
        !           422:        }
        !           423: #endif
        !           424:
        !           425:        h = idp->id_machine << 24;
        !           426:        h |= idp->id_hostid[0] << 16;
        !           427:        h |= idp->id_hostid[1] << 8;
        !           428:        h |= idp->id_hostid[2];
        !           429:        hostid = h;
        !           430:        clockreg = cl;
        !           431: }
        !           432:
        !           433: /*
        !           434:  * The OPENPROM calls the timer the "counter-timer".
        !           435:  */
        !           436: int
        !           437: timermatch(parent, vcf, aux)
        !           438:        struct device *parent;
        !           439:        void *vcf, *aux;
        !           440: {
        !           441:        struct confargs *ca = aux;
        !           442:
        !           443:        if (CPU_ISSUN4) {
        !           444:                if (cpuinfo.cpu_type != CPUTYP_4_300 &&
        !           445:                    cpuinfo.cpu_type != CPUTYP_4_400)
        !           446:                        return (0);
        !           447:
        !           448:                if (strcmp("timer", ca->ca_ra.ra_name) != 0)
        !           449:                        return (0);
        !           450:
        !           451:                /* Make sure there is something there */
        !           452:                if (probeget(ca->ca_ra.ra_vaddr, 4) == -1)
        !           453:                        return (0);
        !           454:
        !           455:                return (1);
        !           456:        }
        !           457:
        !           458:        if (CPU_ISSUN4C) {
        !           459:                return (strcmp("counter-timer", ca->ca_ra.ra_name) == 0);
        !           460:        }
        !           461:
        !           462:        if (CPU_ISSUN4M) {
        !           463:                return (strcmp("counter", ca->ca_ra.ra_name) == 0);
        !           464:        }
        !           465:
        !           466:        return (0);
        !           467: }
        !           468:
        !           469: /* ARGSUSED */
        !           470: void
        !           471: timerattach(parent, self, aux)
        !           472:        struct device *parent, *self;
        !           473:        void *aux;
        !           474: {
        !           475:        struct confargs *ca = aux;
        !           476:        struct romaux *ra = &ca->ca_ra;
        !           477:        volatile int *cnt = NULL, *lim = NULL;
        !           478:                /* XXX: must init to NULL to avoid stupid gcc -Wall warning */
        !           479:
        !           480:        if (CPU_ISSUN4M) {
        !           481:                (void)mapdev(&ra->ra_reg[ra->ra_nreg-1], TIMERREG_VA, 0,
        !           482:                             sizeof(struct timer_4m));
        !           483:                (void)mapdev(&ra->ra_reg[0], COUNTERREG_VA, 0,
        !           484:                             sizeof(struct counter_4m));
        !           485:                timerreg_4m = (struct timer_4m *)TIMERREG_VA;
        !           486:                counterreg_4m = (struct counter_4m *)COUNTERREG_VA;
        !           487:
        !           488:                /* Put processor counter in "timer" mode */
        !           489:                timerreg_4m->t_cfg = 0;
        !           490:
        !           491:                cnt = &counterreg_4m->t_counter;
        !           492:                lim = &counterreg_4m->t_limit;
        !           493:        }
        !           494:
        !           495:        if (CPU_ISSUN4OR4C) {
        !           496:                /*
        !           497:                 * This time, we ignore any existing virtual address because
        !           498:                 * we have a fixed virtual address for the timer, to make
        !           499:                 * microtime() faster (in SUN4/SUN4C kernel only).
        !           500:                 */
        !           501:                (void)mapdev(ra->ra_reg, TIMERREG_VA, 0,
        !           502:                             sizeof(struct timerreg_4));
        !           503:
        !           504:                cnt = &timerreg4->t_c14.t_counter;
        !           505:                lim = &timerreg4->t_c14.t_limit;
        !           506:        }
        !           507:
        !           508:        timerok = 1;
        !           509:
        !           510:        /*
        !           511:         * Calibrate delay() by tweaking the magic constant
        !           512:         * until a delay(100) actually reads (at least) 100 us on the clock.
        !           513:         * Note: sun4m clocks tick with 500ns periods.
        !           514:         */
        !           515:
        !           516:        for (timerblurb = 1; ; timerblurb++) {
        !           517:                volatile int discard;
        !           518:                int t0, t1;
        !           519:
        !           520:                /* Reset counter register by writing some large limit value */
        !           521:                discard = *lim;
        !           522:                *lim = tmr_ustolim(TMR_MASK-1);
        !           523:
        !           524:                t0 = *cnt;
        !           525:                delay(100);
        !           526:                t1 = *cnt;
        !           527:
        !           528:                if (t1 & TMR_LIMIT)
        !           529:                        panic("delay calibration");
        !           530:
        !           531:                t0 = (t0 >> TMR_SHIFT) & TMR_MASK;
        !           532:                t1 = (t1 >> TMR_SHIFT) & TMR_MASK;
        !           533:
        !           534:                if (t1 >= t0 + 100)
        !           535:                        break;
        !           536:
        !           537:        }
        !           538:
        !           539:        printf(" delay constant %d\n", timerblurb);
        !           540:
        !           541:        /* should link interrupt handlers here, rather than compiled-in? */
        !           542: }
        !           543:
        !           544: /*
        !           545:  * Write en/dis-able clock registers.  We coordinate so that several
        !           546:  * writers can run simultaneously.
        !           547:  */
        !           548: void
        !           549: clk_wenable(onoff)
        !           550:        int onoff;
        !           551: {
        !           552:        int s;
        !           553:        vm_prot_t prot;/* nonzero => change prot */
        !           554:        static int writers;
        !           555:
        !           556:        s = splhigh();
        !           557:        if (onoff)
        !           558:                prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0;
        !           559:        else
        !           560:                prot = --writers == 0 ? VM_PROT_READ : 0;
        !           561:        splx(s);
        !           562:        if (prot)
        !           563:                pmap_changeprot(pmap_kernel(), (vaddr_t)clockreg & ~(NBPG-1),
        !           564:                                prot, 1);
        !           565: }
        !           566:
        !           567: /*
        !           568:  * XXX this belongs elsewhere
        !           569:  */
        !           570: void
        !           571: myetheraddr(cp)
        !           572:        u_char *cp;
        !           573: {
        !           574:        struct clockreg *cl = clockreg;
        !           575:        struct idprom *idp = &cl->cl_idprom;
        !           576:
        !           577: #if defined(SUN4)
        !           578:        if (CPU_ISSUN4)
        !           579:                idp = &idprom;
        !           580: #endif
        !           581:
        !           582:        cp[0] = idp->id_ether[0];
        !           583:        cp[1] = idp->id_ether[1];
        !           584:        cp[2] = idp->id_ether[2];
        !           585:        cp[3] = idp->id_ether[3];
        !           586:        cp[4] = idp->id_ether[4];
        !           587:        cp[5] = idp->id_ether[5];
        !           588: }
        !           589:
        !           590: /*
        !           591:  * Set up the real-time and statistics clocks.  Leave stathz 0 only if
        !           592:  * no alternative timer is available.
        !           593:  *
        !           594:  * The frequencies of these clocks must be an even number of microseconds.
        !           595:  */
        !           596: void
        !           597: cpu_initclocks()
        !           598: {
        !           599:        int statint, minint;
        !           600:
        !           601: #if defined(SUN4)
        !           602:        if (oldclk) {
        !           603:                int dummy;
        !           604:
        !           605:                if (hz != 100) {
        !           606:                        printf("oclock0: cannot get %d Hz clock; using 100 Hz\n", hz);
        !           607:                }
        !           608:
        !           609:                profhz = hz = 100;
        !           610:                tick = 1000000 / hz;
        !           611:
        !           612:                i7->clk_intr_reg = INTERSIL_INTER_CSECONDS; /* 1/100 sec */
        !           613:
        !           614:                ienab_bic(IE_L14 | IE_L10);     /* disable all clock intrs */
        !           615:                intersil_disable(i7);           /* disable clock */
        !           616:                dummy = intersil_clear(i7);     /* clear interrupts */
        !           617:                ienab_bis(IE_L10);              /* enable l10 interrupt */
        !           618:                intersil_enable(i7);            /* enable clock */
        !           619:
        !           620:                return;
        !           621:        }
        !           622: #endif /* SUN4 */
        !           623:
        !           624:        if (1000000 % hz) {
        !           625:                printf("clock0: cannot get %d Hz clock; using 100 Hz\n", hz);
        !           626:                hz = 100;
        !           627:                tick = 1000000 / hz;
        !           628:        }
        !           629:        if (stathz == 0)
        !           630:                stathz = hz;
        !           631:        if (1000000 % stathz) {
        !           632:                printf("clock0: cannot get %d Hz statclock; using 100 Hz\n", stathz);
        !           633:                stathz = 100;
        !           634:        }
        !           635:        profhz = stathz;                /* always */
        !           636:
        !           637:        statint = 1000000 / stathz;
        !           638:        minint = statint / 2 + 100;
        !           639:        while (statvar > minint)
        !           640:                statvar >>= 1;
        !           641:
        !           642:        if (CPU_ISSUN4M) {
        !           643:                timerreg_4m->t_limit = tmr_ustolim4m(tick);
        !           644:                counterreg_4m->t_limit = tmr_ustolim4m(statint);
        !           645:        }
        !           646:
        !           647:        if (CPU_ISSUN4OR4C) {
        !           648:                timerreg4->t_c10.t_limit = tmr_ustolim(tick);
        !           649:                timerreg4->t_c14.t_limit = tmr_ustolim(statint);
        !           650:        }
        !           651:
        !           652:        statmin = statint - (statvar >> 1);
        !           653:
        !           654: #if defined(SUN4M)
        !           655:        if (CPU_ISSUN4M)
        !           656:                ienab_bic(SINTR_T);
        !           657: #endif
        !           658:
        !           659:        if (CPU_ISSUN4OR4C)
        !           660:                ienab_bis(IE_L14 | IE_L10);
        !           661:
        !           662: }
        !           663:
        !           664: /*
        !           665:  * Dummy setstatclockrate(), since we know profhz==hz.
        !           666:  */
        !           667: /* ARGSUSED */
        !           668: void
        !           669: setstatclockrate(newhz)
        !           670:        int newhz;
        !           671: {
        !           672:        /* nothing */
        !           673: }
        !           674:
        !           675: /*
        !           676:  * Level 10 (clock) interrupts.  If we are using the FORTH PROM for
        !           677:  * console input, we need to check for that here as well, and generate
        !           678:  * a software interrupt to read it.
        !           679:  */
        !           680: int
        !           681: clockintr(cap)
        !           682:        void *cap;
        !           683: {
        !           684:        volatile int discard;
        !           685:        int s;
        !           686:
        !           687:        /*
        !           688:         * Protect the clearing of the clock interrupt.  If we don't
        !           689:         * do this, and we're interrupted (by the zs, for example),
        !           690:         * the clock stops!
        !           691:         * XXX WHY DOES THIS HAPPEN?
        !           692:         */
        !           693:        s = splhigh();
        !           694:
        !           695: #if defined(SUN4)
        !           696:        if (oldclk) {
        !           697:                discard = intersil_clear(i7);
        !           698:                ienab_bic(IE_L10);  /* clear interrupt */
        !           699:                ienab_bis(IE_L10);  /* enable interrupt */
        !           700:                goto forward;
        !           701:        }
        !           702: #endif
        !           703: #if defined(SUN4M)
        !           704:        /* read the limit register to clear the interrupt */
        !           705:        if (CPU_ISSUN4M) {
        !           706:                discard = timerreg_4m->t_limit;
        !           707:        }
        !           708: #endif
        !           709: #if defined(SUN4) || defined(SUN4C)
        !           710:        if (CPU_ISSUN4OR4C) {
        !           711:                discard = timerreg4->t_c10.t_limit;
        !           712:        }
        !           713: #endif
        !           714: #if defined(SUN4)
        !           715: forward:
        !           716: #endif
        !           717:        splx(s);
        !           718:
        !           719:        hardclock((struct clockframe *)cap);
        !           720:
        !           721:        return (1);
        !           722: }
        !           723:
        !           724: /*
        !           725:  * Level 14 (stat clock) interrupt handler.
        !           726:  */
        !           727: int
        !           728: statintr(cap)
        !           729:        void *cap;
        !           730: {
        !           731:        volatile int discard;
        !           732:        u_long newint, r, var;
        !           733:
        !           734: #if defined(SUN4)
        !           735:        if (oldclk) {
        !           736:                panic("oldclk statintr");
        !           737:                return (1);
        !           738:        }
        !           739: #endif
        !           740:
        !           741:        /* read the limit register to clear the interrupt */
        !           742:        if (CPU_ISSUN4M) {
        !           743:                discard = counterreg_4m->t_limit;
        !           744:                if (timerok == 0) {
        !           745:                        /* Stop the clock */
        !           746:                        counterreg_4m->t_limit = 0;
        !           747:                        counterreg_4m->t_ss = 0;
        !           748:                        timerreg_4m->t_cfg = TMR_CFG_USER;
        !           749:                        return 1;
        !           750:                }
        !           751:        }
        !           752:
        !           753:        if (CPU_ISSUN4OR4C) {
        !           754:                discard = timerreg4->t_c14.t_limit;
        !           755:        }
        !           756:        statclock((struct clockframe *)cap);
        !           757:
        !           758:        /*
        !           759:         * Compute new randomized interval.  The intervals are uniformly
        !           760:         * distributed on [statint - statvar / 2, statint + statvar / 2],
        !           761:         * and therefore have mean statint, giving a stathz frequency clock.
        !           762:         */
        !           763:        var = statvar;
        !           764:        do {
        !           765:                r = random() & (var - 1);
        !           766:        } while (r == 0);
        !           767:        newint = statmin + r;
        !           768:
        !           769:        if (CPU_ISSUN4M) {
        !           770:                counterreg_4m->t_limit = tmr_ustolim4m(newint);
        !           771:        }
        !           772:
        !           773:        if (CPU_ISSUN4OR4C) {
        !           774:                timerreg4->t_c14.t_limit = tmr_ustolim(newint);
        !           775:        }
        !           776:        return (1);
        !           777: }
        !           778:
        !           779: /*
        !           780:  * Set up the system's time, given a `reasonable' time value.
        !           781:  */
        !           782: void
        !           783: inittodr(base)
        !           784:        time_t base;
        !           785: {
        !           786:        struct clockreg *cl = clockreg;
        !           787:        struct clock_ymdhms dt;
        !           788:        int badbase = 0, waszero = base == 0;
        !           789:        char *bad = NULL;
        !           790:
        !           791:        if (base < 5 * SECYR) {
        !           792:                /*
        !           793:                 * If base is 0, assume filesystem time is just unknown
        !           794:                 * in stead of preposterous. Don't bark.
        !           795:                 */
        !           796:                if (base != 0)
        !           797:                        printf("WARNING: preposterous time in file system\n");
        !           798:                /* not going to use it anyway, if the chip is readable */
        !           799:                base = 21*SECYR + 186*SECDAY + SECDAY/2;
        !           800:                badbase = 1;
        !           801:        }
        !           802: #if defined(SUN4)
        !           803:        if (oldclk) {
        !           804:                time.tv_sec = oclk_get_secs();
        !           805:                goto forward;
        !           806:        }
        !           807: #endif
        !           808:        clk_wenable(1);
        !           809:        cl->cl_csr |= CLK_READ;         /* enable read (stop time) */
        !           810:        dt.dt_sec = FROMBCD(cl->cl_sec);
        !           811:        dt.dt_min = FROMBCD(cl->cl_min);
        !           812:        dt.dt_hour = FROMBCD(cl->cl_hour);
        !           813:        dt.dt_day = FROMBCD(cl->cl_mday);
        !           814:        dt.dt_mon = FROMBCD(cl->cl_month);
        !           815:        dt.dt_year = FROMBCD(cl->cl_year) + CLOCK_BASE_YEAR;
        !           816:        cl->cl_csr &= ~CLK_READ;        /* time wears on */
        !           817:        clk_wenable(0);
        !           818:        time.tv_sec = clock_ymdhms_to_secs(&dt);
        !           819:
        !           820: #if defined(SUN4)
        !           821: forward:
        !           822: #endif
        !           823:        if (time.tv_sec == 0) {
        !           824:                /*
        !           825:                 * Believe the time in the file system for lack of
        !           826:                 * anything better, resetting the clock.
        !           827:                 */
        !           828:                bad = "WARNING: bad date in battery clock";
        !           829:                time.tv_sec = base;
        !           830:                if (!badbase)
        !           831:                        resettodr();
        !           832:        } else {
        !           833:                int deltat = time.tv_sec - base;
        !           834:
        !           835:                if (deltat < 0)
        !           836:                        deltat = -deltat;
        !           837:                if (waszero || deltat < 2 * SECDAY)
        !           838:                        return;
        !           839:
        !           840: #ifndef SMALL_KERNEL
        !           841:                printf("WARNING: clock %s %d days",
        !           842:                    time.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
        !           843:                bad = "";
        !           844: #endif
        !           845:        }
        !           846:        if (bad) {
        !           847:                printf("%s", bad);
        !           848:                printf(" -- CHECK AND RESET THE DATE!\n");
        !           849:        }
        !           850: }
        !           851:
        !           852: /*
        !           853:  * Reset the clock based on the current time.
        !           854:  * Used when the current clock is preposterous, when the time is changed,
        !           855:  * and when rebooting.  Do nothing if the time is not yet known, e.g.,
        !           856:  * when crashing during autoconfig.
        !           857:  */
        !           858: void
        !           859: resettodr()
        !           860: {
        !           861:        struct clockreg *cl;
        !           862:        struct clock_ymdhms dt;
        !           863:
        !           864: #if defined(SUN4)
        !           865:        if (oldclk) {
        !           866:                if (!time.tv_sec || i7 == NULL)
        !           867:                        return;
        !           868:                oclk_set_secs(time.tv_sec);
        !           869:                return;
        !           870:        }
        !           871: #endif
        !           872:
        !           873:        if (!time.tv_sec || (cl = clockreg) == NULL)
        !           874:                return;
        !           875:
        !           876:        clock_secs_to_ymdhms(time.tv_sec, &dt);
        !           877:
        !           878:        clk_wenable(1);
        !           879:        cl->cl_csr |= CLK_WRITE;        /* enable write */
        !           880:        cl->cl_sec = TOBCD(dt.dt_sec);
        !           881:        cl->cl_min = TOBCD(dt.dt_min);
        !           882:        cl->cl_hour = TOBCD(dt.dt_hour);
        !           883:        cl->cl_wday = TOBCD(dt.dt_wday);
        !           884:        cl->cl_mday = TOBCD(dt.dt_day);
        !           885:        cl->cl_month = TOBCD(dt.dt_mon);
        !           886:        cl->cl_year = TOBCD(dt.dt_year - CLOCK_BASE_YEAR);
        !           887:        cl->cl_csr &= ~CLK_WRITE;       /* load them up */
        !           888:        clk_wenable(0);
        !           889: }
        !           890:
        !           891: #if defined(SUN4)
        !           892: /*
        !           893:  * Now routines to get and set clock as POSIX time.
        !           894:  */
        !           895: long
        !           896: oclk_get_secs()
        !           897: {
        !           898:         struct intersil_dt idt;
        !           899:        struct clock_ymdhms dt;
        !           900:
        !           901:         oclk_get_dt(&idt);
        !           902:        dt.dt_sec = idt.dt_sec;
        !           903:        dt.dt_min = idt.dt_min;
        !           904:        dt.dt_hour = idt.dt_hour;
        !           905:        dt.dt_day = idt.dt_day;
        !           906:        dt.dt_mon = idt.dt_month;
        !           907:        dt.dt_year = idt.dt_year + CLOCK_BASE_YEAR;
        !           908:         return clock_ymdhms_to_secs(&dt);
        !           909: }
        !           910:
        !           911: void
        !           912: oclk_set_secs(secs)
        !           913:        long secs;
        !           914: {
        !           915:         struct intersil_dt idt;
        !           916:        struct clock_ymdhms dt;
        !           917:
        !           918:        clock_secs_to_ymdhms(secs, &dt);
        !           919:
        !           920:        idt.dt_hour = dt.dt_hour;
        !           921:        idt.dt_min = dt.dt_min;
        !           922:        idt.dt_sec = dt.dt_sec;
        !           923:        idt.dt_month = dt.dt_mon;
        !           924:        idt.dt_day = dt.dt_day;
        !           925:        idt.dt_year = dt.dt_year - CLOCK_BASE_YEAR;
        !           926:        idt.dt_dow = dt.dt_wday;
        !           927:         oclk_set_dt(&idt);
        !           928: }
        !           929:
        !           930: /*
        !           931:  * Routine to copy state into and out of the clock.
        !           932:  * The clock registers have to be read or written
        !           933:  * in sequential order (or so it appears). -gwr
        !           934:  */
        !           935: void
        !           936: oclk_get_dt(dt)
        !           937:        struct intersil_dt *dt;
        !           938: {
        !           939:         int s;
        !           940:         register volatile char *src, *dst;
        !           941:
        !           942:         src = (char *) &i7->counters;
        !           943:
        !           944:         s = splhigh();
        !           945:         i7->clk_cmd_reg =
        !           946:                 intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
        !           947:
        !           948:         dst = (char *) dt;
        !           949:         dt++;   /* end marker */
        !           950:         do {
        !           951:                 *dst++ = *src++;
        !           952:         } while (dst < (char *)dt);
        !           953:
        !           954:         i7->clk_cmd_reg =
        !           955:                 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
        !           956:         splx(s);
        !           957: }
        !           958:
        !           959: void
        !           960: oclk_set_dt(dt)
        !           961:        struct intersil_dt *dt;
        !           962: {
        !           963:         int s;
        !           964:         register volatile char *src, *dst;
        !           965:
        !           966:         dst = (char *) &i7->counters;
        !           967:
        !           968:         s = splhigh();
        !           969:         i7->clk_cmd_reg =
        !           970:                 intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
        !           971:
        !           972:         src = (char *) dt;
        !           973:         dt++;   /* end marker */
        !           974:         do {
        !           975:                 *dst++ = *src++;
        !           976:         } while (src < (char *)dt);
        !           977:
        !           978:         i7->clk_cmd_reg =
        !           979:                 intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
        !           980:         splx(s);
        !           981: }
        !           982: #endif /* SUN4 */
        !           983:
        !           984: #if defined(SUN4)
        !           985: /*
        !           986:  * Return the best possible estimate of the time in the timeval
        !           987:  * to which tvp points.  We do this by returning the current time
        !           988:  * plus the amount of time since the last clock interrupt.
        !           989:  *
        !           990:  * Check that this time is no less than any previously-reported time,
        !           991:  * which could happen around the time of a clock adjustment.  Just for
        !           992:  * fun, we guarantee that the time will be greater than the value
        !           993:  * obtained by a previous call.
        !           994:  */
        !           995: void
        !           996: microtime(tvp)
        !           997:        struct timeval *tvp;
        !           998: {
        !           999:        int s;
        !          1000:        static struct timeval lasttime;
        !          1001:        static struct timeval oneusec = {0, 1};
        !          1002:
        !          1003:        if (!oldclk) {
        !          1004:                lo_microtime(tvp);
        !          1005:                return;
        !          1006:        }
        !          1007:
        !          1008:        s = splhigh();
        !          1009:        *tvp = time;
        !          1010:        splx(s);
        !          1011:
        !          1012:        if (timercmp(tvp, &lasttime, <=))
        !          1013:                timeradd(&lasttime, &oneusec, tvp);
        !          1014:
        !          1015:        lasttime = *tvp;
        !          1016: }
        !          1017: #endif /* SUN4 */
        !          1018:
        !          1019: /*
        !          1020:  * XXX: these may actually belong somewhere else, but since the
        !          1021:  * EEPROM is so closely tied to the clock on some models, perhaps
        !          1022:  * it needs to stay here...
        !          1023:  */
        !          1024: int
        !          1025: eeprom_uio(uio)
        !          1026:        struct uio *uio;
        !          1027: {
        !          1028: #if defined(SUN4)
        !          1029:        int error;
        !          1030:        int off;        /* NOT off_t */
        !          1031:        u_int cnt, bcnt;
        !          1032:        caddr_t buf = NULL;
        !          1033:
        !          1034:        if (!CPU_ISSUN4)
        !          1035:                return (ENODEV);
        !          1036:
        !          1037:        off = uio->uio_offset;
        !          1038:        if (off > EEPROM_SIZE)
        !          1039:                return (EFAULT);
        !          1040:
        !          1041:        cnt = uio->uio_resid;
        !          1042:        if (cnt > (EEPROM_SIZE - off))
        !          1043:                cnt = (EEPROM_SIZE - off);
        !          1044:
        !          1045:        if ((error = eeprom_take()) != 0)
        !          1046:                return (error);
        !          1047:
        !          1048:        if (eeprom_va == NULL) {
        !          1049:                error = ENXIO;
        !          1050:                goto out;
        !          1051:        }
        !          1052:
        !          1053:        /*
        !          1054:         * The EEPROM can only be accessed one byte at a time, yet
        !          1055:         * uiomove() will attempt long-word access.  To circumvent
        !          1056:         * this, we byte-by-byte copy the eeprom contents into a
        !          1057:         * temporary buffer.
        !          1058:         */
        !          1059:        buf = malloc(EEPROM_SIZE, M_DEVBUF, M_WAITOK);
        !          1060:
        !          1061:        if (uio->uio_rw == UIO_READ)
        !          1062:                for (bcnt = 0; bcnt < EEPROM_SIZE; ++bcnt)
        !          1063:                        *(char *)(buf + bcnt) = *(char *)(eeprom_va + bcnt);
        !          1064:
        !          1065:        if ((error = uiomove(buf + off, (int)cnt, uio)) != 0)
        !          1066:                goto out;
        !          1067:
        !          1068:        if (uio->uio_rw != UIO_READ)
        !          1069:                error = eeprom_update(buf, off, cnt);
        !          1070:
        !          1071:  out:
        !          1072:        if (buf)
        !          1073:                free(buf, M_DEVBUF);
        !          1074:        eeprom_give();
        !          1075:        return (error);
        !          1076: #else /* ! SUN4 */
        !          1077:        return (ENODEV);
        !          1078: #endif /* SUN4 */
        !          1079: }
        !          1080:
        !          1081: #if defined(SUN4)
        !          1082: /*
        !          1083:  * Update the EEPROM from the passed buf.
        !          1084:  */
        !          1085: int
        !          1086: eeprom_update(buf, off, cnt)
        !          1087:        char *buf;
        !          1088:        int off, cnt;
        !          1089: {
        !          1090:        int error = 0;
        !          1091:        volatile char *ep;
        !          1092:        char *bp;
        !          1093:
        !          1094:        if (eeprom_va == NULL)
        !          1095:                return (ENXIO);
        !          1096:
        !          1097:        ep = eeprom_va + off;
        !          1098:        bp = buf + off;
        !          1099:
        !          1100:        if (eeprom_nvram)
        !          1101:                clk_wenable(1);
        !          1102:
        !          1103:        while (cnt > 0) {
        !          1104:                /*
        !          1105:                 * DO NOT WRITE IT UNLESS WE HAVE TO because the
        !          1106:                 * EEPROM has a limited number of write cycles.
        !          1107:                 * After some number of writes it just fails!
        !          1108:                 */
        !          1109:                if (*ep != *bp) {
        !          1110:                        *ep = *bp;
        !          1111:                        /*
        !          1112:                         * We have written the EEPROM, so now we must
        !          1113:                         * sleep for at least 10 milliseconds while
        !          1114:                         * holding the lock to prevent all access to
        !          1115:                         * the EEPROM while it recovers.
        !          1116:                         */
        !          1117:                        (void)tsleep(eeprom_va, PZERO - 1, "eeprom", hz/50);
        !          1118:                }
        !          1119:                /* Make sure the write worked. */
        !          1120:                if (*ep != *bp) {
        !          1121:                        error = EIO;
        !          1122:                        goto out;
        !          1123:                }
        !          1124:                ++ep;
        !          1125:                ++bp;
        !          1126:                --cnt;
        !          1127:        }
        !          1128:  out:
        !          1129:        if (eeprom_nvram)
        !          1130:                clk_wenable(0);
        !          1131:
        !          1132:        return (error);
        !          1133: }
        !          1134:
        !          1135: /* Take a lock on the eeprom. */
        !          1136: int
        !          1137: eeprom_take()
        !          1138: {
        !          1139:        int error = 0;
        !          1140:
        !          1141:        while (eeprom_busy) {
        !          1142:                eeprom_wanted = 1;
        !          1143:                error = tsleep(&eeprom_busy, PZERO | PCATCH, "eeprom", 0);
        !          1144:                eeprom_wanted = 0;
        !          1145:                if (error)      /* interrupted */
        !          1146:                        goto out;
        !          1147:        }
        !          1148:        eeprom_busy = 1;
        !          1149:  out:
        !          1150:        return (error);
        !          1151: }
        !          1152:
        !          1153: /* Give a lock on the eeprom away. */
        !          1154: void
        !          1155: eeprom_give()
        !          1156: {
        !          1157:
        !          1158:        eeprom_busy = 0;
        !          1159:        if (eeprom_wanted) {
        !          1160:                eeprom_wanted = 0;
        !          1161:                wakeup(&eeprom_busy);
        !          1162:        }
        !          1163: }
        !          1164: #endif /* SUN4 */

CVSweb