diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2013-07-20 13:06:35 -0400 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2013-07-20 19:30:01 -0400 |
commit | b7ab1784acf11b1ebf63b6dd40d45c2bbf745ef7 (patch) | |
tree | daa4eecaa4596ec46c410c61e929b4069c231a82 /src | |
parent | 69013378972c07c9a1e46fa6ec274070cad1a532 (diff) | |
download | seabios-hppa-b7ab1784acf11b1ebf63b6dd40d45c2bbf745ef7.zip seabios-hppa-b7ab1784acf11b1ebf63b6dd40d45c2bbf745ef7.tar.gz seabios-hppa-b7ab1784acf11b1ebf63b6dd40d45c2bbf745ef7.tar.bz2 |
Improve accuracy of internal timers.
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>
Diffstat (limited to 'src')
-rw-r--r-- | src/biosvar.h | 3 | ||||
-rw-r--r-- | src/clock.c | 2 | ||||
-rw-r--r-- | src/pit.h | 7 | ||||
-rw-r--r-- | 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) { @@ -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 |