X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fclock.c;h=d88515c45c482153144365c90b099c465b5076e8;hb=b29200c51beb1e75e7f844a4093da05b8394ad5e;hp=062658c16e2c1a1e51a8490bb553c63c7187dbf4;hpb=b5cc2ca5d06b4982139afd1205a0390944c60c7d;p=seabios.git diff --git a/src/clock.c b/src/clock.c index 062658c..d88515c 100644 --- a/src/clock.c +++ b/src/clock.c @@ -1,6 +1,6 @@ // 16bit code to handle system clocks. // -// Copyright (C) 2008 Kevin O'Connor +// Copyright (C) 2008-2010 Kevin O'Connor // Copyright (C) 2002 MandrakeSoft S.A. // // This file may be distributed under the terms of the GNU LGPLv3 license. @@ -48,6 +48,12 @@ #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) /**************************************************************** @@ -57,11 +63,25 @@ #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 */ @@ -85,14 +105,51 @@ calibrate_tsc(void) , (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(); @@ -101,7 +158,7 @@ tscdelay(u64 diff) static void tscsleep(u64 diff) { - u64 start = rdtscll(); + u64 start = get_tsc(); u64 end = start + diff; while (!check_tsc(end)) yield(); @@ -132,13 +189,13 @@ u64 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); } @@ -200,21 +257,34 @@ void 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"); } @@ -231,14 +301,18 @@ 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) { - u32 kticks = DIV_ROUND_UP((u64)(msecs * PIT_TICK_RATE), PIT_TICK_INTERVAL); + 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)