diff -r bd97e45e073a xen/arch/x86/time.c --- a/xen/arch/x86/time.c Tue Jul 08 09:28:50 2008 +0100 +++ b/xen/arch/x86/time.c Mon Jul 14 11:10:09 2008 -0600 @@ -54,14 +54,14 @@ struct cpu_time { s_time_t stime_local_stamp; s_time_t stime_master_stamp; struct time_scale tsc_scale; - u32 cstate_plt_count_stamp; + u64 cstate_plt_count_stamp; struct timer calibration_timer; }; struct platform_timesource { char *name; u64 frequency; - u32 (*read_counter)(void); + u64 (*read_counter)(void); int counter_bits; }; @@ -311,7 +311,7 @@ static char *freq_string(u64 freq) * PLATFORM TIMER 1: PROGRAMMABLE INTERVAL TIMER (LEGACY PIT) */ -static u32 read_pit_count(void) +static u64 read_pit_count(void) { u16 count16; u32 count32; @@ -327,7 +327,7 @@ static u32 read_pit_count(void) spin_unlock_irqrestore(&pit_lock, flags); - return count32; + return (u64)count32; } static void init_pit(struct platform_timesource *pts) @@ -343,9 +343,9 @@ static void init_pit(struct platform_tim * PLATFORM TIMER 2: HIGH PRECISION EVENT TIMER (HPET) */ -static u32 read_hpet_count(void) +static u64 read_hpet_count(void) { - return hpet_read32(HPET_COUNTER); + return (u64)hpet_read32(HPET_COUNTER); } static int init_hpet(struct platform_timesource *pts) @@ -383,9 +383,9 @@ int use_cyclone; /* Cyclone MPMC0 register. */ static volatile u32 *cyclone_timer; -static u32 read_cyclone_count(void) +static u64 read_cyclone_count(void) { - return *cyclone_timer; + return (u64)*cyclone_timer; } static volatile u32 *map_cyclone_reg(unsigned long regaddr) @@ -433,9 +433,9 @@ u32 pmtmr_ioport; /* ACPI PM timer ticks at 3.579545 MHz. */ #define ACPI_PM_FREQUENCY 3579545 -static u32 read_pmtimer_count(void) +static u64 read_pmtimer_count(void) { - return inl(pmtmr_ioport); + return (u64)inl(pmtmr_ioport); } static int init_pmtimer(struct platform_timesource *pts) @@ -452,15 +452,38 @@ static int init_pmtimer(struct platform_ } /************************************************************ + * PLATFORM TIMER 5: TSC + */ + +static u64 tsc_freq; + +static u64 read_tsc_count(void) +{ + u64 tsc; + rdtscll(tsc); + return tsc; +} + +static int init_tsctimer(struct platform_timesource *pts) +{ + /* TODO: evaluate stability of TSC here, return 0 if not stable */ + pts->name = "TSC"; + pts->frequency = tsc_freq; + pts->read_counter = read_tsc_count; + pts->counter_bits = 64; + return 1; +} + +/************************************************************ * GENERIC PLATFORM TIMER INFRASTRUCTURE */ static struct platform_timesource plt_src; /* details of chosen timesource */ -static u32 plt_mask; /* hardware-width mask */ +static u64 plt_mask; /* hardware-width mask */ static u64 plt_overflow_period; /* ns between calls to plt_overflow() */ static struct time_scale plt_scale; /* scale: platform counter -> nanosecs */ -/* Protected by platform_timer_lock. */ +/* Protected by platform_timer_lock. Must be zero for 64-bit clocksources */ static DEFINE_SPINLOCK(platform_timer_lock); static s_time_t stime_platform_stamp; /* System time at below platform time */ static u64 platform_timer_stamp; /* Platform time at above system time */ @@ -472,8 +495,9 @@ static void plt_overflow(void *unused) { u32 count; + ASSERT(plt_src.counter_bits <= 32); spin_lock(&platform_timer_lock); - count = plt_src.read_counter(); + count = (u32)plt_src.read_counter(); plt_stamp64 += (count - plt_stamp) & plt_mask; plt_stamp = count; spin_unlock(&platform_timer_lock); @@ -506,6 +530,8 @@ static void platform_time_calibration(vo u64 count; s_time_t stamp; + if ( plt_src.counter_bits == 64 ) + return; spin_lock(&platform_timer_lock); count = plt_stamp64 + ((plt_src.read_counter() - plt_stamp) & plt_mask); stamp = __read_platform_stime(count); @@ -516,6 +542,8 @@ static void platform_time_calibration(vo static void resume_platform_timer(void) { + if ( plt_src.counter_bits == 64 ) + return; /* No change in platform_stime across suspend/resume. */ platform_timer_stamp = plt_stamp64; plt_stamp = plt_src.read_counter(); @@ -536,6 +564,8 @@ static void init_platform_timer(void) rc = init_cyclone(pts); else if ( !strcmp(opt_clocksource, "acpi") ) rc = init_pmtimer(pts); + else if ( !strcmp(opt_clocksource, "tsc") ) + rc = init_tsctimer(pts); if ( rc <= 0 ) printk("WARNING: %s clocksource '%s'.\n", @@ -549,16 +579,26 @@ static void init_platform_timer(void) !init_pmtimer(pts) ) init_pit(pts); - plt_mask = (u32)~0u >> (32 - pts->counter_bits); - set_time_scale(&plt_scale, pts->frequency); - plt_overflow_period = scale_delta( - 1ull << (pts->counter_bits-1), &plt_scale); - init_timer(&plt_overflow_timer, plt_overflow, NULL, 0); - plt_overflow(NULL); + if (pts->counter_bits != 64 ) + { + plt_mask = (u32)~0u >> (32 - pts->counter_bits); - platform_timer_stamp = plt_stamp64; + plt_overflow_period = scale_delta( + 1ull << (pts->counter_bits-1), &plt_scale); + init_timer(&plt_overflow_timer, plt_overflow, NULL, 0); + plt_overflow(NULL); + + platform_timer_stamp = plt_stamp64; + } + else + { + plt_mask = -1LL; + platform_timer_stamp = stime_platform_stamp = 0; + plt_stamp = plt_stamp64 = 0; + } + printk("Platform timer is %s %s\n", freq_string(pts->frequency), pts->name); @@ -577,10 +617,11 @@ void cstate_restore_tsc(void) void cstate_restore_tsc(void) { struct cpu_time *t; - u32 plt_count_delta; + u64 plt_count_delta; u64 tsc_delta; - if (!tsc_invariant){ + if ( !tsc_invariant ) + { t = &this_cpu(cpu_time); /* if platform counter overflow happens, interrupt will bring CPU from @@ -687,14 +728,19 @@ static unsigned long get_cmos_time(void) s_time_t get_s_time(void) { - struct cpu_time *t = &this_cpu(cpu_time); + struct cpu_time *t; u64 tsc, delta; s_time_t now; rdtscll(tsc); - delta = tsc - t->local_tsc_stamp; - now = t->stime_local_stamp + scale_delta(delta, &t->tsc_scale); - + if ( plt_src.counter_bits == 64 ) + now = scale_delta(tsc, &plt_scale); + else + { + t = &this_cpu(cpu_time); + delta = tsc - t->local_tsc_stamp; + now = t->stime_local_stamp + scale_delta(delta, &t->tsc_scale); + } return now; } @@ -996,6 +1042,7 @@ void __init early_time_init(void) { u64 tmp = init_pit_and_calibrate_tsc(); + tsc_freq = tmp; set_time_scale(&this_cpu(cpu_time).tsc_scale, tmp); do_div(tmp, 1000);