+#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);
+}
+
+#define DECREMENTER_MAX UINT_MAX
+static uint32_t
+deadline_to_decrementer(
+ uint64_t deadline,
+ uint64_t now)
+{
+ uint64_t delta;
+
+ if (deadline <= now)
+ return rtc_decrementer_min;
+ else {
+ delta = deadline - now;
+ return MIN(MAX(rtc_decrementer_min,delta),DECREMENTER_MAX);
+ }
+}
+
+static inline uint64_t
+lapic_time_countdown(uint32_t initial_count)
+{
+ boolean_t state;
+ uint64_t start_time;
+ uint64_t stop_time;
+ lapic_timer_count_t count;
+
+ state = ml_set_interrupts_enabled(FALSE);
+ lapic_set_timer(FALSE, one_shot, divide_by_1, initial_count);
+ start_time = rdtsc64();
+ do {
+ lapic_get_timer(NULL, NULL, NULL, &count);
+ } while (count > 0);
+ stop_time = rdtsc64();
+ ml_set_interrupts_enabled(state);
+
+ return tsc_to_nanoseconds(stop_time - start_time);
+}
+
+static void
+rtc_lapic_timer_calibrate(void)
+{
+ uint32_t nsecs;
+ uint64_t countdown;
+
+ if (!(cpuid_features() & CPUID_FEATURE_APIC))
+ return;
+
+ /*
+ * Set the local apic timer counting down to zero without an interrupt.
+ * Use the timestamp to calculate how long this takes.
+ */
+ nsecs = (uint32_t) lapic_time_countdown(rtc_intr_nsec);
+
+ /*
+ * Compute a countdown ratio for a given time in nanoseconds.
+ * That is, countdown = time * numer / denom.
+ */
+ countdown = (uint64_t)rtc_intr_nsec * (uint64_t)rtc_intr_nsec / nsecs;
+
+ nsecs = (uint32_t) lapic_time_countdown((uint32_t) countdown);
+
+ rtc_lapic_scale.numer = countdown;
+ rtc_lapic_scale.denom = nsecs;
+
+ kprintf("rtc_lapic_timer_calibrate() scale: %d/%d\n",
+ (uint32_t) countdown, nsecs);
+}
+
+static void
+rtc_lapic_set_timer(
+ uint32_t interval)
+{
+ uint64_t count;
+
+ assert(rtc_lapic_scale.denom);
+
+ count = interval * (uint64_t) rtc_lapic_scale.numer;
+ count /= rtc_lapic_scale.denom;
+
+ lapic_set_timer(TRUE, one_shot, divide_by_1, (uint32_t) count);
+}
+
+static void
+rtc_lapic_start_ticking(void)
+{
+ uint64_t abstime;
+ uint64_t first_tick;
+ uint64_t decr;
+
+ abstime = mach_absolute_time();
+ first_tick = abstime + NSEC_PER_HZ;
+ current_cpu_datap()->cpu_rtc_tick_deadline = first_tick;
+ decr = deadline_to_decrementer(first_tick, abstime);
+ rtc_lapic_set_timer(decr);