// 16bit code to handle system clocks.
//
-// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2002 MandrakeSoft S.A.
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#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 TICKS_PER_DAY (u32)((u64)60*60*24*PIT_TICK_RATE / PIT_TICK_INTERVAL)
#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 */
, (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 = rdtscll();
+ u64 start = get_tsc();
u64 end = start + diff;
while (!check_tsc(end))
cpu_relax();
static void
tscsleep(u64 diff)
{
- u64 start = rdtscll();
+ u64 start = get_tsc();
u64 end = start + diff;
while (!check_tsc(end))
yield();
calc_future_tsc(u32 msecs)
{
u32 khz = GET_GLOBAL(cpu_khz);
- return rdtscll() + ((u64)khz * msecs);
+ return get_tsc() + ((u64)khz * msecs);
}
u64
calc_future_tsc_usec(u32 usecs)
{
u32 khz = GET_GLOBAL(cpu_khz);
- return rdtscll() + ((u64)(khz/1000) * usecs);
+ return get_tsc() + ((u64)(khz/1000) * usecs);
}
timer_setup(void)
{
dprintf(3, "init timer\n");
+#if 1
calibrate_tsc();
+#endif
+ dprintf(3, "init timer: 01\n");
pit_setup();
+ dprintf(3, "init timer: 02\n");
init_rtc();
+ dprintf(3, "init timer: 03\n");
rtc_updating();
+ dprintf(3, "init timer: 04\n");
u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
+ dprintf(3, "init timer: 05\n");
u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
+ dprintf(3, "init timer: 06\n");
u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
+ dprintf(3, "init timer: 07\n");
u32 ticks = (hours * 60 + minutes) * 60 + seconds;
+ dprintf(3, "init timer: 08\n");
ticks = ((u64)ticks * PIT_TICK_RATE) / PIT_TICK_INTERVAL;
+ dprintf(3, "init timer: 09\n");
SET_BDA(timer_counter, ticks);
- SET_BDA(timer_rollover, 0);
+ dprintf(3, "init timer: 10\n");
- enable_hwirq(0, entry_08);
- enable_hwirq(8, entry_70);
+ enable_hwirq(0, FUNC16(entry_08));
+ dprintf(3, "init timer: 11\n");
+ enable_hwirq(8, FUNC16(entry_70));
+ dprintf(3, "init timer: 12\n");
}
* Standard clock functions
****************************************************************/
+#define TICKS_PER_DAY (u32)((u64)60*60*24*PIT_TICK_RATE / PIT_TICK_INTERVAL)
+
+// Calculate the timer value at 'count' number of full timer ticks in
+// the future.
+u32
+calc_future_timer_ticks(u32 count)
+{
+ return (GET_BDA(timer_counter) + count + 1) % TICKS_PER_DAY;
+}
+
+// Return the timer value that is 'msecs' time in the future.
+u32
+calc_future_timer(u32 msecs)
+{
+ if (!msecs)
+ return GET_BDA(timer_counter);
+ u32 kticks = DIV_ROUND_UP((u64)msecs * PIT_TICK_RATE, PIT_TICK_INTERVAL);
+ u32 ticks = DIV_ROUND_UP(kticks, 1000);
+ return calc_future_timer_ticks(ticks);
+}
+
+// Check if the given timer value has passed.
+int
+check_timer(u32 end)
+{
+ return (((GET_BDA(timer_counter) + TICKS_PER_DAY - end) % TICKS_PER_DAY)
+ < (TICKS_PER_DAY/2));
+}
+
// get current clock count
static void
handle_1a00(struct bregs *regs)