aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2013-07-20 13:06:35 -0400
committerKevin O'Connor <kevin@koconnor.net>2013-07-20 19:30:01 -0400
commitb7ab1784acf11b1ebf63b6dd40d45c2bbf745ef7 (patch)
treedaa4eecaa4596ec46c410c61e929b4069c231a82
parent69013378972c07c9a1e46fa6ec274070cad1a532 (diff)
downloadseabios-b7ab1784acf11b1ebf63b6dd40d45c2bbf745ef7.tar.gz
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>
-rw-r--r--src/biosvar.h3
-rw-r--r--src/clock.c2
-rw-r--r--src/pit.h7
-rw-r--r--src/timer.c38
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