Annotation of XNU/osfmk/i386/rtclock.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
        !             3:  *
        !             4:  * @APPLE_LICENSE_HEADER_START@
        !             5:  * 
        !             6:  * The contents of this file constitute Original Code as defined in and
        !             7:  * are subject to the Apple Public Source License Version 1.1 (the
        !             8:  * "License").  You may not use this file except in compliance with the
        !             9:  * License.  Please obtain a copy of the License at
        !            10:  * http://www.apple.com/publicsource and read it before using this file.
        !            11:  * 
        !            12:  * This Original Code and all software distributed under the License are
        !            13:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
        !            14:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
        !            15:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
        !            16:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
        !            17:  * License for the specific language governing rights and limitations
        !            18:  * under the License.
        !            19:  * 
        !            20:  * @APPLE_LICENSE_HEADER_END@
        !            21:  */
        !            22: /*
        !            23:  * @OSF_COPYRIGHT@
        !            24:  */
        !            25: 
        !            26: /*
        !            27:  *     File:           i386/rtclock.c
        !            28:  *     Purpose:        Routines for handling the machine dependent
        !            29:  *                     real-time clock. This clock is generated by
        !            30:  *                     the Intel 8254 Programmable Interval Timer.
        !            31:  */
        !            32: 
        !            33: #include <cpus.h>
        !            34: #include <platforms.h>
        !            35: #include <mp_v1_1.h>
        !            36: #include <mach_kdb.h>
        !            37: #include <kern/cpu_number.h>
        !            38: #include <kern/cpu_data.h>
        !            39: #include <kern/clock.h>
        !            40: #include <kern/macro_help.h>
        !            41: #include <kern/misc_protos.h>
        !            42: #include <kern/spl.h>
        !            43: #include <machine/mach_param.h>        /* HZ */
        !            44: #include <mach/vm_prot.h>
        !            45: #include <vm/pmap.h>
        !            46: #include <vm/vm_kern.h>                /* for kernel_map */
        !            47: #include <i386/ipl.h>
        !            48: #include <i386/pit.h>
        !            49: #include <i386/pio.h>
        !            50: #include <i386/misc_protos.h>
        !            51: #include <i386/rtclock_entries.h>
        !            52: #include <i386/hardclock_entries.h>
        !            53: 
        !            54: int            sysclk_config(void);
        !            55: 
        !            56: int            sysclk_init(void);
        !            57: 
        !            58: kern_return_t  sysclk_gettime(
        !            59:        mach_timespec_t                 *cur_time);
        !            60: 
        !            61: kern_return_t  sysclk_getattr(
        !            62:        clock_flavor_t                  flavor,
        !            63:        clock_attr_t                    attr,
        !            64:        mach_msg_type_number_t  *count);
        !            65: 
        !            66: kern_return_t  sysclk_setattr(
        !            67:        clock_flavor_t                  flavor,
        !            68:        clock_attr_t                    attr,
        !            69:        mach_msg_type_number_t  count);
        !            70: 
        !            71: void           sysclk_setalarm(
        !            72:        mach_timespec_t                 *alarm_time);
        !            73: 
        !            74: extern void (*IOKitRegisterInterruptHook)(void *,  int irq, int isclock);
        !            75: 
        !            76: /*
        !            77:  * Lists of clock routines.
        !            78:  */
        !            79: struct clock_ops  sysclk_ops = {
        !            80:        sysclk_config,                  sysclk_init,
        !            81:        sysclk_gettime,                 0,
        !            82:        sysclk_getattr,                 sysclk_setattr,
        !            83:        sysclk_setalarm,
        !            84: };
        !            85: 
        !            86: int            calend_config(void);
        !            87: 
        !            88: int            calend_init(void);
        !            89: 
        !            90: kern_return_t  calend_gettime(
        !            91:        mach_timespec_t                 *cur_time);
        !            92: 
        !            93: kern_return_t  calend_settime(
        !            94:        mach_timespec_t                 *cur_time);
        !            95: 
        !            96: kern_return_t  calend_getattr(
        !            97:        clock_flavor_t                  flavor,
        !            98:        clock_attr_t                    attr,
        !            99:        mach_msg_type_number_t  *count);
        !           100: 
        !           101: struct clock_ops calend_ops = {
        !           102:        calend_config,                  calend_init,
        !           103:        calend_gettime,                 calend_settime,
        !           104:        calend_getattr,                 0,
        !           105:        0,
        !           106: };
        !           107: 
        !           108: /* local data declarations */
        !           109: mach_timespec_t                *RtcTime = (mach_timespec_t *)0;
        !           110: mach_timespec_t                *RtcAlrm;
        !           111: clock_res_t                    RtcDelt;
        !           112: 
        !           113: /* global data declarations */
        !           114: struct {
        !           115:        AbsoluteTime            abstime;
        !           116: 
        !           117:        mach_timespec_t         time;
        !           118:        mach_timespec_t         alarm_time;     /* time of next alarm */
        !           119: 
        !           120:        mach_timespec_t         calend_offset;
        !           121:        boolean_t                       calend_is_set;
        !           122: 
        !           123:        AbsoluteTime            timer_deadline;
        !           124:        boolean_t                       timer_is_set;
        !           125:        clock_timer_func_t      timer_expire;
        !           126: 
        !           127:        clock_res_t                     new_ires;       /* pending new resolution (nano ) */
        !           128:        clock_res_t                     intr_nsec;      /* interrupt resolution (nano) */
        !           129: 
        !           130:        decl_simple_lock_data(,lock)    /* real-time clock device lock */
        !           131: } rtclock;
        !           132: 
        !           133: unsigned int           clknum;                        /* clks per second */
        !           134: unsigned int           new_clknum;                    /* pending clknum */
        !           135: unsigned int           time_per_clk;                  /* time per clk in ZHZ */
        !           136: unsigned int           clks_per_int;                  /* clks per interrupt */
        !           137: unsigned int           clks_per_int_99;
        !           138: int                                    rtc_intr_count;                /* interrupt counter */
        !           139: int                                    rtc_intr_hertz;                /* interrupts per HZ */
        !           140: int                                    rtc_intr_freq;                 /* interrupt frequency */
        !           141: int                                    rtc_print_lost_tick;           /* print lost tick */
        !           142: 
        !           143: /*
        !           144:  *     Macros to lock/unlock real-time clock device.
        !           145:  */
        !           146: #define LOCK_RTC(s)                                    \
        !           147: MACRO_BEGIN                                                    \
        !           148:        (s) = splclock();                               \
        !           149:        simple_lock(&rtclock.lock);             \
        !           150: MACRO_END
        !           151: 
        !           152: #define UNLOCK_RTC(s)                          \
        !           153: MACRO_BEGIN                                                    \
        !           154:        simple_unlock(&rtclock.lock);   \
        !           155:        splx(s);                                                \
        !           156: MACRO_END
        !           157: 
        !           158: /*
        !           159:  * i8254 control.  ** MONUMENT **
        !           160:  *
        !           161:  * The i8254 is a traditional PC device with some arbitrary characteristics.
        !           162:  * Basically, it is a register that counts at a fixed rate and can be
        !           163:  * programmed to generate an interrupt every N counts.  The count rate is
        !           164:  * clknum counts per second (see pit.h), historically 1193167 we believe.
        !           165:  * Various constants are computed based on this value, and we calculate
        !           166:  * them at init time for execution efficiency.  To obtain sufficient
        !           167:  * accuracy, some of the calculation are most easily done in floating
        !           168:  * point and then converted to int.
        !           169:  *
        !           170:  * We want an interrupt every 10 milliseconds, approximately.  The count
        !           171:  * which will do that is clks_per_int.  However, that many counts is not
        !           172:  * *exactly* 10 milliseconds; it is a bit more or less depending on
        !           173:  * roundoff.  The actual time per tick is calculated and saved in
        !           174:  * rtclock.intr_nsec, and it is that value which is added to the time
        !           175:  * register on each tick.
        !           176:  *
        !           177:  * The i8254 counter can be read between interrupts in order to determine
        !           178:  * the time more accurately.  The counter counts down from the preset value
        !           179:  * toward 0, and we have to handle the case where the counter has been
        !           180:  * reset just before being read and before the interrupt has been serviced.
        !           181:  * Given a count since the last interrupt, the time since then is given
        !           182:  * by (count * time_per_clk).  In order to minimize integer truncation,
        !           183:  * we perform this calculation in an arbitrary unit of time which maintains
        !           184:  * the maximum precision, i.e. such that one tick is 1.0e9 of these units,
        !           185:  * or close to the precision of a 32-bit int.  We then divide by this unit
        !           186:  * (which doesn't lose precision) to get nanoseconds.  For notation
        !           187:  * purposes, this unit is defined as ZHZ = zanoseconds per nanosecond.
        !           188:  *
        !           189:  * This sequence to do all this is in sysclk_gettime.  For efficiency, this
        !           190:  * sequence also needs the value that the counter will have if it has just
        !           191:  * overflowed, so we precompute that also.  ALSO, certain platforms
        !           192:  * (specifically the DEC XL5100) have been observed to have problem
        !           193:  * with latching the counter, and they occasionally (say, one out of
        !           194:  * 100,000 times) return a bogus value.  Hence, the present code reads
        !           195:  * the counter twice and checks for a consistent pair of values.
        !           196:  *
        !           197:  * Some attributes of the rt clock can be changed, including the
        !           198:  * interrupt resolution.  We default to the minimum resolution (10 ms),
        !           199:  * but allow a finer resolution to be requested.  The assumed frequency
        !           200:  * of the clock can also be set since it appears that the actual
        !           201:  * frequency of real-world hardware can vary from the nominal by
        !           202:  * 200 ppm or more.  When the frequency is set, the values above are
        !           203:  * recomputed and we continue without resetting or changing anything else.
        !           204:  */
        !           205: #define RTC_MINRES     (NSEC_PER_SEC / HZ)     /* nsec per tick */
        !           206: #define        RTC_MAXRES      (RTC_MINRES / 20)       /* nsec per tick */
        !           207: #define        ZANO            (1000000000)
        !           208: #define ZHZ             (ZANO / (NSEC_PER_SEC / HZ))
        !           209: #define READ_8254(val) { \
        !           210:         outb(PITCTL_PORT, PIT_C0);             \
        !           211:        (val) = inb(PITCTR0_PORT);               \
        !           212:        (val) |= inb(PITCTR0_PORT) << 8 ; }
        !           213: 
        !           214: /*
        !           215:  * Calibration delay counts.
        !           216:  */
        !           217: unsigned int   delaycount = 10;
        !           218: unsigned int   microdata = 50;
        !           219: 
        !           220: /*
        !           221:  * Forward decl.
        !           222:  */
        !           223: 
        !           224: extern int   measure_delay(int us);
        !           225: void         rtc_setvals( unsigned int, clock_res_t );
        !           226: 
        !           227: /*
        !           228:  * Initialize non-zero clock structure values.
        !           229:  */
        !           230: void
        !           231: rtc_setvals(
        !           232:        unsigned int new_clknum,
        !           233:        clock_res_t  new_ires
        !           234:        )
        !           235: {
        !           236:     unsigned int timeperclk;
        !           237:     unsigned int scale0;
        !           238:     unsigned int scale1;
        !           239:     unsigned int res;
        !           240: 
        !           241:     clknum = new_clknum;
        !           242:     rtc_intr_freq = (NSEC_PER_SEC / new_ires);
        !           243:     rtc_intr_hertz = rtc_intr_freq / HZ;
        !           244:     clks_per_int = (clknum + (rtc_intr_freq / 2)) / rtc_intr_freq;
        !           245:     clks_per_int_99 = clks_per_int - clks_per_int/100;
        !           246: 
        !           247:     /*
        !           248:      * The following calculations are done with scaling integer operations
        !           249:      * in order that the integer results are accurate to the lsb.
        !           250:      */
        !           251:     timeperclk = div_scale(ZANO, clknum, &scale0);     /* 838.105647 nsec */
        !           252: 
        !           253:     time_per_clk = mul_scale(ZHZ, timeperclk, &scale1);        /* 83810 */
        !           254:     if (scale0 > scale1)
        !           255:        time_per_clk >>= (scale0 - scale1);
        !           256:     else if (scale0 < scale1)
        !           257:        panic("rtc_clock: time_per_clk overflow\n");
        !           258: 
        !           259:     /*
        !           260:      * Notice that rtclock.intr_nsec is signed ==> use unsigned int res
        !           261:      */
        !           262:     res = mul_scale(clks_per_int, timeperclk, &scale1);        /* 10000276 */
        !           263:     if (scale0 > scale1)
        !           264:        rtclock.intr_nsec = res >> (scale0 - scale1);
        !           265:     else
        !           266:        panic("rtc_clock: rtclock.intr_nsec overflow\n");
        !           267: 
        !           268:     rtc_intr_count = 1;
        !           269:     RtcDelt = rtclock.intr_nsec/2;
        !           270: }
        !           271: 
        !           272: /*
        !           273:  * Configure the real-time clock device. Return success (1)
        !           274:  * or failure (0).
        !           275:  */
        !           276: 
        !           277: int
        !           278: sysclk_config(void)
        !           279: {
        !           280:        int     RtcFlag;
        !           281:        int     pic;
        !           282: 
        !           283: #if    NCPUS > 1
        !           284:        mp_disable_preemption();
        !           285:        if (cpu_number() != master_cpu) {
        !           286:                mp_enable_preemption();
        !           287:                return(1);
        !           288:        }
        !           289:        mp_enable_preemption();
        !           290: #endif
        !           291:        /*
        !           292:         * Setup device.
        !           293:         */
        !           294: #if    MP_V1_1
        !           295:     {
        !           296:        extern boolean_t mp_v1_1_initialized;
        !           297:        if (mp_v1_1_initialized)
        !           298:            pic = 2;
        !           299:        else
        !           300:            pic = 0;
        !           301:     }
        !           302: #else
        !           303:        pic = 0;        /* FIXME .. interrupt registration moved to AppleIntelClock */
        !           304: #endif
        !           305: 
        !           306: 
        !           307:        /*
        !           308:         * We should attempt to test the real-time clock
        !           309:         * device here. If it were to fail, we should panic
        !           310:         * the system.
        !           311:         */
        !           312:        RtcFlag = /* test device */1;
        !           313:        printf("realtime clock configured\n");
        !           314: 
        !           315:        simple_lock_init(&rtclock.lock, ETAP_NO_TRACE);
        !           316:        return (RtcFlag);
        !           317: }
        !           318: 
        !           319: /*
        !           320:  * Initialize the real-time clock device. Return success (1)
        !           321:  * or failure (0). Since the real-time clock is required to
        !           322:  * provide canonical mapped time, we allocate a page to keep
        !           323:  * the clock time value. In addition, various variables used
        !           324:  * to support the clock are initialized.  Note: the clock is
        !           325:  * not started until rtclock_reset is called.
        !           326:  */
        !           327: int
        !           328: sysclk_init(void)
        !           329: {
        !           330:        vm_offset_t     *vp;
        !           331: #if    NCPUS > 1
        !           332:        mp_disable_preemption();
        !           333:        if (cpu_number() != master_cpu) {
        !           334:                mp_enable_preemption();
        !           335:                return(1);
        !           336:        }
        !           337:        mp_enable_preemption();
        !           338: #endif
        !           339: 
        !           340:        RtcTime = &rtclock.time;
        !           341:        rtc_setvals( CLKNUM, RTC_MINRES );  /* compute constants */
        !           342:        return (1);
        !           343: }
        !           344: 
        !           345: static volatile unsigned int     last_ival = 0;
        !           346: 
        !           347: /*
        !           348:  * Get the clock device time. This routine is responsible
        !           349:  * for converting the device's machine dependent time value
        !           350:  * into a canonical mach_timespec_t value.
        !           351:  */
        !           352: kern_return_t
        !           353: sysclk_gettime(
        !           354:        mach_timespec_t *cur_time)      /* OUT */
        !           355: {
        !           356:         mach_timespec_t        itime = {0, 0};
        !           357:        unsigned int    val, val2;
        !           358:        int             s;
        !           359: 
        !           360:        if (!RtcTime) {
        !           361:                /* Uninitialized */
        !           362:                cur_time->tv_nsec = 0;
        !           363:                cur_time->tv_sec = 0;
        !           364:                return (KERN_SUCCESS);
        !           365:        }
        !           366: 
        !           367:        /*
        !           368:         * Inhibit interrupts. Determine the incremental
        !           369:         * time since the last interrupt. (This could be
        !           370:         * done in assembler for a bit more speed).
        !           371:         */
        !           372:        LOCK_RTC(s);
        !           373:        do {
        !           374:            READ_8254(val);                 /* read clock */
        !           375:            READ_8254(val2);                /* read clock */
        !           376:        } while ( val2 > val || val2 < val - 10 );
        !           377:        if ( val > clks_per_int_99 ) {
        !           378:            outb( 0x0a, 0x20 );             /* see if interrupt pending */
        !           379:            if ( inb( 0x20 ) & 1 )
        !           380:                itime.tv_nsec = rtclock.intr_nsec; /* yes, add a tick */
        !           381:        }
        !           382:        itime.tv_nsec += ((clks_per_int - val) * time_per_clk) / ZHZ;
        !           383:        if ( itime.tv_nsec < last_ival ) {
        !           384:            if (rtc_print_lost_tick)
        !           385:                printf( "rtclock: missed clock interrupt.\n" );
        !           386:        }
        !           387:        last_ival = itime.tv_nsec;
        !           388:        cur_time->tv_sec = rtclock.time.tv_sec;
        !           389:        cur_time->tv_nsec = rtclock.time.tv_nsec;
        !           390:        UNLOCK_RTC(s);
        !           391:        ADD_MACH_TIMESPEC(cur_time, ((mach_timespec_t *)&itime));
        !           392:        return (KERN_SUCCESS);
        !           393: }
        !           394: 
        !           395: kern_return_t
        !           396: sysclk_gettime_internal(
        !           397:        mach_timespec_t *cur_time)      /* OUT */
        !           398: {
        !           399:         mach_timespec_t        itime = {0, 0};
        !           400:        unsigned int    val, val2;
        !           401: 
        !           402:        if (!RtcTime) {
        !           403:                /* Uninitialized */
        !           404:                cur_time->tv_nsec = 0;
        !           405:                cur_time->tv_sec = 0;
        !           406:                return (KERN_SUCCESS);
        !           407:        }
        !           408: 
        !           409:        /*
        !           410:         * Inhibit interrupts. Determine the incremental
        !           411:         * time since the last interrupt. (This could be
        !           412:         * done in assembler for a bit more speed).
        !           413:         */
        !           414:        do {
        !           415:            READ_8254(val);                 /* read clock */
        !           416:            READ_8254(val2);                /* read clock */
        !           417:        } while ( val2 > val || val2 < val - 10 );
        !           418:        if ( val > clks_per_int_99 ) {
        !           419:            outb( 0x0a, 0x20 );             /* see if interrupt pending */
        !           420:            if ( inb( 0x20 ) & 1 )
        !           421:                itime.tv_nsec = rtclock.intr_nsec; /* yes, add a tick */
        !           422:        }
        !           423:        itime.tv_nsec += ((clks_per_int - val) * time_per_clk) / ZHZ;
        !           424:        if ( itime.tv_nsec < last_ival ) {
        !           425:            if (rtc_print_lost_tick)
        !           426:                printf( "rtclock: missed clock interrupt.\n" );
        !           427:        }
        !           428:        last_ival = itime.tv_nsec;
        !           429:        cur_time->tv_sec = rtclock.time.tv_sec;
        !           430:        cur_time->tv_nsec = rtclock.time.tv_nsec;
        !           431:        ADD_MACH_TIMESPEC(cur_time, ((mach_timespec_t *)&itime));
        !           432:        return (KERN_SUCCESS);
        !           433: }
        !           434: 
        !           435: /*
        !           436:  * Get the clock device time when ALL interrupts are already disabled.
        !           437:  * Same as above except for turning interrupts off and on.
        !           438:  * This routine is responsible for converting the device's machine dependent
        !           439:  * time value into a canonical mach_timespec_t value.
        !           440:  */
        !           441: void
        !           442: sysclk_gettime_interrupts_disabled(
        !           443:        mach_timespec_t *cur_time)      /* OUT */
        !           444: {
        !           445:        mach_timespec_t itime = {0, 0};
        !           446:        unsigned int    val;
        !           447: 
        !           448:        if (!RtcTime) {
        !           449:                /* Uninitialized */
        !           450:                cur_time->tv_nsec = 0;
        !           451:                cur_time->tv_sec = 0;
        !           452:                return;
        !           453:        }
        !           454: 
        !           455:        simple_lock(&rtclock.lock);
        !           456: 
        !           457:        /*
        !           458:         * Copy the current time knowing that we cant be interrupted
        !           459:         * between the two longwords and so dont need to use MTS_TO_TS
        !           460:         */
        !           461:        READ_8254(val);                     /* read clock */
        !           462:        if ( val > clks_per_int_99 ) {
        !           463:            outb( 0x0a, 0x20 );             /* see if interrupt pending */
        !           464:            if ( inb( 0x20 ) & 1 )
        !           465:                itime.tv_nsec = rtclock.intr_nsec; /* yes, add a tick */
        !           466:        }
        !           467:        itime.tv_nsec += ((clks_per_int - val) * time_per_clk) / ZHZ;
        !           468:        if ( itime.tv_nsec < last_ival ) {
        !           469:            if (rtc_print_lost_tick)
        !           470:                printf( "rtclock: missed clock interrupt.\n" );
        !           471:        }
        !           472:        last_ival = itime.tv_nsec;
        !           473:        cur_time->tv_sec = rtclock.time.tv_sec;
        !           474:        cur_time->tv_nsec = rtclock.time.tv_nsec;
        !           475:        ADD_MACH_TIMESPEC(cur_time, ((mach_timespec_t *)&itime));
        !           476: 
        !           477:        simple_unlock(&rtclock.lock);
        !           478: }
        !           479: 
        !           480: static
        !           481: natural_t
        !           482: get_uptime_ticks(void)
        !           483: {
        !           484:        natural_t               result = 0;
        !           485:        unsigned int    val, val2;
        !           486: 
        !           487:        if (!RtcTime)
        !           488:                return (result);
        !           489: 
        !           490:        /*
        !           491:         * Inhibit interrupts. Determine the incremental
        !           492:         * time since the last interrupt. (This could be
        !           493:         * done in assembler for a bit more speed).
        !           494:         */
        !           495:        do {
        !           496:            READ_8254(val);                 /* read clock */
        !           497:            READ_8254(val2);                /* read clock */
        !           498:        } while (val2 > val || val2 < val - 10);
        !           499:        if (val > clks_per_int_99) {
        !           500:            outb(0x0a, 0x20);                           /* see if interrupt pending */
        !           501:            if (inb(0x20) & 1)
        !           502:                        result = rtclock.intr_nsec;     /* yes, add a tick */
        !           503:        }
        !           504:        result += ((clks_per_int - val) * time_per_clk) / ZHZ;
        !           505:        if (result < last_ival) {
        !           506:            if (rtc_print_lost_tick)
        !           507:                        printf( "rtclock: missed clock interrupt.\n" );
        !           508:        }
        !           509: 
        !           510:        return (result);
        !           511: }
        !           512: 
        !           513: /*
        !           514:  * Get clock device attributes.
        !           515:  */
        !           516: kern_return_t
        !           517: sysclk_getattr(
        !           518:        clock_flavor_t          flavor,
        !           519:        clock_attr_t            attr,           /* OUT */
        !           520:        mach_msg_type_number_t  *count)         /* IN/OUT */
        !           521: {
        !           522:        spl_t   s;
        !           523: 
        !           524:        if (*count != 1)
        !           525:                return (KERN_FAILURE);
        !           526:        switch (flavor) {
        !           527: 
        !           528:        case CLOCK_GET_TIME_RES:        /* >0 res */
        !           529: #if    (NCPUS == 1 || (MP_V1_1 && 0))
        !           530:                LOCK_RTC(s);
        !           531:                *(clock_res_t *) attr = 1000;
        !           532:                UNLOCK_RTC(s);
        !           533:                break;
        !           534: #endif /* (NCPUS == 1 || (MP_V1_1 && 0)) && AT386 */
        !           535:        case CLOCK_ALARM_CURRES:        /* =0 no alarm */
        !           536:                LOCK_RTC(s);
        !           537:                *(clock_res_t *) attr = rtclock.intr_nsec;
        !           538:                UNLOCK_RTC(s);
        !           539:                break;
        !           540: 
        !           541:        case CLOCK_ALARM_MAXRES:
        !           542:                *(clock_res_t *) attr = RTC_MAXRES;
        !           543:                break;
        !           544: 
        !           545:        case CLOCK_ALARM_MINRES:
        !           546:                *(clock_res_t *) attr = RTC_MINRES;
        !           547:                break;
        !           548: 
        !           549:        default:
        !           550:                return (KERN_INVALID_VALUE);
        !           551:        }
        !           552:        return (KERN_SUCCESS);
        !           553: }
        !           554: 
        !           555: /*
        !           556:  * Set clock device attributes.
        !           557:  */
        !           558: kern_return_t
        !           559: sysclk_setattr(
        !           560:        clock_flavor_t          flavor,
        !           561:        clock_attr_t            attr,           /* IN */
        !           562:        mach_msg_type_number_t  count)          /* IN */
        !           563: {
        !           564:        spl_t           s;
        !           565:        int             freq;
        !           566:        int             adj;
        !           567:        clock_res_t     new_ires;
        !           568: 
        !           569:        if (count != 1)
        !           570:                return (KERN_FAILURE);
        !           571:        switch (flavor) {
        !           572: 
        !           573:        case CLOCK_GET_TIME_RES:
        !           574:        case CLOCK_ALARM_MAXRES:
        !           575:        case CLOCK_ALARM_MINRES:
        !           576:                return (KERN_FAILURE);
        !           577: 
        !           578:        case CLOCK_ALARM_CURRES:
        !           579:                new_ires = *(clock_res_t *) attr;
        !           580: 
        !           581:                /*
        !           582:                 * The new resolution must be within the predetermined
        !           583:                 * range.  If the desired resolution cannot be achieved
        !           584:                 * to within 0.1%, an error is returned.
        !           585:                 */
        !           586:                if (new_ires < RTC_MAXRES || new_ires > RTC_MINRES)
        !           587:                        return (KERN_INVALID_VALUE);
        !           588:                freq = (NSEC_PER_SEC / new_ires);
        !           589:                adj = (((clknum % freq) * new_ires) / clknum);
        !           590:                if (adj > (new_ires / 1000))
        !           591:                        return (KERN_INVALID_VALUE);
        !           592:                /*
        !           593:                 * Record the new alarm resolution which will take effect
        !           594:                 * on the next HZ aligned clock tick.
        !           595:                 */
        !           596:                LOCK_RTC(s);
        !           597:                if ( freq != rtc_intr_freq ) {
        !           598:                    rtclock.new_ires = new_ires;
        !           599:                    new_clknum = clknum;
        !           600:                }
        !           601:                UNLOCK_RTC(s);
        !           602:                return (KERN_SUCCESS);
        !           603: 
        !           604:        default:
        !           605:                return (KERN_INVALID_VALUE);
        !           606:        }
        !           607: }
        !           608: 
        !           609: /*
        !           610:  * Set next alarm time for the clock device. This call
        !           611:  * always resets the time to deliver an alarm for the
        !           612:  * clock.
        !           613:  */
        !           614: void
        !           615: sysclk_setalarm(
        !           616:        mach_timespec_t *alarm_time)
        !           617: {
        !           618:        spl_t           s;
        !           619: 
        !           620:        LOCK_RTC(s);
        !           621:        rtclock.alarm_time = *alarm_time;
        !           622:        RtcAlrm = &rtclock.alarm_time;
        !           623:        UNLOCK_RTC(s);
        !           624: }
        !           625: 
        !           626: /*
        !           627:  * Configure the calendar clock.
        !           628:  */
        !           629: int
        !           630: calend_config(void)
        !           631: {
        !           632:        return bbc_config();
        !           633: }
        !           634: 
        !           635: /*
        !           636:  * Initialize calendar clock.
        !           637:  */
        !           638: int
        !           639: calend_init(void)
        !           640: {
        !           641:        return (1);
        !           642: }
        !           643: 
        !           644: /*
        !           645:  * Get the current clock time.
        !           646:  */
        !           647: kern_return_t
        !           648: calend_gettime(
        !           649:        mach_timespec_t *cur_time)      /* OUT */
        !           650: {
        !           651:        spl_t           s;
        !           652: 
        !           653:        LOCK_RTC(s);
        !           654:        if (!rtclock.calend_is_set) {
        !           655:                UNLOCK_RTC(s);
        !           656:                return (KERN_FAILURE);
        !           657:        }
        !           658: 
        !           659:        (void) sysclk_gettime_internal(cur_time);
        !           660:        ADD_MACH_TIMESPEC(cur_time, &rtclock.calend_offset);
        !           661:        UNLOCK_RTC(s);
        !           662: 
        !           663:        return (KERN_SUCCESS);
        !           664: }
        !           665: 
        !           666: /*
        !           667:  * Set the current clock time.
        !           668:  */
        !           669: kern_return_t
        !           670: calend_settime(
        !           671:        mach_timespec_t *new_time)
        !           672: {
        !           673:        mach_timespec_t curr_time;
        !           674:        spl_t           s;
        !           675: 
        !           676:        LOCK_RTC(s);
        !           677:        (void) sysclk_gettime_internal(&curr_time);
        !           678:        rtclock.calend_offset = *new_time;
        !           679:        SUB_MACH_TIMESPEC(&rtclock.calend_offset, &curr_time);
        !           680:        rtclock.calend_is_set = TRUE;
        !           681:        UNLOCK_RTC(s);
        !           682: 
        !           683:        (void) bbc_settime(new_time);
        !           684: 
        !           685:        return (KERN_SUCCESS);
        !           686: }
        !           687: 
        !           688: /*
        !           689:  * Get clock device attributes.
        !           690:  */
        !           691: kern_return_t
        !           692: calend_getattr(
        !           693:        clock_flavor_t          flavor,
        !           694:        clock_attr_t            attr,           /* OUT */
        !           695:        mach_msg_type_number_t  *count)         /* IN/OUT */
        !           696: {
        !           697:        spl_t   s;
        !           698: 
        !           699:        if (*count != 1)
        !           700:                return (KERN_FAILURE);
        !           701:        switch (flavor) {
        !           702: 
        !           703:        case CLOCK_GET_TIME_RES:        /* >0 res */
        !           704: #if    (NCPUS == 1 || (MP_V1_1 && 0))
        !           705:                LOCK_RTC(s);
        !           706:                *(clock_res_t *) attr = 1000;
        !           707:                UNLOCK_RTC(s);
        !           708:                break;
        !           709: #else  /* (NCPUS == 1 || (MP_V1_1 && 0)) && AT386 */
        !           710:                LOCK_RTC(s);
        !           711:                *(clock_res_t *) attr = rtclock.intr_nsec;
        !           712:                UNLOCK_RTC(s);
        !           713:                break;
        !           714: #endif /* (NCPUS == 1 || (MP_V1_1 && 0)) && AT386 */
        !           715: 
        !           716:        case CLOCK_ALARM_CURRES:        /* =0 no alarm */
        !           717:        case CLOCK_ALARM_MINRES:
        !           718:        case CLOCK_ALARM_MAXRES:
        !           719:                *(clock_res_t *) attr = 0;
        !           720:                break;
        !           721: 
        !           722:        default:
        !           723:                return (KERN_INVALID_VALUE);
        !           724:        }
        !           725:        return (KERN_SUCCESS);
        !           726: }
        !           727: 
        !           728: void
        !           729: clock_adjust_calendar(
        !           730:        clock_res_t     nsec)
        !           731: {
        !           732:        spl_t           s;
        !           733: 
        !           734:        LOCK_RTC(s);
        !           735:        if (rtclock.calend_is_set)
        !           736:                ADD_MACH_TIMESPEC_NSEC(&rtclock.calend_offset, nsec);
        !           737:        UNLOCK_RTC(s);
        !           738: }
        !           739: 
        !           740: void
        !           741: clock_initialize_calendar(void)
        !           742: {
        !           743:        mach_timespec_t bbc_time, curr_time;
        !           744:        spl_t           s;
        !           745: 
        !           746:        if (bbc_gettime(&bbc_time) != KERN_SUCCESS)
        !           747:                return;
        !           748: 
        !           749:        LOCK_RTC(s);
        !           750:        if (!rtclock.calend_is_set) {
        !           751:                (void) sysclk_gettime_internal(&curr_time);
        !           752:                rtclock.calend_offset = bbc_time;
        !           753:                SUB_MACH_TIMESPEC(&rtclock.calend_offset, &curr_time);
        !           754:                rtclock.calend_is_set = TRUE;
        !           755:        }
        !           756:        UNLOCK_RTC(s);
        !           757: }
        !           758: 
        !           759: mach_timespec_t
        !           760: clock_get_calendar_offset(void)
        !           761: {
        !           762:        mach_timespec_t result = MACH_TIMESPEC_ZERO;
        !           763:        spl_t           s;
        !           764: 
        !           765:        LOCK_RTC(s);
        !           766:        if (rtclock.calend_is_set)
        !           767:                result = rtclock.calend_offset;
        !           768:        UNLOCK_RTC(s);
        !           769: 
        !           770:        return (result);
        !           771: }
        !           772: 
        !           773: void
        !           774: clock_get_timebase_info(
        !           775:        natural_t               *delta,
        !           776:        natural_t               *abs_to_ns_num,
        !           777:        natural_t               *abs_to_ns_denom,
        !           778:        natural_t               *proc_to_abs_num,
        !           779:        natural_t               *proc_to_abs_denom)
        !           780: {
        !           781:        spl_t   s;
        !           782: 
        !           783:        LOCK_RTC(s);
        !           784:        *abs_to_ns_num = *abs_to_ns_denom =     1;
        !           785:        UNLOCK_RTC(s);
        !           786: 
        !           787:        *delta = 1;
        !           788:        *proc_to_abs_num = *proc_to_abs_denom = 1;
        !           789: }      
        !           790: 
        !           791: void
        !           792: clock_set_timer_deadline(
        !           793:        AbsoluteTime                    deadline)
        !           794: {
        !           795:        spl_t                   s;
        !           796: 
        !           797:        LOCK_RTC(s);
        !           798:        rtclock.timer_deadline = deadline;
        !           799:        rtclock.timer_is_set = TRUE;
        !           800:        UNLOCK_RTC(s);
        !           801: }
        !           802: 
        !           803: void
        !           804: clock_set_timer_func(
        !           805:        clock_timer_func_t              func)
        !           806: {
        !           807:        spl_t           s;
        !           808: 
        !           809:        LOCK_RTC(s);
        !           810:        if (rtclock.timer_expire == NULL)
        !           811:                rtclock.timer_expire = func;
        !           812:        UNLOCK_RTC(s);
        !           813: }
        !           814: 
        !           815: 
        !           816: 
        !           817: /*
        !           818:  * Load the count register and start the clock.
        !           819:  */
        !           820: #define RTCLOCK_RESET()        {                                       \
        !           821:        outb(PITCTL_PORT, PIT_C0|PIT_NDIVMODE|PIT_READMODE);    \
        !           822:        outb(PITCTR0_PORT, (clks_per_int & 0xff));              \
        !           823:        outb(PITCTR0_PORT, (clks_per_int >> 8));                \
        !           824: }
        !           825: 
        !           826: /*
        !           827:  * Reset the clock device. This causes the realtime clock
        !           828:  * device to reload its mode and count value (frequency).
        !           829:  * Note: the CPU should be calibrated
        !           830:  * before starting the clock for the first time.
        !           831:  */
        !           832: 
        !           833: void
        !           834: rtclock_reset(void)
        !           835: {
        !           836:        int             s;
        !           837: 
        !           838: #if    NCPUS > 1 && !(MP_V1_1 && 0)
        !           839:        mp_disable_preemption();
        !           840:        if (cpu_number() != master_cpu) {
        !           841:                mp_enable_preemption();
        !           842:                return;
        !           843:        }
        !           844:        mp_enable_preemption();
        !           845: #endif /* NCPUS > 1 && AT386 && !MP_V1_1 */
        !           846:        LOCK_RTC(s);
        !           847:        RTCLOCK_RESET();
        !           848:        UNLOCK_RTC(s);
        !           849: }
        !           850: 
        !           851: /*
        !           852:  * Real-time clock device interrupt. Called only on the
        !           853:  * master processor. Updates the clock time and upcalls
        !           854:  * into the higher level clock code to deliver alarms.
        !           855:  */
        !           856: int
        !           857: rtclock_intr(void)
        !           858: {
        !           859:        AbsoluteTime    abstime;
        !           860:        mach_timespec_t clock_time;
        !           861:        int                             i;
        !           862:        spl_t                   s;
        !           863: 
        !           864:        /*
        !           865:         * Update clock time. Do the update so that the macro
        !           866:         * MTS_TO_TS() for reading the mapped time works (e.g.
        !           867:         * update in order: mtv_csec, mtv_time.tv_nsec, mtv_time.tv_sec).
        !           868:         */      
        !           869:        LOCK_RTC(s);
        !           870:        i = rtclock.time.tv_nsec + rtclock.intr_nsec;
        !           871:        if (i < NSEC_PER_SEC)
        !           872:            rtclock.time.tv_nsec = i;
        !           873:        else {
        !           874:            rtclock.time.tv_nsec = i - NSEC_PER_SEC;
        !           875:            rtclock.time.tv_sec++;
        !           876:        }
        !           877:        /* note time now up to date */
        !           878:        last_ival = 0;
        !           879: 
        !           880:        ADD_ABSOLUTETIME_TICKS(&rtclock.abstime, NSEC_PER_SEC/HZ);
        !           881:        abstime = rtclock.abstime;
        !           882:        if (rtclock.timer_is_set &&
        !           883:                        CMP_ABSOLUTETIME(&rtclock.timer_deadline, &abstime) <= 0) {
        !           884:                rtclock.timer_is_set = FALSE;
        !           885:                UNLOCK_RTC(s);
        !           886: 
        !           887:                (*rtclock.timer_expire)(abstime);
        !           888: 
        !           889:                LOCK_RTC(s);
        !           890:        }
        !           891: 
        !           892:        /*
        !           893:         * Perform alarm clock processing if needed. The time
        !           894:         * passed up is incremented by a half-interrupt tick
        !           895:         * to trigger alarms closest to their desired times.
        !           896:         * The clock_alarm_intr() routine calls sysclk_setalrm()
        !           897:         * before returning if later alarms are pending.
        !           898:         */
        !           899: 
        !           900:        if (RtcAlrm && (RtcAlrm->tv_sec < RtcTime->tv_sec ||
        !           901:                        (RtcAlrm->tv_sec == RtcTime->tv_sec &&
        !           902:                         RtcDelt >= RtcAlrm->tv_nsec - RtcTime->tv_nsec))) {
        !           903:                clock_time.tv_sec = 0;
        !           904:                clock_time.tv_nsec = RtcDelt;
        !           905:                ADD_MACH_TIMESPEC (&clock_time, RtcTime);
        !           906:                RtcAlrm = 0;
        !           907:                UNLOCK_RTC(s);
        !           908:                /*
        !           909:                 * Call clock_alarm_intr() without RTC-lock.
        !           910:                 * The lock ordering is always CLOCK-lock
        !           911:                 * before RTC-lock.
        !           912:                 */
        !           913:                clock_alarm_intr(SYSTEM_CLOCK, &clock_time);
        !           914:                LOCK_RTC(s);
        !           915:        }
        !           916: 
        !           917:        /*
        !           918:         * On a HZ-tick boundary: return 0 and adjust the clock
        !           919:         * alarm resolution (if requested).  Otherwise return a
        !           920:         * non-zero value.
        !           921:         */
        !           922:        if ((i = --rtc_intr_count) == 0) {
        !           923:            if (rtclock.new_ires) {
        !           924:                        rtc_setvals(new_clknum, rtclock.new_ires);
        !           925:                        RTCLOCK_RESET();            /* lock clock register */
        !           926:                        rtclock.new_ires = 0;
        !           927:            }
        !           928:            rtc_intr_count = rtc_intr_hertz;
        !           929:        }
        !           930:        UNLOCK_RTC(s);
        !           931:        return (i);
        !           932: }
        !           933: 
        !           934: void
        !           935: clock_get_uptime(
        !           936:        AbsoluteTime    *result)
        !           937: {
        !           938:        natural_t               ticks;
        !           939:        spl_t                   s;
        !           940: 
        !           941:        LOCK_RTC(s);
        !           942:        ticks = get_uptime_ticks();
        !           943:        *result = rtclock.abstime;
        !           944:        UNLOCK_RTC(s);
        !           945: 
        !           946:        ADD_ABSOLUTETIME_TICKS(result, ticks);
        !           947: }
        !           948: 
        !           949: void
        !           950: clock_interval_to_deadline(
        !           951:        natural_t                       interval,
        !           952:        natural_t                       scale_factor,
        !           953:        AbsoluteTime            *result)
        !           954: {
        !           955:        AbsoluteTime            abstime;
        !           956: 
        !           957:        clock_get_uptime(result);
        !           958: 
        !           959:        clock_interval_to_absolutetime_interval(interval, scale_factor, &abstime);
        !           960: 
        !           961:        ADD_ABSOLUTETIME(result, &abstime);
        !           962: }
        !           963: 
        !           964: void
        !           965: clock_interval_to_absolutetime_interval(
        !           966:        natural_t                       interval,
        !           967:        natural_t                       scale_factor,
        !           968:        AbsoluteTime            *result)
        !           969: {
        !           970:        AbsoluteTime_to_scalar(result) = (abstime_scalar_t)interval * scale_factor;
        !           971: }
        !           972: 
        !           973: void
        !           974: clock_absolutetime_interval_to_deadline(
        !           975:        AbsoluteTime            abstime,
        !           976:        AbsoluteTime            *result)
        !           977: {
        !           978:        clock_get_uptime(result);
        !           979: 
        !           980:        ADD_ABSOLUTETIME(result, &abstime);
        !           981: }
        !           982: 
        !           983: void
        !           984: absolutetime_to_nanoseconds(
        !           985:        AbsoluteTime            abstime,
        !           986:        UInt64                          *result)
        !           987: {
        !           988:        *result = AbsoluteTime_to_scalar(&abstime);
        !           989: }
        !           990: 
        !           991: void
        !           992: nanoseconds_to_absolutetime(
        !           993:        UInt64                          nanoseconds,
        !           994:        AbsoluteTime            *result)
        !           995: {
        !           996:        AbsoluteTime_to_scalar(result) = nanoseconds;
        !           997: }
        !           998: 
        !           999: /*
        !          1000:  * measure_delay(microseconds)
        !          1001:  *
        !          1002:  * Measure elapsed time for delay calls
        !          1003:  * Returns microseconds.
        !          1004:  * 
        !          1005:  * Microseconds must not be too large since the counter (short) 
        !          1006:  * will roll over.  Max is about 13 ms.  Values smaller than 1 ms are ok.
        !          1007:  * This uses the assumed frequency of the rt clock which is emperically
        !          1008:  * accurate to only about 200 ppm.
        !          1009:  */
        !          1010: 
        !          1011: int
        !          1012: measure_delay(
        !          1013:        int us)
        !          1014: {
        !          1015:        unsigned int    lsb, val;
        !          1016: 
        !          1017:        outb(PITCTL_PORT, PIT_C0|PIT_NDIVMODE|PIT_READMODE);
        !          1018:        outb(PITCTR0_PORT, 0xff);       /* set counter to max value */
        !          1019:        outb(PITCTR0_PORT, 0xff);
        !          1020:        delay(us);
        !          1021:        outb(PITCTL_PORT, PIT_C0);
        !          1022:        lsb = inb(PITCTR0_PORT);
        !          1023:        val = (inb(PITCTR0_PORT) << 8) | lsb;
        !          1024:        val = 0xffff - val;
        !          1025:        val *= 1000000;
        !          1026:        val /= CLKNUM;
        !          1027:        return(val);
        !          1028: }
        !          1029: 
        !          1030: /*
        !          1031:  * calibrate_delay(void)
        !          1032:  *
        !          1033:  * Adjust delaycount.  Called from startup before clock is started
        !          1034:  * for normal interrupt generation.
        !          1035:  */
        !          1036: 
        !          1037: void
        !          1038: calibrate_delay(void)
        !          1039: {
        !          1040:        unsigned        val;
        !          1041:        int             prev = 0;
        !          1042:        register int    i;
        !          1043: 
        !          1044:        printf("adjusting delay count: %d", delaycount);
        !          1045:        for (i=0; i<10; i++) {
        !          1046:                prev = delaycount;
        !          1047:                /* 
        !          1048:                 * microdata must not be to large since measure_timer
        !          1049:                 * will not return accurate values if the counter (short) 
        !          1050:                 * rolls over
        !          1051:                 */
        !          1052:                val = measure_delay(microdata);
        !          1053:                delaycount *= microdata;
        !          1054:                delaycount += val-1;    /* round up to upper us */
        !          1055:                delaycount /= val;
        !          1056:                if (delaycount <= 0)
        !          1057:                        delaycount = 1;
        !          1058:                if (delaycount != prev)
        !          1059:                        printf(" %d", delaycount);
        !          1060:        }
        !          1061:        printf("\n");
        !          1062: }
        !          1063: 
        !          1064: #if    MACH_KDB
        !          1065: void
        !          1066: test_delay(void);
        !          1067: 
        !          1068: void
        !          1069: test_delay(void)
        !          1070: {
        !          1071:        register i;
        !          1072: 
        !          1073:        for (i = 0; i < 10; i++)
        !          1074:                printf("%d, %d\n", i, measure_delay(i));
        !          1075:        for (i = 10; i <= 100; i+=10)
        !          1076:                printf("%d, %d\n", i, measure_delay(i));
        !          1077: }
        !          1078: #endif /* MACH_KDB */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.