|
|
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 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.