/*
- * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2012 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* the cpu clock counted by the timestamp MSR.
*/
-#include <platforms.h>
#include <mach/mach_types.h>
#include <kern/misc_protos.h>
#include <kern/spl.h>
#include <kern/assert.h>
-#include <kern/etimer.h>
+#include <kern/timer_queue.h>
#include <mach/vm_prot.h>
#include <vm/pmap.h>
#include <vm/vm_kern.h> /* for kernel_map */
#include <sys/kdebug.h>
#include <i386/tsc.h>
#include <i386/rtclock_protos.h>
-
#define UI_CPUFREQ_ROUNDING_FACTOR 10000000
-int rtclock_config(void);
-
int rtclock_init(void);
uint64_t tsc_rebase_abs_time = 0;
/*
* Force a complete re-evaluation of timer deadlines.
*/
- etimer_resync_deadlines();
-}
-
-/*
- * tsc_to_nanoseconds:
- *
- * Basic routine to convert a raw 64 bit TSC value to a
- * 64 bit nanosecond value. The conversion is implemented
- * based on the scale factor and an implicit 32 bit shift.
- */
-static inline uint64_t
-_tsc_to_nanoseconds(uint64_t value)
-{
-#if defined(__i386__)
- asm volatile("movl %%edx,%%esi ;"
- "mull %%ecx ;"
- "movl %%edx,%%edi ;"
- "movl %%esi,%%eax ;"
- "mull %%ecx ;"
- "addl %%edi,%%eax ;"
- "adcl $0,%%edx "
- : "+A" (value)
- : "c" (pal_rtc_nanotime_info.scale)
- : "esi", "edi");
-#elif defined(__x86_64__)
- asm volatile("mul %%rcx;"
- "shrq $32, %%rax;"
- "shlq $32, %%rdx;"
- "orq %%rdx, %%rax;"
- : "=a"(value)
- : "a"(value), "c"(pal_rtc_nanotime_info.scale)
- : "rdx", "cc" );
-#else
-#error Unsupported architecture
-#endif
-
- return (value);
+ x86_lcpu()->rtcDeadline = EndOfAllTime;
+ timer_resync_deadlines();
}
static inline uint32_t
_absolutetime_to_microtime(uint64_t abstime, clock_sec_t *secs, clock_usec_t *microsecs)
{
uint32_t remain;
-#if defined(__i386__)
- asm volatile(
- "divl %3"
- : "=a" (*secs), "=d" (remain)
- : "A" (abstime), "r" (NSEC_PER_SEC));
- asm volatile(
- "divl %3"
- : "=a" (*microsecs)
- : "0" (remain), "d" (0), "r" (NSEC_PER_USEC));
-#elif defined(__x86_64__)
*secs = abstime / (uint64_t)NSEC_PER_SEC;
remain = (uint32_t)(abstime % (uint64_t)NSEC_PER_SEC);
*microsecs = remain / NSEC_PER_USEC;
-#else
-#error Unsupported architecture
-#endif
return remain;
}
static inline void
_absolutetime_to_nanotime(uint64_t abstime, clock_sec_t *secs, clock_usec_t *nanosecs)
{
-#if defined(__i386__)
- asm volatile(
- "divl %3"
- : "=a" (*secs), "=d" (*nanosecs)
- : "A" (abstime), "r" (NSEC_PER_SEC));
-#elif defined(__x86_64__)
*secs = abstime / (uint64_t)NSEC_PER_SEC;
*nanosecs = (clock_usec_t)(abstime % (uint64_t)NSEC_PER_SEC);
-#else
-#error Unsupported architecture
-#endif
}
-/*
- * Configure the real-time clock device. Return success (1)
- * or failure (0).
- */
-
-int
-rtclock_config(void)
-{
- /* nothing to do */
- return (1);
-}
-
-
/*
* Nanotime/mach_absolutime_time
* -----------------------------
_pal_rtc_nanotime_store(tsc, base, rntp->scale, rntp->shift, rntp);
}
-static void
+void
rtc_nanotime_init(uint64_t base)
{
_rtc_nanotime_init(&pal_rtc_nanotime_info, base);
static inline uint64_t
rtc_nanotime_read(void)
{
-
-#if CONFIG_EMBEDDED
- if (gPEClockFrequencyInfo.timebase_frequency_hz > SLOW_TSC_THRESHOLD)
- return _rtc_nanotime_read(&rtc_nanotime_info, 1); /* slow processor */
- else
-#endif
- return _rtc_nanotime_read(&pal_rtc_nanotime_info, 0); /* assume fast processor */
+ return _rtc_nanotime_read(&pal_rtc_nanotime_info);
}
/*
assert(!ml_get_interrupts_enabled());
tsc = rdtsc64();
- oldnsecs = rntp->ns_base + _tsc_to_nanoseconds(tsc - rntp->tsc_base);
- newnsecs = base + _tsc_to_nanoseconds(tsc - tsc_base);
+ oldnsecs = rntp->ns_base + _rtc_tsc_to_nanoseconds(tsc - rntp->tsc_base, rntp);
+ newnsecs = base + _rtc_tsc_to_nanoseconds(tsc - tsc_base, rntp);
/*
* Only update the base values if time using the new base values
if (oldnsecs < newnsecs) {
_pal_rtc_nanotime_store(tsc_base, base, rntp->scale, rntp->shift, rntp);
rtc_nanotime_set_commpage(rntp);
- trace_set_timebases(tsc_base, base);
}
}
* rtc_sleep_wakeup:
*
* Invoked from power management when we have awoken from a sleep (S3)
- * and the TSC has been reset. The nanotime data is updated based on
- * the passed in value.
+ * and the TSC has been reset, or from Deep Idle (S0) sleep when the TSC
+ * has progressed. The nanotime data is updated based on the passed-in value.
*
* The caller must guarantee non-reentrancy.
*/
uint64_t base)
{
/* Set fixed configuration for lapic timers */
- rtc_timer->config();
+ rtc_timer->rtc_config();
/*
* Reset nanotime.
rtc_nanotime_init(base);
}
+void
+rtc_decrementer_configure(void) {
+ rtc_timer->rtc_config();
+}
+/*
+ * rtclock_early_init() is called very early at boot to
+ * establish mach_absolute_time() and set it to zero.
+ */
+void
+rtclock_early_init(void)
+{
+ assert(tscFreq);
+ rtc_set_timescale(tscFreq);
+}
+
/*
* Initialize the real-time clock device.
* In addition, various variables used to support the clock are initialized.
if (cpu_number() == master_cpu) {
assert(tscFreq);
- rtc_set_timescale(tscFreq);
/*
* Adjust and set the exported cpu speed.
rtc_timer_init();
clock_timebase_init();
ml_init_lock_timeout();
- ml_init_delay_spin_threshold();
+ ml_init_delay_spin_threshold(10);
}
/* Set fixed configuration for lapic timers */
- rtc_timer->config();
+ rtc_timer->rtc_config();
rtc_timer_start();
return (1);
rtc_set_timescale(uint64_t cycles)
{
pal_rtc_nanotime_t *rntp = &pal_rtc_nanotime_info;
+ uint32_t shift = 0;
+
+ /* the "scale" factor will overflow unless cycles>SLOW_TSC_THRESHOLD */
+
+ while ( cycles <= SLOW_TSC_THRESHOLD) {
+ shift++;
+ cycles <<= 1;
+ }
+
rntp->scale = (uint32_t)(((uint64_t)NSEC_PER_SEC << 32) / cycles);
-#if CONFIG_EMBEDDED
- if (cycles <= SLOW_TSC_THRESHOLD)
- rntp->shift = (uint32_t)cycles;
- else
-#endif
- rntp->shift = 32;
+ rntp->shift = shift;
+ /*
+ * On some platforms, the TSC is not reset at warm boot. But the
+ * rebase time must be relative to the current boot so we can't use
+ * mach_absolute_time(). Instead, we convert the TSC delta since boot
+ * to nanoseconds.
+ */
if (tsc_rebase_abs_time == 0)
- tsc_rebase_abs_time = mach_absolute_time();
+ tsc_rebase_abs_time = _rtc_tsc_to_nanoseconds(
+ rdtsc64() - tsc_at_boot, rntp);
rtc_nanotime_init(0);
}
static uint64_t
rtc_export_speed(uint64_t cyc_per_sec)
{
+ pal_rtc_nanotime_t *rntp = &pal_rtc_nanotime_info;
uint64_t cycles;
+ if (rntp->shift != 0 )
+ printf("Slow TSC, rtc_nanotime.shift == %d\n", rntp->shift);
+
/* Round: */
cycles = ((cyc_per_sec + (UI_CPUFREQ_ROUNDING_FACTOR/2))
/ UI_CPUFREQ_ROUNDING_FACTOR)
}
void
-clock_gettimeofday_set_commpage(
- uint64_t abstime,
- uint64_t epoch,
- uint64_t offset,
- clock_sec_t *secs,
- clock_usec_t *microsecs)
+clock_gettimeofday_set_commpage(uint64_t abstime, uint64_t sec, uint64_t frac, uint64_t scale, uint64_t tick_per_sec)
{
- uint64_t now = abstime + offset;
- uint32_t remain;
-
- remain = _absolutetime_to_microtime(now, secs, microsecs);
-
- *secs += (clock_sec_t)epoch;
-
- commpage_set_timestamp(abstime - remain, *secs);
+ commpage_set_timestamp(abstime, sec, frac, scale, tick_per_sec);
}
void
}
/* call the generic etimer */
- etimer_intr(user_mode, rip);
+ timer_intr(user_mode, rip);
}
*/
uint64_t
-setPop(
- uint64_t time)
+setPop(uint64_t time)
{
uint64_t now;
uint64_t pop;
if (time == 0 || time == EndOfAllTime ) {
time = EndOfAllTime;
now = 0;
- pop = rtc_timer->set(0, 0);
+ pop = rtc_timer->rtc_set(0, 0);
} else {
now = rtc_nanotime_read(); /* The time in nanoseconds */
- pop = rtc_timer->set(time, now);
+ pop = rtc_timer->rtc_set(time, now);
}
/* Record requested and actual deadlines set */
return rtc_nanotime_read();
}
+uint64_t
+mach_approximate_time(void)
+{
+ return rtc_nanotime_read();
+}
+
void
clock_interval_to_absolutetime_interval(
uint32_t interval,
_absolutetime_to_microtime(abstime, secs, microsecs);
}
-void
-absolutetime_to_nanotime(
- uint64_t abstime,
- clock_sec_t *secs,
- clock_nsec_t *nanosecs)
-{
- _absolutetime_to_nanotime(abstime, secs, nanosecs);
-}
-
void
nanotime_to_absolutetime(
clock_sec_t secs,
void
machine_delay_until(
+ uint64_t interval,
uint64_t deadline)
{
- uint64_t now;
-
- do {
+ (void)interval;
+ while (mach_absolute_time() < deadline) {
cpu_pause();
- now = mach_absolute_time();
- } while (now < deadline);
+ }
}