-int sysclk_config(void);
-
-int sysclk_init(void);
-
-kern_return_t sysclk_gettime(
- mach_timespec_t *cur_time);
-
-kern_return_t sysclk_getattr(
- clock_flavor_t flavor,
- clock_attr_t attr,
- mach_msg_type_number_t *count);
-
-void sysclk_setalarm(
- mach_timespec_t *alarm_time);
-
-/*
- * Lists of clock routines.
- */
-struct clock_ops sysclk_ops = {
- sysclk_config, sysclk_init,
- sysclk_gettime, 0,
- sysclk_getattr, 0,
- sysclk_setalarm,
-};
-
-int calend_config(void);
-
-int calend_init(void);
-
-kern_return_t calend_gettime(
- mach_timespec_t *cur_time);
-
-kern_return_t calend_getattr(
- clock_flavor_t flavor,
- clock_attr_t attr,
- mach_msg_type_number_t *count);
-
-struct clock_ops calend_ops = {
- calend_config, calend_init,
- calend_gettime, 0,
- calend_getattr, 0,
- 0,
-};
-
-/* local data declarations */
-
-static clock_timer_func_t rtclock_timer_expire;
-
-static timer_call_data_t rtclock_alarm_timer;
-
-static void rtclock_alarm_expire(
- timer_call_param_t p0,
- timer_call_param_t p1);
-
-struct {
- mach_timespec_t calend_offset;
- boolean_t calend_is_set;
-
- int64_t calend_adjtotal;
- int32_t calend_adjdelta;
-
- uint32_t boottime;
-
- mach_timebase_info_data_t timebase_const;
-
- decl_simple_lock_data(,lock) /* real-time clock device lock */
-} rtclock;
-
-boolean_t rtc_initialized = FALSE;
-clock_res_t rtc_intr_nsec = NSEC_PER_HZ; /* interrupt res */
-uint64_t rtc_cycle_count; /* clocks in 1/20th second */
-uint64_t rtc_cyc_per_sec; /* processor cycles per sec */
-uint32_t rtc_boot_frequency; /* provided by 1st speed-step */
-uint32_t rtc_quant_scale; /* clock to nanos multiplier */
-uint32_t rtc_quant_shift; /* clock to nanos right shift */
-uint64_t rtc_decrementer_min;
-
-static mach_timebase_info_data_t rtc_lapic_scale; /* nsec to lapic count */
-
-/*
- * Macros to lock/unlock real-time clock data.
- */
-#define RTC_INTRS_OFF(s) \
- (s) = splclock()
-
-#define RTC_INTRS_ON(s) \
- splx(s)
-
-#define RTC_LOCK(s) \
-MACRO_BEGIN \
- RTC_INTRS_OFF(s); \
- simple_lock(&rtclock.lock); \
-MACRO_END
-
-#define RTC_UNLOCK(s) \
-MACRO_BEGIN \
- simple_unlock(&rtclock.lock); \
- RTC_INTRS_ON(s); \
-MACRO_END
-
-/*
- * i8254 control. ** MONUMENT **
- *
- * The i8254 is a traditional PC device with some arbitrary characteristics.
- * Basically, it is a register that counts at a fixed rate and can be
- * programmed to generate an interrupt every N counts. The count rate is
- * clknum counts per sec (see pit.h), historically 1193167=14.318MHz/12
- * but the more accurate value is 1193182=14.31818MHz/12. [14.31818 MHz being
- * the master crystal oscillator reference frequency since the very first PC.]
- * Various constants are computed based on this value, and we calculate
- * them at init time for execution efficiency. To obtain sufficient
- * accuracy, some of the calculation are most easily done in floating
- * point and then converted to int.
- *
- */
-
-/*
- * Forward decl.
- */
-
-static uint64_t rtc_set_cyc_per_sec(uint64_t cycles);
-uint64_t rtc_nanotime_read(void);
-
-/*
- * create_mul_quant_GHZ
- * create a constant used to multiply the TSC by to convert to nanoseconds.
- * This is a 32 bit number and the TSC *MUST* have a frequency higher than
- * 1000Mhz for this routine to work.
- *
- * The theory here is that we know how many TSCs-per-sec the processor runs at.
- * Normally to convert this to nanoseconds you would multiply the current
- * timestamp by 1000000000 (a billion) then divide by TSCs-per-sec.
- * Unfortunatly the TSC is 64 bits which would leave us with 96 bit intermediate
- * results from the multiply that must be divided by.
- * Usually thats
- * uint96 = tsc * numer
- * nanos = uint96 / denom
- * Instead, we create this quant constant and it becomes the numerator,
- * the denominator can then be 0x100000000 which makes our division as simple as
- * forgetting the lower 32 bits of the result. We can also pass this number to
- * user space as the numer and pass 0xFFFFFFFF (RTC_FAST_DENOM) as the denom to
- * convert raw counts * to nanos. The difference is so small as to be
- * undetectable by anything.
- *
- * Unfortunatly we can not do this for sub GHZ processors. In this case, all
- * we do is pass the CPU speed in raw as the denom and we pass in 1000000000
- * as the numerator. No short cuts allowed
- */
-#define RTC_FAST_DENOM 0xFFFFFFFF
-inline static uint32_t
-create_mul_quant_GHZ(int shift, uint32_t quant)
-{
- return (uint32_t)((((uint64_t)NSEC_PER_SEC/20) << shift) / quant);
-}
-/*
- * This routine takes a value of raw TSC ticks and applies the passed mul_quant
- * generated by create_mul_quant() This is our internal routine for creating
- * nanoseconds.
- * Since we don't really have uint96_t this routine basically does this....
- * uint96_t intermediate = (*value) * scale
- * return (intermediate >> 32)
- */
-inline static uint64_t
-fast_get_nano_from_abs(uint64_t value, int scale)
-{
- asm (" movl %%edx,%%esi \n\t"
- " mull %%ecx \n\t"
- " movl %%edx,%%edi \n\t"
- " movl %%esi,%%eax \n\t"
- " mull %%ecx \n\t"
- " xorl %%ecx,%%ecx \n\t"
- " addl %%edi,%%eax \n\t"
- " adcl %%ecx,%%edx "
- : "+A" (value)
- : "c" (scale)
- : "%esi", "%edi");
- return value;
-}
-
-/*
- * This routine basically does this...
- * ts.tv_sec = nanos / 1000000000; create seconds
- * ts.tv_nsec = nanos % 1000000000; create remainder nanos
- */
-inline static mach_timespec_t
-nanos_to_timespec(uint64_t nanos)
-{
- union {
- mach_timespec_t ts;
- uint64_t u64;
- } ret;
- ret.u64 = nanos;
- asm volatile("divl %1" : "+A" (ret.u64) : "r" (NSEC_PER_SEC));
- return ret.ts;
-}
-
-/*
- * The following two routines perform the 96 bit arithmetic we need to
- * convert generic absolute<->nanoseconds
- * The multiply routine takes a uint64_t and a uint32_t and returns the result
- * in a uint32_t[3] array.
- * The divide routine takes this uint32_t[3] array and divides it by a uint32_t
- * returning a uint64_t
- */
-inline static void
-longmul(uint64_t *abstime, uint32_t multiplicand, uint32_t *result)
-{
- asm volatile(
- " pushl %%ebx \n\t"
- " movl %%eax,%%ebx \n\t"
- " movl (%%eax),%%eax \n\t"
- " mull %%ecx \n\t"
- " xchg %%eax,%%ebx \n\t"
- " pushl %%edx \n\t"
- " movl 4(%%eax),%%eax \n\t"
- " mull %%ecx \n\t"
- " movl %2,%%ecx \n\t"
- " movl %%ebx,(%%ecx) \n\t"
- " popl %%ebx \n\t"
- " addl %%ebx,%%eax \n\t"
- " popl %%ebx \n\t"
- " movl %%eax,4(%%ecx) \n\t"
- " adcl $0,%%edx \n\t"
- " movl %%edx,8(%%ecx) // and save it"
- : : "a"(abstime), "c"(multiplicand), "m"(result));
-
-}
-
-inline static uint64_t
-longdiv(uint32_t *numer, uint32_t denom)
-{
- uint64_t result;
- asm volatile(
- " pushl %%ebx \n\t"
- " movl %%eax,%%ebx \n\t"
- " movl 8(%%eax),%%edx \n\t"
- " movl 4(%%eax),%%eax \n\t"
- " divl %%ecx \n\t"
- " xchg %%ebx,%%eax \n\t"
- " movl (%%eax),%%eax \n\t"
- " divl %%ecx \n\t"
- " xchg %%ebx,%%edx \n\t"
- " popl %%ebx \n\t"
- : "=A"(result) : "a"(numer),"c"(denom));
- return result;
-}
-
-/*
- * Enable or disable timer 2.
- * Port 0x61 controls timer 2:
- * bit 0 gates the clock,
- * bit 1 gates output to speaker.
- */
-inline static void
-enable_PIT2(void)
-{
- asm volatile(
- " inb $0x61,%%al \n\t"
- " and $0xFC,%%al \n\t"
- " or $1,%%al \n\t"
- " outb %%al,$0x61 \n\t"
- : : : "%al" );
-}
-
-inline static void
-disable_PIT2(void)
-{
- asm volatile(
- " inb $0x61,%%al \n\t"
- " and $0xFC,%%al \n\t"
- " outb %%al,$0x61 \n\t"
- : : : "%al" );
-}
-
-inline static void
-set_PIT2(int value)
-{
-/*
- * First, tell the clock we are going to write 16 bits to the counter
- * and enable one-shot mode (command 0xB8 to port 0x43)
- * Then write the two bytes into the PIT2 clock register (port 0x42).
- * Loop until the value is "realized" in the clock,
- * this happens on the next tick.
- */
- asm volatile(
- " movb $0xB8,%%al \n\t"
- " outb %%al,$0x43 \n\t"
- " movb %%dl,%%al \n\t"
- " outb %%al,$0x42 \n\t"
- " movb %%dh,%%al \n\t"
- " outb %%al,$0x42 \n"
-"1: inb $0x42,%%al \n\t"
- " inb $0x42,%%al \n\t"
- " cmp %%al,%%dh \n\t"
- " jne 1b"
- : : "d"(value) : "%al");
-}
-
-inline static uint64_t
-get_PIT2(unsigned int *value)
-{
- register uint64_t result;
-/*
- * This routine first latches the time (command 0x80 to port 0x43),
- * then gets the time stamp so we know how long the read will take later.
- * Read (from port 0x42) and return the current value of the timer.
- */
- asm volatile(
- " xorl %%ecx,%%ecx \n\t"
- " movb $0x80,%%al \n\t"
- " outb %%al,$0x43 \n\t"
- " rdtsc \n\t"
- " pushl %%eax \n\t"
- " inb $0x42,%%al \n\t"
- " movb %%al,%%cl \n\t"
- " inb $0x42,%%al \n\t"
- " movb %%al,%%ch \n\t"
- " popl %%eax "
- : "=A"(result), "=c"(*value));
- return result;
-}
-
-/*
- * timeRDTSC()
- * This routine sets up PIT counter 2 to count down 1/20 of a second.
- * It pauses until the value is latched in the counter
- * and then reads the time stamp counter to return to the caller.
- */
-static uint64_t
-timeRDTSC(void)
-{
- int attempts = 0;
- uint64_t latchTime;
- uint64_t saveTime,intermediate;
- unsigned int timerValue, lastValue;
- boolean_t int_enabled;
- /*
- * Table of correction factors to account for
- * - timer counter quantization errors, and
- * - undercounts 0..5
- */
-#define SAMPLE_CLKS_EXACT (((double) CLKNUM) / 20.0)
-#define SAMPLE_CLKS_INT ((int) CLKNUM / 20)
-#define SAMPLE_NSECS (2000000000LL)
-#define SAMPLE_MULTIPLIER (((double)SAMPLE_NSECS)*SAMPLE_CLKS_EXACT)
-#define ROUND64(x) ((uint64_t)((x) + 0.5))
- uint64_t scale[6] = {
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-0)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-1)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-2)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-3)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-4)),
- ROUND64(SAMPLE_MULTIPLIER/(double)(SAMPLE_CLKS_INT-5))
- };
-
- int_enabled = ml_set_interrupts_enabled(FALSE);
-
-restart:
- if (attempts >= 2)
- panic("timeRDTSC() calibation failed with %d attempts\n", attempts);
- attempts++;
- enable_PIT2(); // turn on PIT2
- set_PIT2(0); // reset timer 2 to be zero
- latchTime = rdtsc64(); // get the time stamp to time
- latchTime = get_PIT2(&timerValue) - latchTime; // time how long this takes
- set_PIT2(SAMPLE_CLKS_INT); // set up the timer for (almost) 1/20th a second
- saveTime = rdtsc64(); // now time how long a 20th a second is...
- get_PIT2(&lastValue);
- get_PIT2(&lastValue); // read twice, first value may be unreliable
- do {
- intermediate = get_PIT2(&timerValue);
- if (timerValue > lastValue) {
- printf("Hey we are going backwards! %u -> %u, restarting timing\n",
- timerValue,lastValue);
- set_PIT2(0);
- disable_PIT2();
- goto restart;
- }
- lastValue = timerValue;
- } while (timerValue > 5);
- kprintf("timerValue %d\n",timerValue);
- kprintf("intermediate 0x%016llx\n",intermediate);
- kprintf("saveTime 0x%016llx\n",saveTime);
-
- intermediate -= saveTime; // raw count for about 1/20 second
- intermediate *= scale[timerValue]; // rescale measured time spent
- intermediate /= SAMPLE_NSECS; // so its exactly 1/20 a second
- intermediate += latchTime; // add on our save fudge
-
- set_PIT2(0); // reset timer 2 to be zero
- disable_PIT2(); // turn off PIT 2
-
- ml_set_interrupts_enabled(int_enabled);
- return intermediate;
-}
-
-static uint64_t
-tsc_to_nanoseconds(uint64_t abstime)
-{
- uint32_t numer;
- uint32_t denom;
- uint32_t intermediate[3];
-
- numer = rtclock.timebase_const.numer;
- denom = rtclock.timebase_const.denom;
- if (denom == RTC_FAST_DENOM) {
- abstime = fast_get_nano_from_abs(abstime, numer);
- } else {
- longmul(&abstime, numer, intermediate);
- abstime = longdiv(intermediate, denom);
- }
- return abstime;
-}
-
-inline static mach_timespec_t
-tsc_to_timespec(void)
-{
- uint64_t currNanos;
- currNanos = rtc_nanotime_read();
- return nanos_to_timespec(currNanos);
-}