--- /dev/null
+#include <lwk/kernel.h>
+#include <lwk/init.h>
+#include <lwk/spinlock.h>
+#include <lwk/cpuinfo.h>
+#include <lwk/smp.h>
+#include <lwk/time.h>
+#include <arch/io.h>
+#include <arch/apic.h>
+
+#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
+ );
+}
+