+
+#define RTC_B_SET 0x80
+#define RTC_B_PIE 0x40
+#define RTC_B_AIE 0x20
+#define RTC_B_UIE 0x10
+#define RTC_B_BIN 0x04
+#define RTC_B_24HR 0x02
+#define RTC_B_DSE 0x01
+
+
+// Bits for PORT_PS2_CTRLB
+#define PPCB_T2GATE (1<<0)
+#define PPCB_SPKR (1<<1)
+#define PPCB_T2OUT (1<<5)
+
+// Bits for PORT_PIT_MODE
+#define PM_SEL_TIMER0 (0<<6)
+#define PM_SEL_TIMER1 (1<<6)
+#define PM_SEL_TIMER2 (2<<6)
+#define PM_SEL_READBACK (3<<6)
+#define PM_ACCESS_LATCH (0<<4)
+#define PM_ACCESS_LOBYTE (1<<4)
+#define PM_ACCESS_HIBYTE (2<<4)
+#define PM_ACCESS_WORD (3<<4)
+#define PM_MODE0 (0<<1)
+#define PM_MODE1 (1<<1)
+#define PM_MODE2 (2<<1)
+#define PM_MODE3 (3<<1)
+#define PM_MODE4 (4<<1)
+#define PM_MODE5 (5<<1)
+#define PM_CNT_BINARY (0<<0)
+#define PM_CNT_BCD (1<<0)
+#define PM_READ_COUNTER0 (1<<1)
+#define PM_READ_COUNTER1 (1<<2)
+#define PM_READ_COUNTER2 (1<<3)
+#define PM_READ_STATUSVALUE (0<<4)
+#define PM_READ_VALUE (1<<4)
+#define PM_READ_STATUS (2<<4)
+
+
+/****************************************************************
+ * TSC timer
+ ****************************************************************/
+
+#define CALIBRATE_COUNT 0x800 // Approx 1.7ms
+
+u32 cpu_khz VAR16VISIBLE;
+u8 no_tsc VAR16VISIBLE;
+
+static void
+calibrate_tsc(void)
+{
+ u32 eax, ebx, ecx, edx, cpuid_features = 0;
+ cpuid(0, &eax, &ebx, &ecx, &edx);
+ if (eax > 0)
+ cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
+
+ if (!(cpuid_features & CPUID_TSC)) {
+ SET_GLOBAL(no_tsc, 1);
+ SET_GLOBAL(cpu_khz, PIT_TICK_RATE / 1000);
+ dprintf(3, "386/486 class CPU. Using TSC emulation\n");
+ return;
+ }
+
+ // Setup "timer2"
+#if 0
+ u8 orig = inb(PORT_PS2_CTRLB);
+ outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB);
+ /* binary, mode 0, LSB/MSB, Ch 2 */
+ outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE);
+ /* LSB of ticks */
+ outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2);
+ /* MSB of ticks */
+ outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2);
+
+ u64 start = rdtscll();
+ while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0)
+ ;
+ u64 end = rdtscll();
+
+ // Restore PORT_PS2_CTRLB
+ outb(orig, PORT_PS2_CTRLB);
+
+ // Store calibrated cpu khz.
+ u64 diff = end - start;
+ dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n"
+ , (u32)start, (u32)end, (u32)diff);
+ u32 hz = diff * PIT_TICK_RATE / CALIBRATE_COUNT;
+ SET_GLOBAL(cpu_khz, hz / 1000);
+#else
+ u32 hz = 800 * 1000000;
+ SET_GLOBAL(cpu_khz, hz / 1000);
+#endif
+
+ dprintf(1, "CPU Mhz=%u\n", hz / 1000000);
+}
+
+static u64
+emulate_tsc(void)
+{
+ int cnt, d;
+ u16 ebda_seg = get_ebda_seg();
+ u64 ret;
+ /* read timer 0 current count */
+ ret = GET_EBDA2(ebda_seg, tsc_8254);
+ /* readback mode has slightly shifted registers, works on all 8254, readback PIT0 latch */
+ outb(PM_SEL_READBACK | PM_READ_VALUE | PM_READ_COUNTER0, PORT_PIT_MODE);
+ cnt = (inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8));
+ d = GET_EBDA2(ebda_seg, last_tsc_8254) - cnt;
+ /* Determine the ticks count from last invocation of this function */
+ ret += (d > 0) ? d : (PIT_TICK_INTERVAL + d);
+ SET_EBDA2(ebda_seg, last_tsc_8254, cnt);
+ SET_EBDA2(ebda_seg, tsc_8254, ret);
+ return ret;
+}
+
+static u64
+get_tsc(void)
+{
+ if (unlikely(GET_GLOBAL(no_tsc)))
+ return emulate_tsc();
+ return rdtscll();
+}
+
+int
+check_tsc(u64 end)
+{
+ return (s64)(get_tsc() - end) > 0;
+}
+
+static void
+tscdelay(u64 diff)
+{
+ u64 start = get_tsc();
+ u64 end = start + diff;
+ while (!check_tsc(end))
+ cpu_relax();
+}
+
+static void
+tscsleep(u64 diff)
+{
+ u64 start = get_tsc();
+ u64 end = start + diff;
+ while (!check_tsc(end))
+ yield();
+}
+
+void ndelay(u32 count) {
+ tscdelay(count * GET_GLOBAL(cpu_khz) / 1000000);
+}
+void udelay(u32 count) {
+ tscdelay(count * GET_GLOBAL(cpu_khz) / 1000);
+}
+void mdelay(u32 count) {
+ tscdelay(count * GET_GLOBAL(cpu_khz));
+}
+
+void nsleep(u32 count) {
+ tscsleep(count * GET_GLOBAL(cpu_khz) / 1000000);
+}
+void usleep(u32 count) {
+ tscsleep(count * GET_GLOBAL(cpu_khz) / 1000);
+}
+void msleep(u32 count) {
+ tscsleep(count * GET_GLOBAL(cpu_khz));
+}
+
+// Return the TSC value that is 'msecs' time in the future.
+u64
+calc_future_tsc(u32 msecs)
+{
+ u32 khz = GET_GLOBAL(cpu_khz);
+ return get_tsc() + ((u64)khz * msecs);
+}
+u64
+calc_future_tsc_usec(u32 usecs)
+{
+ u32 khz = GET_GLOBAL(cpu_khz);
+ return get_tsc() + ((u64)(khz/1000) * usecs);
+}