The TICKS_PER_DAY setting is a bios standard and needs to be 1573040 for compatibility. However, there are actually ~1573042.24 ticks in a day. So, only use TICKS_PER_DAY when working with the BDA timer_counter - not when calculating any internal times.
The PIT hz is actually 143181800 / 12 (~1193181.667). This can be accurately encoded as PMTIMER hz / 3. Because the PIT hz is usually multiplied and divided by other numbers, we can use the PMTIMER hz and defer the division by 3 to improve accuracy.
When doing division for delay time calculations, always round up the division so the delay is never less than the requested time.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/biosvar.h | 3 +++ src/clock.c | 2 +- src/pit.h | 7 ------- src/timer.c | 38 +++++++++++++++++++++----------------- 4 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/src/biosvar.h b/src/biosvar.h index bbb196a..e49a10a 100644 --- a/src/biosvar.h +++ b/src/biosvar.h @@ -132,6 +132,9 @@ struct bios_data_area_s { #define FMS_DOUBLE_STEPPING (1<<5) #define FMS_DATA_RATE_MASK (0xc0)
+// Limit of BDA timer_counter field +#define TICKS_PER_DAY 1573040 + // Accessor functions #define GET_BDA(var) \ GET_FARVAR(SEG_BDA, ((struct bios_data_area_s *)0)->var) diff --git a/src/clock.c b/src/clock.c index 2f2ca07..4b33bb8 100644 --- a/src/clock.c +++ b/src/clock.c @@ -95,7 +95,7 @@ clock_setup(void) u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES)); u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS)); u32 ticks = ticks_from_ms(((hours * 60 + minutes) * 60 + seconds) * 1000); - SET_BDA(timer_counter, ticks); + SET_BDA(timer_counter, ticks % TICKS_PER_DAY);
// Setup Century storage if (CONFIG_QEMU) { diff --git a/src/pit.h b/src/pit.h index 7b5e5e8..098f270 100644 --- a/src/pit.h +++ b/src/pit.h @@ -2,13 +2,6 @@ #ifndef __PIT_H #define __PIT_H
-/* PM Timer ticks per second (HZ) */ -#define PM_TIMER_FREQUENCY 3579545 - -#define PIT_TICK_RATE 1193180 // Underlying HZ of PIT -#define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer -#define TICKS_PER_DAY (u32)((u64)60*60*24*PIT_TICK_RATE / PIT_TICK_INTERVAL) - // Bits for PORT_PIT_MODE #define PM_SEL_TIMER0 (0<<6) #define PM_SEL_TIMER1 (1<<6) diff --git a/src/timer.c b/src/timer.c index 0fd0194..d659e94 100644 --- a/src/timer.c +++ b/src/timer.c @@ -15,6 +15,10 @@ #define PPCB_SPKR (1<<1) #define PPCB_T2OUT (1<<5)
+#define PMTIMER_HZ 3579545 // Underlying Hz of the PM Timer +#define PMTIMER_TO_PIT 3 // Ratio of pmtimer rate to pit rate +#define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer +
/**************************************************************** * TSC timer @@ -44,8 +48,8 @@ timer_setup(void) 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); + no_tsc = 1; + cpu_khz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); dprintf(3, "386/486 class CPU. Using TSC emulation\n"); return; } @@ -72,10 +76,10 @@ timer_setup(void) 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); + u32 t = DIV_ROUND_UP(diff * PMTIMER_HZ, CALIBRATE_COUNT); + cpu_khz = DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT);
- dprintf(1, "CPU Mhz=%u\n", hz / 1000000); + dprintf(1, "CPU Mhz=%u\n", t / (1000000 * PMTIMER_TO_PIT)); }
/* TSC emulation timekeepers */ @@ -103,10 +107,9 @@ void pmtimer_setup(u16 ioport) { if (!CONFIG_PMTIMER) return; - u32 khz = PM_TIMER_FREQUENCY / 1000; - dprintf(1, "Using pmtimer, ioport 0x%x, freq %d kHz\n", ioport, khz); - SET_GLOBAL(pmtimer_ioport, ioport); - SET_GLOBAL(cpu_khz, khz); + dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport); + pmtimer_ioport = ioport; + cpu_khz = DIV_ROUND_UP(PMTIMER_HZ, 1000); }
static u64 pmtimer_get(void) @@ -160,20 +163,20 @@ tscsleep(u64 diff) }
void ndelay(u32 count) { - tscdelay(count * GET_GLOBAL(cpu_khz) / 1000000); + tscdelay(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000000)); } void udelay(u32 count) { - tscdelay(count * GET_GLOBAL(cpu_khz) / 1000); + tscdelay(DIV_ROUND_UP(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); + tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000000)); } void usleep(u32 count) { - tscsleep(count * GET_GLOBAL(cpu_khz) / 1000); + tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000)); } void msleep(u32 count) { tscsleep(count * GET_GLOBAL(cpu_khz)); @@ -190,7 +193,7 @@ u64 calc_future_tsc_usec(u32 usecs) { u32 khz = GET_GLOBAL(cpu_khz); - return get_tsc() + ((u64)(khz/1000) * usecs); + return get_tsc() + ((u64)DIV_ROUND_UP(khz, 1000) * usecs); }
@@ -202,15 +205,16 @@ calc_future_tsc_usec(u32 usecs) u32 ticks_to_ms(u32 ticks) { - return DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * ticks, PIT_TICK_RATE); + u32 t = PIT_TICK_INTERVAL * 1000 * PMTIMER_TO_PIT * ticks; + return DIV_ROUND_UP(t, PMTIMER_HZ); }
// Return the number of timer irqs in 'ms' number of milliseconds. u32 ticks_from_ms(u32 ms) { - u32 kticks = DIV_ROUND_UP((u64)ms * PIT_TICK_RATE, PIT_TICK_INTERVAL); - return DIV_ROUND_UP(kticks, 1000); + u32 t = DIV_ROUND_UP((u64)ms * PMTIMER_HZ, PIT_TICK_INTERVAL); + return DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT); }
// Calculate the timer value at 'count' number of full timer ticks in