X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=kitten%2Farch%2Fx86_64%2Fkernel%2Ftime.c;fp=kitten%2Farch%2Fx86_64%2Fkernel%2Ftime.c;h=cc2b0bf19d7e918db1217c90589a4e4b27f48c3f;hb=66a1a4c7a9edcd7d8bc207aca093d694a6e6b5b2;hp=0000000000000000000000000000000000000000;hpb=f7cf9c19ecb0a589dd45ae0d2c91814bd3c2acc2;p=palacios.git diff --git a/kitten/arch/x86_64/kernel/time.c b/kitten/arch/x86_64/kernel/time.c new file mode 100644 index 0000000..cc2b0bf --- /dev/null +++ b/kitten/arch/x86_64/kernel/time.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PC +/** + * Lock that synchronizes access to the Programmable Interval Timer. + */ +static DEFINE_SPINLOCK(pit_lock); + +/** + * This stops the Programmable Interval Timer's periodic system timer + * (channel 0). Some systems, BOCHS included, are booted with the PIT system + * timer enabled. The LWK doesn't use the PIT, so this function is used during + * bootstrap to disable it. + */ +static void __init +pit_stop_timer0(void) +{ + unsigned long flags; + unsigned PIT_MODE = 0x43; + unsigned PIT_CH0 = 0x40; + + spin_lock_irqsave(&pit_lock, flags); + outb_p(0x30, PIT_MODE); /* mode 0 */ + outb_p(0, PIT_CH0); /* LSB system timer interval */ + outb_p(0, PIT_CH0); /* MSB system timer interval */ + spin_unlock_irqrestore(&pit_lock, flags); +} + +/** + * This uses the Programmable Interval Timer that is standard on all + * PC-compatible systems to determine the time stamp counter frequency. + * + * This uses the speaker output (channel 2) of the PIT. This is better than + * using the timer interrupt output because we can read the value of the + * speaker with just one inb(), where we need three i/o operations for the + * interrupt channel. We count how many ticks the TSC does in 50 ms. + * + * Returns the detected time stamp counter frequency in KHz. + */ +static unsigned int __init +pit_calibrate_tsc(void) +{ + cycles_t start, end; + unsigned long flags; + unsigned long pit_tick_rate = 1193182UL; /* 1.193182 MHz */ + + spin_lock_irqsave(&pit_lock, flags); + + outb((inb(0x61) & ~0x02) | 0x01, 0x61); + + outb(0xb0, 0x43); + outb((pit_tick_rate / (1000 / 50)) & 0xff, 0x42); + outb((pit_tick_rate / (1000 / 50)) >> 8, 0x42); + start = get_cycles_sync(); + while ((inb(0x61) & 0x20) == 0); + end = get_cycles_sync(); + + spin_unlock_irqrestore(&pit_lock, flags); + + return (end - start) / 50; +} +#endif + +#ifdef CONFIG_CRAY_XT +/** + * The Cray XT platform does not have any real time clocks. Therefore, + * we have to inspect various MSRs to determine the CPU frequency and + * trust that it is accurate. + * + * Returns the detected CPU frequency in KHz. + * + * NOTE: This function should only be used on Cray XT3/XT4/XT? platforms. + * While it will work on (some) AMD Opteron K8 and K10 systems, using a + * timer based mechanism to detect the actual CPU frequency is preferred. + */ +static unsigned int __init +crayxt_detect_cpu_freq(void) +{ + unsigned int MHz = 200; + unsigned int lower, upper; + int amd_family = cpu_info[this_cpu].arch.x86_family; + int amd_model = cpu_info[this_cpu].arch.x86_model; + + if (amd_family == 16) { + unsigned int fid; /* current frequency id */ + unsigned int did; /* current divide id */ + + rdmsr(MSR_K10_COFVID_STATUS, lower, upper); + fid = lower & 0x3f; + did = (lower >> 6) & 0x3f; + MHz = 100 * (fid + 0x10) / (1 << did); + + } else if (amd_family == 15) { + unsigned int fid; /* current frequency id */ + + if (amd_model < 16) { + /* Revision C and earlier */ + rdmsr(MSR_K8_HWCR, lower, upper); + fid = (lower >> 24) & 0x3f; + } else { + /* Revision D and later */ + rdmsr(MSR_K8_FIDVID_STATUS, lower, upper); + fid = lower & 0x3f; + } + + switch (fid) { + case 0: MHz *= 4; break; + case 2: MHz *= 5; break; + case 4: MHz *= 6; break; + case 6: MHz *= 7; break; + case 8: MHz *= 8; break; + case 10: MHz *= 9; break; + case 12: MHz *= 10; break; + case 14: MHz *= 11; break; + case 16: MHz *= 12; break; + case 18: MHz *= 13; break; + case 20: MHz *= 14; break; + case 22: MHz *= 15; break; + case 24: MHz *= 16; break; + case 26: MHz *= 17; break; + case 28: MHz *= 18; break; + case 30: MHz *= 19; break; + case 32: MHz *= 20; break; + case 34: MHz *= 21; break; + case 36: MHz *= 22; break; + case 38: MHz *= 23; break; + case 40: MHz *= 24; break; + case 42: MHz *= 25; break; + } + } else { + panic("Unknown AMD CPU family (%d).", amd_family); + } + + return (MHz * 1000); /* return CPU freq. in KHz */ +} +#endif + +void __init +time_init(void) +{ + unsigned int cpu_khz; + unsigned int lapic_khz; + + /* + * Detect the CPU frequency + */ +#if defined CONFIG_PC + cpu_khz = pit_calibrate_tsc(); + pit_stop_timer0(); +#elif defined CONFIG_CRAY_XT + cpu_khz = crayxt_detect_cpu_freq(); +#else + #error "In time_init(), unknown system architecture." +#endif + + cpu_info[this_cpu].arch.cur_cpu_khz = cpu_khz; + cpu_info[this_cpu].arch.max_cpu_khz = cpu_khz; + cpu_info[this_cpu].arch.min_cpu_khz = cpu_khz; + cpu_info[this_cpu].arch.tsc_khz = cpu_khz; + + init_cycles2ns(cpu_khz); + + /* + * Detect the Local APIC timer's base clock frequency + */ + if (this_cpu == 0) { + lapic_khz = lapic_calibrate_timer(); + } else { + lapic_khz = cpu_info[0].arch.lapic_khz; + } + + cpu_info[this_cpu].arch.lapic_khz = lapic_khz; + + printk(KERN_DEBUG "CPU %u: %u.%03u MHz, LAPIC bus %u.%03u MHz\n", + this_cpu, + cpu_khz / 1000, cpu_khz % 1000, + lapic_khz / 1000, lapic_khz % 1000 + ); +} +