This patch series improves the accuracy of the internal timers. It also refactors a bit of the code and makes it a little cleaner.
-Kevin
Kevin O'Connor (10): Move internal timer code from clock.c to a new file timer.c. Don't pass khz to pmtimer_setup - it's always PM_TIMER_FREQUENCY. Add helper functions to convert timer irqs to milliseconds. Improve accuracy of internal timers. Rename cpu_khz to TimerKHz. Shift CPU TSC down to reduce need for 64bit variables. Rename check_timer() function (and similar) to irqtimer_check(). Rename check_tsc() (and similar) to timer_check() and use u32. Separate out timer setup code. Unify pmtimer_read() and pittimer_read() code.
Makefile | 2 +- src/acpi.c | 2 +- src/ahci.c | 19 ++--- src/ata.c | 18 ++--- src/biosvar.h | 3 + src/blockcmd.c | 6 +- src/boot.c | 4 +- src/clock.c | 249 ++------------------------------------------------------- src/csm.c | 1 + src/floppy.c | 8 +- src/megasas.c | 10 +-- src/pciinit.c | 7 +- src/pit.h | 29 +++++++ src/post.c | 1 + src/ps2port.c | 8 +- src/serial.c | 12 +-- src/timer.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/usb-ehci.c | 16 ++-- src/usb-hub.c | 8 +- src/usb-ohci.c | 18 ++--- src/usb-uhci.c | 14 ++-- src/util.c | 4 +- src/util.h | 29 ++++--- 23 files changed, 374 insertions(+), 338 deletions(-) create mode 100644 src/pit.h create mode 100644 src/timer.c
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- Makefile | 2 +- src/clock.c | 240 +-------------------------------------------------------- src/csm.c | 1 + src/pit.h | 33 ++++++++ src/post.c | 1 + src/timer.c | 225 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/usb-ehci.c | 1 + src/usb-ohci.c | 1 + src/usb-uhci.c | 1 + src/util.h | 13 ++-- 10 files changed, 273 insertions(+), 245 deletions(-) create mode 100644 src/pit.h create mode 100644 src/timer.c
diff --git a/Makefile b/Makefile index 759bbbb..00ef346 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ OUT=out/
# Source files SRCBOTH=misc.c stacks.c output.c util.c block.c floppy.c ata.c mouse.c \ - kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \ + kbd.c pci.c serial.c timer.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \ pnpbios.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \ usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \ virtio-ring.c virtio-pci.c virtio-blk.c virtio-scsi.c apm.c ahci.c \ diff --git a/src/clock.c b/src/clock.c index 2eedab8..b9a708b 100644 --- a/src/clock.c +++ b/src/clock.c @@ -10,6 +10,7 @@ #include "disk.h" // floppy_tick #include "cmos.h" // inb_cmos #include "pic.h" // pic_eoi1 +#include "pit.h" // PM_SEL_TIMER0 #include "bregs.h" // struct bregs #include "biosvar.h" // GET_GLOBAL #include "usb-hid.h" // usb_check_event @@ -26,213 +27,6 @@ #define RTC_B_DSE 0x01
-// Bits for PORT_PS2_CTRLB -#define PPCB_T2GATE (1<<0) -#define PPCB_SPKR (1<<1) -#define PPCB_T2OUT (1<<5) - -// Bits for PORT_PIT_MODE -#define PM_SEL_TIMER0 (0<<6) -#define PM_SEL_TIMER1 (1<<6) -#define PM_SEL_TIMER2 (2<<6) -#define PM_SEL_READBACK (3<<6) -#define PM_ACCESS_LATCH (0<<4) -#define PM_ACCESS_LOBYTE (1<<4) -#define PM_ACCESS_HIBYTE (2<<4) -#define PM_ACCESS_WORD (3<<4) -#define PM_MODE0 (0<<1) -#define PM_MODE1 (1<<1) -#define PM_MODE2 (2<<1) -#define PM_MODE3 (3<<1) -#define PM_MODE4 (4<<1) -#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 CALIBRATE_COUNT 0x800 // Approx 1.7ms - -u32 cpu_khz VARFSEG; -u8 no_tsc VARFSEG; - -u16 pmtimer_ioport VARFSEG; -u32 pmtimer_wraps VARLOW; -u32 pmtimer_last VARLOW; - -static void -calibrate_tsc(void) -{ - u32 eax, ebx, ecx, edx, cpuid_features = 0; - - if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) { - dprintf(3, "pmtimer already configured; will not calibrate TSC\n"); - return; - } - - 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" - u8 orig = inb(PORT_PS2_CTRLB); - outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB); - /* binary, mode 0, LSB/MSB, Ch 2 */ - outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE); - /* LSB of ticks */ - outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2); - /* MSB of ticks */ - outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2); - - u64 start = rdtscll(); - while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0) - ; - u64 end = rdtscll(); - - // Restore PORT_PS2_CTRLB - outb(orig, PORT_PS2_CTRLB); - - // Store calibrated cpu khz. - 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); - - dprintf(1, "CPU Mhz=%u\n", hz / 1000000); -} - -/* TSC emulation timekeepers */ -u64 TSC_8254 VARLOW; -int Last_TSC_8254 VARLOW; - -static u64 -emulate_tsc(void) -{ - /* read timer 0 current count */ - u64 ret = GET_LOW(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); - int cnt = (inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8)); - int d = GET_LOW(Last_TSC_8254) - cnt; - /* Determine the ticks count from last invocation of this function */ - ret += (d > 0) ? d : (PIT_TICK_INTERVAL + d); - SET_LOW(Last_TSC_8254, cnt); - SET_LOW(TSC_8254, ret); - return ret; -} - -void pmtimer_setup(u16 ioport, u32 khz) -{ - if (!CONFIG_PMTIMER) - return; - dprintf(1, "Using pmtimer, ioport 0x%x, freq %d kHz\n", ioport, khz); - SET_GLOBAL(pmtimer_ioport, ioport); - SET_GLOBAL(cpu_khz, khz); -} - -static u64 pmtimer_get(void) -{ - u16 ioport = GET_GLOBAL(pmtimer_ioport); - u32 wraps = GET_LOW(pmtimer_wraps); - u32 pmtimer = inl(ioport) & 0xffffff; - - if (pmtimer < GET_LOW(pmtimer_last)) { - wraps++; - SET_LOW(pmtimer_wraps, wraps); - } - SET_LOW(pmtimer_last, pmtimer); - - dprintf(9, "pmtimer: %u:%u\n", wraps, pmtimer); - return (u64)wraps << 24 | pmtimer; -} - -static u64 -get_tsc(void) -{ - if (unlikely(GET_GLOBAL(no_tsc))) - return emulate_tsc(); - if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) - return pmtimer_get(); - return rdtscll(); -} - -int -check_tsc(u64 end) -{ - return (s64)(get_tsc() - end) > 0; -} - -static void -tscdelay(u64 diff) -{ - u64 start = get_tsc(); - u64 end = start + diff; - while (!check_tsc(end)) - cpu_relax(); -} - -static void -tscsleep(u64 diff) -{ - u64 start = get_tsc(); - u64 end = start + diff; - while (!check_tsc(end)) - yield(); -} - -void ndelay(u32 count) { - tscdelay(count * GET_GLOBAL(cpu_khz) / 1000000); -} -void udelay(u32 count) { - tscdelay(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); -} -void usleep(u32 count) { - tscsleep(count * GET_GLOBAL(cpu_khz) / 1000); -} -void msleep(u32 count) { - tscsleep(count * GET_GLOBAL(cpu_khz)); -} - -// Return the TSC value that is 'msecs' time in the future. -u64 -calc_future_tsc(u32 msecs) -{ - u32 khz = GET_GLOBAL(cpu_khz); - return get_tsc() + ((u64)khz * msecs); -} -u64 -calc_future_tsc_usec(u32 usecs) -{ - u32 khz = GET_GLOBAL(cpu_khz); - return get_tsc() + ((u64)(khz/1000) * usecs); -} - - /**************************************************************** * Init ****************************************************************/ @@ -290,10 +84,9 @@ bcd2bin(u8 val) u8 Century VARLOW;
void -timer_setup(void) +clock_setup(void) { dprintf(3, "init timer\n"); - calibrate_tsc(); pit_setup();
rtc_setup(); @@ -326,35 +119,6 @@ timer_setup(void) * 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) diff --git a/src/csm.c b/src/csm.c index 2886bba..e855d2c 100644 --- a/src/csm.c +++ b/src/csm.c @@ -170,6 +170,7 @@ handle_csm_0002(struct bregs *regs) bda->hdcount = 0;
timer_setup(); + clock_setup(); device_hardware_setup(); wait_threads(); interactive_bootmenu(); diff --git a/src/pit.h b/src/pit.h new file mode 100644 index 0000000..6d58895 --- /dev/null +++ b/src/pit.h @@ -0,0 +1,33 @@ +// Definitions for the Intel 8253 Programmable Interrupt Timer (PIT). +#ifndef __PIT_H +#define __PIT_H + +#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) +#define PM_SEL_TIMER2 (2<<6) +#define PM_SEL_READBACK (3<<6) +#define PM_ACCESS_LATCH (0<<4) +#define PM_ACCESS_LOBYTE (1<<4) +#define PM_ACCESS_HIBYTE (2<<4) +#define PM_ACCESS_WORD (3<<4) +#define PM_MODE0 (0<<1) +#define PM_MODE1 (1<<1) +#define PM_MODE2 (2<<1) +#define PM_MODE3 (3<<1) +#define PM_MODE4 (4<<1) +#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) + +#endif // pit.h diff --git a/src/post.c b/src/post.c index ff201fa..d706d4f 100644 --- a/src/post.c +++ b/src/post.c @@ -158,6 +158,7 @@ platform_hardware_setup(void) pic_setup(); mathcp_setup(); timer_setup(); + clock_setup();
// Platform specific setup qemu_platform_setup(); diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..a491ef6 --- /dev/null +++ b/src/timer.c @@ -0,0 +1,225 @@ +// Internal timer support. +// +// Copyright (C) 2008-2013 Kevin O'Connor kevin@koconnor.net +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "util.h" // dprintf +#include "pit.h" // PM_SEL_TIMER0 +#include "ioport.h" // PORT_PIT_MODE +#include "config.h" // CONFIG_* +#include "biosvar.h" // GET_LOW + +// Bits for PORT_PS2_CTRLB +#define PPCB_T2GATE (1<<0) +#define PPCB_SPKR (1<<1) +#define PPCB_T2OUT (1<<5) + + +/**************************************************************** + * TSC timer + ****************************************************************/ + +#define CALIBRATE_COUNT 0x800 // Approx 1.7ms + +u32 cpu_khz VARFSEG; +u8 no_tsc VARFSEG; + +u16 pmtimer_ioport VARFSEG; +u32 pmtimer_wraps VARLOW; +u32 pmtimer_last VARLOW; + +void +timer_setup(void) +{ + u32 eax, ebx, ecx, edx, cpuid_features = 0; + + if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) { + dprintf(3, "pmtimer already configured; will not calibrate TSC\n"); + return; + } + + 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" + u8 orig = inb(PORT_PS2_CTRLB); + outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB); + /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE); + /* LSB of ticks */ + outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2); + /* MSB of ticks */ + outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2); + + u64 start = rdtscll(); + while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0) + ; + u64 end = rdtscll(); + + // Restore PORT_PS2_CTRLB + outb(orig, PORT_PS2_CTRLB); + + // Store calibrated cpu khz. + 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); + + dprintf(1, "CPU Mhz=%u\n", hz / 1000000); +} + +/* TSC emulation timekeepers */ +u64 TSC_8254 VARLOW; +int Last_TSC_8254 VARLOW; + +static u64 +emulate_tsc(void) +{ + /* read timer 0 current count */ + u64 ret = GET_LOW(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); + int cnt = (inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8)); + int d = GET_LOW(Last_TSC_8254) - cnt; + /* Determine the ticks count from last invocation of this function */ + ret += (d > 0) ? d : (PIT_TICK_INTERVAL + d); + SET_LOW(Last_TSC_8254, cnt); + SET_LOW(TSC_8254, ret); + return ret; +} + +void pmtimer_setup(u16 ioport, u32 khz) +{ + if (!CONFIG_PMTIMER) + return; + dprintf(1, "Using pmtimer, ioport 0x%x, freq %d kHz\n", ioport, khz); + SET_GLOBAL(pmtimer_ioport, ioport); + SET_GLOBAL(cpu_khz, khz); +} + +static u64 pmtimer_get(void) +{ + u16 ioport = GET_GLOBAL(pmtimer_ioport); + u32 wraps = GET_LOW(pmtimer_wraps); + u32 pmtimer = inl(ioport) & 0xffffff; + + if (pmtimer < GET_LOW(pmtimer_last)) { + wraps++; + SET_LOW(pmtimer_wraps, wraps); + } + SET_LOW(pmtimer_last, pmtimer); + + dprintf(9, "pmtimer: %u:%u\n", wraps, pmtimer); + return (u64)wraps << 24 | pmtimer; +} + +static u64 +get_tsc(void) +{ + if (unlikely(GET_GLOBAL(no_tsc))) + return emulate_tsc(); + if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) + return pmtimer_get(); + return rdtscll(); +} + +int +check_tsc(u64 end) +{ + return (s64)(get_tsc() - end) > 0; +} + +static void +tscdelay(u64 diff) +{ + u64 start = get_tsc(); + u64 end = start + diff; + while (!check_tsc(end)) + cpu_relax(); +} + +static void +tscsleep(u64 diff) +{ + u64 start = get_tsc(); + u64 end = start + diff; + while (!check_tsc(end)) + yield(); +} + +void ndelay(u32 count) { + tscdelay(count * GET_GLOBAL(cpu_khz) / 1000000); +} +void udelay(u32 count) { + tscdelay(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); +} +void usleep(u32 count) { + tscsleep(count * GET_GLOBAL(cpu_khz) / 1000); +} +void msleep(u32 count) { + tscsleep(count * GET_GLOBAL(cpu_khz)); +} + +// Return the TSC value that is 'msecs' time in the future. +u64 +calc_future_tsc(u32 msecs) +{ + u32 khz = GET_GLOBAL(cpu_khz); + return get_tsc() + ((u64)khz * msecs); +} +u64 +calc_future_tsc_usec(u32 usecs) +{ + u32 khz = GET_GLOBAL(cpu_khz); + return get_tsc() + ((u64)(khz/1000) * usecs); +} + + +/**************************************************************** + * IRQ based timer + ****************************************************************/ + +// 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)); +} diff --git a/src/usb-ehci.c b/src/usb-ehci.c index 144dec4..e1e659b 100644 --- a/src/usb-ehci.c +++ b/src/usb-ehci.c @@ -15,6 +15,7 @@ #include "biosvar.h" // GET_LOWFLAT #include "usb-uhci.h" // uhci_setup #include "usb-ohci.h" // ohci_setup +#include "pit.h" // PIT_TICK_RATE
struct usb_ehci_s { struct usb_s usb; diff --git a/src/usb-ohci.c b/src/usb-ohci.c index ef6a52c..f3a3b3b 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -11,6 +11,7 @@ #include "pci_regs.h" // PCI_BASE_ADDRESS_0 #include "usb.h" // struct usb_s #include "biosvar.h" // GET_LOWFLAT +#include "pit.h" // PIT_TICK_RATE
#define FIT (1 << 31)
diff --git a/src/usb-uhci.c b/src/usb-uhci.c index 4098bf2..e77f5c1 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -12,6 +12,7 @@ #include "pci_regs.h" // PCI_BASE_ADDRESS_4 #include "usb.h" // struct usb_s #include "biosvar.h" // GET_LOWFLAT +#include "pit.h" // PIT_TICK_RATE
struct usb_uhci_s { struct usb_s usb; diff --git a/src/util.h b/src/util.h index 8bde0b2..19733cd 100644 --- a/src/util.h +++ b/src/util.h @@ -275,8 +275,13 @@ void serial_setup(void); void lpt_setup(void);
// clock.c -#define PIT_TICK_RATE 1193180 // Underlying HZ of PIT -#define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer +void clock_setup(void); +void handle_1583(struct bregs *regs); +void handle_1586(struct bregs *regs); +void useRTC(void); +void releaseRTC(void); + +// timer.c void pmtimer_setup(u16 ioport, u32 khz); int check_tsc(u64 end); void timer_setup(void); @@ -291,10 +296,6 @@ u64 calc_future_tsc_usec(u32 usecs); u32 calc_future_timer_ticks(u32 count); u32 calc_future_timer(u32 msecs); int check_timer(u32 end); -void handle_1583(struct bregs *regs); -void handle_1586(struct bregs *regs); -void useRTC(void); -void releaseRTC(void);
// apm.c void apm_shutdown(void);
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/acpi.c | 2 +- src/pciinit.c | 7 ++----- src/pit.h | 3 +++ src/timer.c | 3 ++- src/util.h | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/src/acpi.c b/src/acpi.c index 3699898..365cc46 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -732,7 +732,7 @@ find_acpi_features(void) u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk); dprintf(4, "pm_tmr_blk=%x\n", pm_tmr); if (pm_tmr) - pmtimer_setup(pm_tmr, 3579); + pmtimer_setup(pm_tmr);
// Theoretically we should check the 'reset_reg_sup' flag, but Windows // doesn't and thus nobody seems to *set* it. If the table is large enough diff --git a/src/pciinit.c b/src/pciinit.c index 8370b96..6d7f8fa 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -16,9 +16,6 @@ #include "dev-q35.h" // Q35_HOST_BRIDGE_PCIEXBAR_ADDR #include "list.h" // struct hlist_node
-/* PM Timer ticks per second (HZ) */ -#define PM_TIMER_FREQUENCY 3579545 - #define PCI_DEVICE_MEM_MIN 0x1000 #define PCI_BRIDGE_IO_MIN 0x1000 #define PCI_BRIDGE_MEM_MIN 0x100000 @@ -194,7 +191,7 @@ void mch_isa_bridge_setup(struct pci_device *dev, void *arg) /* acpi enable, SCI: IRQ9 000b = irq9*/ pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_ACPI_EN);
- pmtimer_setup(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000); + pmtimer_setup(PORT_ACPI_PM_BASE + 0x08); }
static void storage_ide_setup(struct pci_device *pci, void *arg) @@ -238,7 +235,7 @@ static void piix4_pm_setup(struct pci_device *pci, void *arg) pci_config_writel(bdf, 0x90, PORT_SMB_BASE | 1); pci_config_writeb(bdf, 0xd2, 0x09); /* enable SMBus io space */
- pmtimer_setup(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000); + pmtimer_setup(PORT_ACPI_PM_BASE + 0x08); }
/* ICH9 SMBUS */ diff --git a/src/pit.h b/src/pit.h index 6d58895..7b5e5e8 100644 --- a/src/pit.h +++ b/src/pit.h @@ -2,6 +2,9 @@ #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) diff --git a/src/timer.c b/src/timer.c index a491ef6..87df97b 100644 --- a/src/timer.c +++ b/src/timer.c @@ -99,10 +99,11 @@ emulate_tsc(void) return ret; }
-void pmtimer_setup(u16 ioport, u32 khz) +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); diff --git a/src/util.h b/src/util.h index 19733cd..15982e2 100644 --- a/src/util.h +++ b/src/util.h @@ -282,7 +282,7 @@ void useRTC(void); void releaseRTC(void);
// timer.c -void pmtimer_setup(u16 ioport, u32 khz); +void pmtimer_setup(u16 ioport); int check_tsc(u64 end); void timer_setup(void); void ndelay(u32 count);
Add ticks_to_ms() and ticks_from_ms() helpers.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/clock.c | 3 +-- src/timer.c | 19 ++++++++++++++++--- src/usb-ehci.c | 3 +-- src/usb-ohci.c | 3 +-- src/usb-uhci.c | 3 +-- src/util.h | 2 ++ 6 files changed, 22 insertions(+), 11 deletions(-)
diff --git a/src/clock.c b/src/clock.c index b9a708b..2f2ca07 100644 --- a/src/clock.c +++ b/src/clock.c @@ -94,8 +94,7 @@ clock_setup(void) u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS)); u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES)); u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS)); - u32 ticks = (hours * 60 + minutes) * 60 + seconds; - ticks = ((u64)ticks * PIT_TICK_RATE) / PIT_TICK_INTERVAL; + u32 ticks = ticks_from_ms(((hours * 60 + minutes) * 60 + seconds) * 1000); SET_BDA(timer_counter, ticks);
// Setup Century storage diff --git a/src/timer.c b/src/timer.c index 87df97b..0fd0194 100644 --- a/src/timer.c +++ b/src/timer.c @@ -198,6 +198,21 @@ calc_future_tsc_usec(u32 usecs) * IRQ based timer ****************************************************************/
+// Return the number of milliseconds in 'ticks' number of timer irqs. +u32 +ticks_to_ms(u32 ticks) +{ + return DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * ticks, PIT_TICK_RATE); +} + +// 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); +} + // Calculate the timer value at 'count' number of full timer ticks in // the future. u32 @@ -212,9 +227,7 @@ 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); + return calc_future_timer_ticks(ticks_from_ms(msecs)); }
// Check if the given timer value has passed. diff --git a/src/usb-ehci.c b/src/usb-ehci.c index e1e659b..c1638e4 100644 --- a/src/usb-ehci.c +++ b/src/usb-ehci.c @@ -15,7 +15,6 @@ #include "biosvar.h" // GET_LOWFLAT #include "usb-uhci.h" // uhci_setup #include "usb-ohci.h" // ohci_setup -#include "pit.h" // PIT_TICK_RATE
struct usb_ehci_s { struct usb_s usb; @@ -427,7 +426,7 @@ ehci_alloc_intr_pipe(struct usbdevice_s *usbdev int maxpacket = epdesc->wMaxPacketSize; // Determine number of entries needed for 2 timer ticks. int ms = 1<<frameexp; - int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms); + int count = DIV_ROUND_UP(ticks_to_ms(2), ms); struct ehci_pipe *pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); struct ehci_qtd *tds = memalign_low(EHCI_QTD_ALIGN, sizeof(*tds) * count); void *data = malloc_low(maxpacket * count); diff --git a/src/usb-ohci.c b/src/usb-ohci.c index f3a3b3b..b2e1d7f 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -11,7 +11,6 @@ #include "pci_regs.h" // PCI_BASE_ADDRESS_0 #include "usb.h" // struct usb_s #include "biosvar.h" // GET_LOWFLAT -#include "pit.h" // PIT_TICK_RATE
#define FIT (1 << 31)
@@ -328,7 +327,7 @@ ohci_alloc_intr_pipe(struct usbdevice_s *usbdev int maxpacket = epdesc->wMaxPacketSize; // Determine number of entries needed for 2 timer ticks. int ms = 1<<frameexp; - int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms)+1; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms) + 1; struct ohci_pipe *pipe = malloc_low(sizeof(*pipe)); struct ohci_td *tds = malloc_low(sizeof(*tds) * count); void *data = malloc_low(maxpacket * count); diff --git a/src/usb-uhci.c b/src/usb-uhci.c index e77f5c1..5fa05a8 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -12,7 +12,6 @@ #include "pci_regs.h" // PCI_BASE_ADDRESS_4 #include "usb.h" // struct usb_s #include "biosvar.h" // GET_LOWFLAT -#include "pit.h" // PIT_TICK_RATE
struct usb_uhci_s { struct usb_s usb; @@ -283,7 +282,7 @@ uhci_alloc_intr_pipe(struct usbdevice_s *usbdev int maxpacket = epdesc->wMaxPacketSize; // Determine number of entries needed for 2 timer ticks. int ms = 1<<frameexp; - int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms); + int count = DIV_ROUND_UP(ticks_to_ms(2), ms); count = ALIGN(count, 2); struct uhci_pipe *pipe = malloc_low(sizeof(*pipe)); struct uhci_td *tds = malloc_low(sizeof(*tds) * count); diff --git a/src/util.h b/src/util.h index 15982e2..a91b7f8 100644 --- a/src/util.h +++ b/src/util.h @@ -282,6 +282,8 @@ void useRTC(void); void releaseRTC(void);
// timer.c +u32 ticks_to_ms(u32 ticks); +u32 ticks_from_ms(u32 ms); void pmtimer_setup(u16 ioport); int check_tsc(u64 end); void timer_setup(void);
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
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/timer.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/timer.c b/src/timer.c index d659e94..c92ebc2 100644 --- a/src/timer.c +++ b/src/timer.c @@ -26,7 +26,7 @@
#define CALIBRATE_COUNT 0x800 // Approx 1.7ms
-u32 cpu_khz VARFSEG; +u32 TimerKHz VARFSEG; u8 no_tsc VARFSEG;
u16 pmtimer_ioport VARFSEG; @@ -49,7 +49,7 @@ timer_setup(void)
if (!(cpuid_features & CPUID_TSC)) { no_tsc = 1; - cpu_khz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); dprintf(3, "386/486 class CPU. Using TSC emulation\n"); return; } @@ -77,7 +77,7 @@ timer_setup(void) dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n" , (u32)start, (u32)end, (u32)diff); u32 t = DIV_ROUND_UP(diff * PMTIMER_HZ, CALIBRATE_COUNT); - cpu_khz = DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT); + TimerKHz = DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT);
dprintf(1, "CPU Mhz=%u\n", t / (1000000 * PMTIMER_TO_PIT)); } @@ -109,7 +109,7 @@ void pmtimer_setup(u16 ioport) return; dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport); pmtimer_ioport = ioport; - cpu_khz = DIV_ROUND_UP(PMTIMER_HZ, 1000); + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000); }
static u64 pmtimer_get(void) @@ -163,36 +163,36 @@ tscsleep(u64 diff) }
void ndelay(u32 count) { - tscdelay(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000000)); + tscdelay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); } void udelay(u32 count) { - tscdelay(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000)); + tscdelay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); } void mdelay(u32 count) { - tscdelay(count * GET_GLOBAL(cpu_khz)); + tscdelay(count * GET_GLOBAL(TimerKHz)); }
void nsleep(u32 count) { - tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000000)); + tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); } void usleep(u32 count) { - tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(cpu_khz), 1000)); + tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); } void msleep(u32 count) { - tscsleep(count * GET_GLOBAL(cpu_khz)); + tscsleep(count * GET_GLOBAL(TimerKHz)); }
// Return the TSC value that is 'msecs' time in the future. u64 calc_future_tsc(u32 msecs) { - u32 khz = GET_GLOBAL(cpu_khz); + u32 khz = GET_GLOBAL(TimerKHz); return get_tsc() + ((u64)khz * msecs); } u64 calc_future_tsc_usec(u32 usecs) { - u32 khz = GET_GLOBAL(cpu_khz); + u32 khz = GET_GLOBAL(TimerKHz); return get_tsc() + ((u64)DIV_ROUND_UP(khz, 1000) * usecs); }
The time-stamp-counter has a higher accuracy than is needed in SeaBIOS. Down shift it to ensure it safely fits in a 32bit variable.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/timer.c | 50 ++++++++++++++++++++++++++++---------------------- src/util.h | 6 +++--- 2 files changed, 31 insertions(+), 25 deletions(-)
diff --git a/src/timer.c b/src/timer.c index c92ebc2..7fa0610 100644 --- a/src/timer.c +++ b/src/timer.c @@ -33,6 +33,8 @@ u16 pmtimer_ioport VARFSEG; u32 pmtimer_wraps VARLOW; u32 pmtimer_last VARLOW;
+u8 ShiftTSC VARFSEG; + void timer_setup(void) { @@ -76,21 +78,25 @@ 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 t = DIV_ROUND_UP(diff * PMTIMER_HZ, CALIBRATE_COUNT); - TimerKHz = DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT); + u64 t = DIV_ROUND_UP(diff * PMTIMER_HZ, CALIBRATE_COUNT); + while (t >= (1<<24)) { + ShiftTSC++; + t >>= 1; + } + TimerKHz = DIV_ROUND_UP((u32)t, 1000 * PMTIMER_TO_PIT);
- dprintf(1, "CPU Mhz=%u\n", t / (1000000 * PMTIMER_TO_PIT)); + dprintf(1, "CPU Mhz=%u\n", (TimerKHz << ShiftTSC) / 1000); }
/* TSC emulation timekeepers */ -u64 TSC_8254 VARLOW; +u32 TSC_8254 VARLOW; int Last_TSC_8254 VARLOW;
-static u64 +static u32 emulate_tsc(void) { /* read timer 0 current count */ - u64 ret = GET_LOW(TSC_8254); + u32 ret = GET_LOW(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); @@ -112,7 +118,7 @@ void pmtimer_setup(u16 ioport) TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000); }
-static u64 pmtimer_get(void) +static u32 pmtimer_get(void) { u16 ioport = GET_GLOBAL(pmtimer_ioport); u32 wraps = GET_LOW(pmtimer_wraps); @@ -125,39 +131,39 @@ static u64 pmtimer_get(void) SET_LOW(pmtimer_last, pmtimer);
dprintf(9, "pmtimer: %u:%u\n", wraps, pmtimer); - return (u64)wraps << 24 | pmtimer; + return wraps << 24 | pmtimer; }
-static u64 +static u32 get_tsc(void) { if (unlikely(GET_GLOBAL(no_tsc))) return emulate_tsc(); if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) return pmtimer_get(); - return rdtscll(); + return rdtscll() >> GET_GLOBAL(ShiftTSC); }
int -check_tsc(u64 end) +check_tsc(u32 end) { - return (s64)(get_tsc() - end) > 0; + return (s32)(get_tsc() - end) > 0; }
static void -tscdelay(u64 diff) +tscdelay(u32 diff) { - u64 start = get_tsc(); - u64 end = start + diff; + u32 start = get_tsc(); + u32 end = start + diff; while (!check_tsc(end)) cpu_relax(); }
static void -tscsleep(u64 diff) +tscsleep(u32 diff) { - u64 start = get_tsc(); - u64 end = start + diff; + u32 start = get_tsc(); + u32 end = start + diff; while (!check_tsc(end)) yield(); } @@ -183,17 +189,17 @@ void msleep(u32 count) { }
// Return the TSC value that is 'msecs' time in the future. -u64 +u32 calc_future_tsc(u32 msecs) { u32 khz = GET_GLOBAL(TimerKHz); - return get_tsc() + ((u64)khz * msecs); + return get_tsc() + (khz * msecs); } -u64 +u32 calc_future_tsc_usec(u32 usecs) { u32 khz = GET_GLOBAL(TimerKHz); - return get_tsc() + ((u64)DIV_ROUND_UP(khz, 1000) * usecs); + return get_tsc() + DIV_ROUND_UP(khz * usecs, 1000); }
diff --git a/src/util.h b/src/util.h index a91b7f8..bc2fd00 100644 --- a/src/util.h +++ b/src/util.h @@ -285,7 +285,7 @@ void releaseRTC(void); u32 ticks_to_ms(u32 ticks); u32 ticks_from_ms(u32 ms); void pmtimer_setup(u16 ioport); -int check_tsc(u64 end); +int check_tsc(u32 end); void timer_setup(void); void ndelay(u32 count); void udelay(u32 count); @@ -293,8 +293,8 @@ void mdelay(u32 count); void nsleep(u32 count); void usleep(u32 count); void msleep(u32 count); -u64 calc_future_tsc(u32 msecs); -u64 calc_future_tsc_usec(u32 usecs); +u32 calc_future_tsc(u32 msecs); +u32 calc_future_tsc_usec(u32 usecs); u32 calc_future_timer_ticks(u32 count); u32 calc_future_timer(u32 msecs); int check_timer(u32 end);
Rename functions to be more consistent and so they are not confused with the normal timer functions.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/boot.c | 4 ++-- src/serial.c | 12 ++++++------ src/timer.c | 8 ++++---- src/util.c | 4 ++-- src/util.h | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/boot.c b/src/boot.c index 2fce315..d421a65 100644 --- a/src/boot.c +++ b/src/boot.c @@ -635,9 +635,9 @@ boot_fail(void) printf("No bootable device. Retrying in %d seconds.\n" , BootRetryTime/1000); // Wait for 'BootRetryTime' milliseconds and then reboot. - u32 end = calc_future_timer(BootRetryTime); + u32 end = irqtimer_calc(BootRetryTime); for (;;) { - if (BootRetryTime != (u32)-1 && check_timer(end)) + if (BootRetryTime != (u32)-1 && irqtimer_check(end)) break; yield_toirq(); } diff --git a/src/serial.c b/src/serial.c index 153f82a..b4a0a53 100644 --- a/src/serial.c +++ b/src/serial.c @@ -91,7 +91,7 @@ handle_1401(struct bregs *regs) u16 addr = getComAddr(regs); if (!addr) return; - u32 end = calc_future_timer_ticks(GET_BDA(com_timeout[regs->dx])); + u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx])); for (;;) { u8 lsr = inb(addr+SEROFF_LSR); if ((lsr & 0x60) == 0x60) { @@ -101,7 +101,7 @@ handle_1401(struct bregs *regs) regs->ah = lsr; break; } - if (check_timer(end)) { + if (irqtimer_check(end)) { // Timed out - can't write data. regs->ah = lsr | 0x80; break; @@ -118,7 +118,7 @@ handle_1402(struct bregs *regs) u16 addr = getComAddr(regs); if (!addr) return; - u32 end = calc_future_timer_ticks(GET_BDA(com_timeout[regs->dx])); + u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx])); for (;;) { u8 lsr = inb(addr+SEROFF_LSR); if (lsr & 0x01) { @@ -127,7 +127,7 @@ handle_1402(struct bregs *regs) regs->ah = lsr; break; } - if (check_timer(end)) { + if (irqtimer_check(end)) { // Timed out - can't read data. regs->ah = lsr | 0x80; break; @@ -234,7 +234,7 @@ handle_1700(struct bregs *regs) if (!addr) return;
- u32 end = calc_future_timer_ticks(GET_BDA(lpt_timeout[regs->dx])); + u32 end = irqtimer_calc_ticks(GET_BDA(lpt_timeout[regs->dx]));
outb(regs->al, addr); u8 val8 = inb(addr+2); @@ -249,7 +249,7 @@ handle_1700(struct bregs *regs) regs->ah = v ^ 0x48; break; } - if (check_timer(end)) { + if (irqtimer_check(end)) { // Timeout regs->ah = (v ^ 0x48) | 0x01; break; diff --git a/src/timer.c b/src/timer.c index 7fa0610..4548abb 100644 --- a/src/timer.c +++ b/src/timer.c @@ -226,23 +226,23 @@ ticks_from_ms(u32 ms) // Calculate the timer value at 'count' number of full timer ticks in // the future. u32 -calc_future_timer_ticks(u32 count) +irqtimer_calc_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) +irqtimer_calc(u32 msecs) { if (!msecs) return GET_BDA(timer_counter); - return calc_future_timer_ticks(ticks_from_ms(msecs)); + return irqtimer_calc_ticks(ticks_from_ms(msecs)); }
// Check if the given timer value has passed. int -check_timer(u32 end) +irqtimer_check(u32 end) { return (((GET_BDA(timer_counter) + TICKS_PER_DAY - end) % TICKS_PER_DAY) < (TICKS_PER_DAY/2)); diff --git a/src/util.c b/src/util.c index ccf84b0..ffabd1c 100644 --- a/src/util.c +++ b/src/util.c @@ -281,11 +281,11 @@ get_raw_keystroke(void) int get_keystroke(int msec) { - u32 end = calc_future_timer(msec); + u32 end = irqtimer_calc(msec); for (;;) { if (check_for_keystroke()) return get_raw_keystroke(); - if (check_timer(end)) + if (irqtimer_check(end)) return -1; yield_toirq(); } diff --git a/src/util.h b/src/util.h index bc2fd00..10c2364 100644 --- a/src/util.h +++ b/src/util.h @@ -295,9 +295,9 @@ void usleep(u32 count); void msleep(u32 count); u32 calc_future_tsc(u32 msecs); u32 calc_future_tsc_usec(u32 usecs); -u32 calc_future_timer_ticks(u32 count); -u32 calc_future_timer(u32 msecs); -int check_timer(u32 end); +u32 irqtimer_calc_ticks(u32 count); +u32 irqtimer_calc(u32 msecs); +int irqtimer_check(u32 end);
// apm.c void apm_shutdown(void);
Rename the check_tsc() function to timer_check(). The CPU TSC is often not the basis of the timer, so use a more appropriate name.
Convert all callers that were using u64 for the timers to use u32.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/ahci.c | 19 ++++++++----------- src/ata.c | 18 +++++++++--------- src/blockcmd.c | 6 +++--- src/clock.c | 4 ++-- src/floppy.c | 8 ++++---- src/megasas.c | 10 ++++------ src/ps2port.c | 8 ++++---- src/timer.c | 48 ++++++++++++++++++++++++------------------------ src/usb-ehci.c | 14 +++++++------- src/usb-hub.c | 8 ++++---- src/usb-ohci.c | 16 ++++++++-------- src/usb-uhci.c | 12 ++++++------ src/util.h | 12 ++++++------ 13 files changed, 89 insertions(+), 94 deletions(-)
diff --git a/src/ahci.c b/src/ahci.c index 879a991..0e99d14 100644 --- a/src/ahci.c +++ b/src/ahci.c @@ -113,7 +113,6 @@ static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi, struct ahci_fis_s *fis = GET_GLOBAL(port->fis); struct ahci_list_s *list = GET_GLOBAL(port->list); u32 pnr = GET_GLOBAL(port->pnr); - u64 end;
SET_LOWFLAT(cmd->fis.reg, 0x27); SET_LOWFLAT(cmd->fis.pmp_type, (1 << 7)); /* cmd fis */ @@ -137,7 +136,7 @@ static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi, ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1); ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1);
- end = calc_future_tsc(AHCI_REQUEST_TIMEOUT); + u32 end = timer_calc(AHCI_REQUEST_TIMEOUT); do { for (;;) { intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); @@ -154,7 +153,7 @@ static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi, break; } } - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return -1; } @@ -329,10 +328,9 @@ static void ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr) { u32 val; - u64 end;
/* disable FIS + CMD */ - end = calc_future_tsc(AHCI_RESET_TIMEOUT); + u32 end = timer_calc(AHCI_RESET_TIMEOUT); for (;;) { val = ahci_port_readl(ctrl, pnr, PORT_CMD); if (!(val & (PORT_CMD_FIS_RX | PORT_CMD_START | @@ -340,7 +338,7 @@ ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr) break; val &= ~(PORT_CMD_FIS_RX | PORT_CMD_START); ahci_port_writel(ctrl, pnr, PORT_CMD, val); - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); break; } @@ -429,7 +427,6 @@ static int ahci_port_setup(struct ahci_port_s *port) char model[MAXMODEL+1]; u16 buffer[256]; u32 cmd, stat, err, tf; - u64 end; int rc;
/* enable FIS recv */ @@ -440,14 +437,14 @@ static int ahci_port_setup(struct ahci_port_s *port) /* spin up */ cmd |= PORT_CMD_SPIN_UP; ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); - end = calc_future_tsc(AHCI_LINK_TIMEOUT); + u32 end = timer_calc(AHCI_LINK_TIMEOUT); for (;;) { stat = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT); if ((stat & 0x07) == 0x03) { dprintf(2, "AHCI/%d: link up\n", port->pnr); break; } - if (check_tsc(end)) { + if (timer_check(end)) { dprintf(2, "AHCI/%d: link down\n", port->pnr); return -1; } @@ -460,13 +457,13 @@ static int ahci_port_setup(struct ahci_port_s *port) ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, err);
/* wait for device becoming ready */ - end = calc_future_tsc(AHCI_REQUEST_TIMEOUT); + end = timer_calc(AHCI_REQUEST_TIMEOUT); for (;;) { tf = ahci_port_readl(ctrl, pnr, PORT_TFDATA); if (!(tf & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ))) break; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); dprintf(1, "AHCI/%d: device not ready (tf 0x%x)\n", port->pnr, tf); return -1; diff --git a/src/ata.c b/src/ata.c index 55bfa9a..e319aa9 100644 --- a/src/ata.c +++ b/src/ata.c @@ -31,12 +31,12 @@ static inline int await_ide(u8 mask, u8 flags, u16 base, u16 timeout) { - u64 end = calc_future_tsc(timeout); + u32 end = timer_calc(timeout); for (;;) { u8 status = inb(base+ATA_CB_STAT); if ((status & mask) == flags) return status; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return -1; } @@ -98,7 +98,7 @@ ata_reset(struct atadrive_s *adrive_g) goto done; if (slave) { // Change device. - u64 end = calc_future_tsc(IDE_TIMEOUT); + u32 end = timer_calc(IDE_TIMEOUT); for (;;) { outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH); status = ndelay_await_not_bsy(iobase1); @@ -107,7 +107,7 @@ ata_reset(struct atadrive_s *adrive_g) if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1) break; // Change drive request failed to take effect - retry. - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); goto done; } @@ -421,14 +421,14 @@ ata_dma_transfer(struct disk_op_s *op) u8 oldcmd = inb(iomaster + BM_CMD); outb(oldcmd | BM_CMD_START, iomaster + BM_CMD);
- u64 end = calc_future_tsc(IDE_TIMEOUT); + u32 end = timer_calc(IDE_TIMEOUT); u8 status; for (;;) { status = inb(iomaster + BM_STATUS); if (status & BM_STATUS_IRQ) break; // Transfer in progress - if (check_tsc(end)) { + if (timer_check(end)) { // Timeout. warn_timeout(); break; @@ -807,7 +807,7 @@ init_drive_ata(struct atadrive_s *dummy, u16 *buffer) return adrive_g; }
-static u64 SpinupEnd; +static u32 SpinupEnd;
// Wait for non-busy status and check for "floating bus" condition. static int @@ -824,7 +824,7 @@ powerup_await_non_bsy(u16 base) dprintf(4, "powerup IDE floating\n"); return orstatus; } - if (check_tsc(SpinupEnd)) { + if (timer_check(SpinupEnd)) { warn_timeout(); return -1; } @@ -1034,7 +1034,7 @@ ata_setup(void)
dprintf(3, "init hard drives\n");
- SpinupEnd = calc_future_tsc(IDE_TIMEOUT); + SpinupEnd = timer_calc(IDE_TIMEOUT); ata_scan();
SET_BDA(disk_control_byte, 0xc0); diff --git a/src/blockcmd.c b/src/blockcmd.c index c3e4b58..5cd4eb0 100644 --- a/src/blockcmd.c +++ b/src/blockcmd.c @@ -64,9 +64,9 @@ scsi_is_ready(struct disk_op_s *op) * reported by the device. If the device reports "IN PROGRESS", * 30 seconds is added. */ int in_progress = 0; - u64 end = calc_future_tsc(5000); + u32 end = timer_calc(5000); for (;;) { - if (check_tsc(end)) { + if (timer_check(end)) { dprintf(1, "test unit ready failed\n"); return -1; } @@ -92,7 +92,7 @@ scsi_is_ready(struct disk_op_s *op) /* IN PROGRESS OF BECOMING READY */ printf("Waiting for device to detect medium... "); /* Allow 30 seconds more */ - end = calc_future_tsc(30000); + end = timer_calc(30000); in_progress = 1; } } diff --git a/src/clock.c b/src/clock.c index 4b33bb8..5014b60 100644 --- a/src/clock.c +++ b/src/clock.c @@ -44,11 +44,11 @@ rtc_updating(void)
if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0) return 0; - u64 end = calc_future_tsc(15); + u32 end = timer_calc(15); for (;;) { if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0) return 0; - if (check_tsc(end)) + if (timer_check(end)) // update-in-progress never transitioned to 0 return -1; yield(); diff --git a/src/floppy.c b/src/floppy.c index 83dfaf8..37b7092 100644 --- a/src/floppy.c +++ b/src/floppy.c @@ -211,12 +211,12 @@ static int floppy_pio(struct floppy_pio_s *pio) { // Send command to controller. - u64 end = calc_future_tsc(FLOPPY_PIO_TIMEOUT); + u32 end = timer_calc(FLOPPY_PIO_TIMEOUT); int i = 0; for (;;) { u8 sts = inb(PORT_FD_STATUS); if (!(sts & 0x80)) { - if (check_tsc(end)) { + if (timer_check(end)) { floppy_disable_controller(); return DISK_RET_ETIMEOUT; } @@ -239,12 +239,12 @@ floppy_pio(struct floppy_pio_s *pio) }
// Read response from controller. - end = calc_future_tsc(FLOPPY_PIO_TIMEOUT); + end = timer_calc(FLOPPY_PIO_TIMEOUT); i = 0; for (;;) { u8 sts = inb(PORT_FD_STATUS); if (!(sts & 0x80)) { - if (check_tsc(end)) { + if (timer_check(end)) { floppy_disable_controller(); return DISK_RET_ETIMEOUT; } diff --git a/src/megasas.c b/src/megasas.c index 170700f..f4eeba0 100644 --- a/src/megasas.c +++ b/src/megasas.c @@ -118,7 +118,6 @@ static int megasas_fire_cmd(u16 pci_id, u32 ioaddr, u32 frame_addr = (u32)frame; int frame_count = 1; u8 cmd_state; - u64 end;
dprintf(2, "Frame 0x%x\n", frame_addr); if (pci_id == PCI_DEVICE_ID_LSI_SAS2004 || @@ -133,13 +132,13 @@ static int megasas_fire_cmd(u16 pci_id, u32 ioaddr, outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQP); }
- end = calc_future_tsc(MEGASAS_POLL_TIMEOUT); + u32 end = timer_calc(MEGASAS_POLL_TIMEOUT); do { for (;;) { cmd_state = GET_LOWFLAT(frame->cmd_status); if (cmd_state != 0xff) break; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return -1; } @@ -270,7 +269,6 @@ static void megasas_scan_target(struct pci_device *pci, u32 iobase) static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr) { u32 fw_state = 0, new_state, mfi_flags = 0; - u64 end;
if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || pci->device == PCI_DEVICE_ID_DELL_PERC5) @@ -327,9 +325,9 @@ static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr) return 0; } // The current state should not last longer than poll timeout - end = calc_future_tsc(MEGASAS_POLL_TIMEOUT); + u32 end = timer_calc(MEGASAS_POLL_TIMEOUT); for (;;) { - if (check_tsc(end)) { + if (timer_check(end)) { break; } yield(); diff --git a/src/ps2port.c b/src/ps2port.c index cfcd5eb..4b6c5df 100644 --- a/src/ps2port.c +++ b/src/ps2port.c @@ -153,7 +153,7 @@ i8042_reboot(void) static int ps2_recvbyte(int aux, int needack, int timeout) { - u64 end = calc_future_tsc(timeout); + u32 end = timer_calc(timeout); for (;;) { u8 status = inb(PORT_PS2_STATUS); if (status & I8042_STR_OBF) { @@ -175,7 +175,7 @@ ps2_recvbyte(int aux, int needack, int timeout) dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status); }
- if (check_tsc(end)) { + if (timer_check(end)) { // Don't warn on second byte of a reset if (timeout > 100) warn_timeout(); @@ -450,12 +450,12 @@ ps2_keyboard_setup(void *data) /* ------------------- keyboard side ------------------------*/ /* reset keyboard and self test (keyboard side) */ int spinupdelay = romfile_loadint("etc/ps2-keyboard-spinup", 0); - u64 end = calc_future_tsc(spinupdelay); + u32 end = timer_calc(spinupdelay); for (;;) { ret = ps2_kbd_command(ATKBD_CMD_RESET_BAT, param); if (!ret) break; - if (check_tsc(end)) { + if (timer_check(end)) { if (spinupdelay) warn_timeout(); return; diff --git a/src/timer.c b/src/timer.c index 4548abb..b60a047 100644 --- a/src/timer.c +++ b/src/timer.c @@ -21,7 +21,7 @@
/**************************************************************** - * TSC timer + * Internal timers ****************************************************************/
#define CALIBRATE_COUNT 0x800 // Approx 1.7ms @@ -93,7 +93,7 @@ u32 TSC_8254 VARLOW; int Last_TSC_8254 VARLOW;
static u32 -emulate_tsc(void) +pittimer_read(void) { /* read timer 0 current count */ u32 ret = GET_LOW(TSC_8254); @@ -118,7 +118,7 @@ void pmtimer_setup(u16 ioport) TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000); }
-static u32 pmtimer_get(void) +static u32 pmtimer_read(void) { u16 ioport = GET_GLOBAL(pmtimer_ioport); u32 wraps = GET_LOW(pmtimer_wraps); @@ -135,71 +135,71 @@ static u32 pmtimer_get(void) }
static u32 -get_tsc(void) +timer_read(void) { if (unlikely(GET_GLOBAL(no_tsc))) - return emulate_tsc(); + return pittimer_read(); if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) - return pmtimer_get(); + return pmtimer_read(); return rdtscll() >> GET_GLOBAL(ShiftTSC); }
int -check_tsc(u32 end) +timer_check(u32 end) { - return (s32)(get_tsc() - end) > 0; + return (s32)(timer_read() - end) > 0; }
static void -tscdelay(u32 diff) +timer_delay(u32 diff) { - u32 start = get_tsc(); + u32 start = timer_read(); u32 end = start + diff; - while (!check_tsc(end)) + while (!timer_check(end)) cpu_relax(); }
static void -tscsleep(u32 diff) +timer_sleep(u32 diff) { - u32 start = get_tsc(); + u32 start = timer_read(); u32 end = start + diff; - while (!check_tsc(end)) + while (!timer_check(end)) yield(); }
void ndelay(u32 count) { - tscdelay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); + timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); } void udelay(u32 count) { - tscdelay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); + timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); } void mdelay(u32 count) { - tscdelay(count * GET_GLOBAL(TimerKHz)); + timer_delay(count * GET_GLOBAL(TimerKHz)); }
void nsleep(u32 count) { - tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); + timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); } void usleep(u32 count) { - tscsleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); + timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); } void msleep(u32 count) { - tscsleep(count * GET_GLOBAL(TimerKHz)); + timer_sleep(count * GET_GLOBAL(TimerKHz)); }
// Return the TSC value that is 'msecs' time in the future. u32 -calc_future_tsc(u32 msecs) +timer_calc(u32 msecs) { u32 khz = GET_GLOBAL(TimerKHz); - return get_tsc() + (khz * msecs); + return timer_read() + (khz * msecs); } u32 -calc_future_tsc_usec(u32 usecs) +timer_calc_usec(u32 usecs) { u32 khz = GET_GLOBAL(TimerKHz); - return get_tsc() + DIV_ROUND_UP(khz * usecs, 1000); + return timer_read() + DIV_ROUND_UP(khz * usecs, 1000); }
diff --git a/src/usb-ehci.c b/src/usb-ehci.c index c1638e4..978ca68 100644 --- a/src/usb-ehci.c +++ b/src/usb-ehci.c @@ -189,7 +189,7 @@ ehci_waittick(struct usb_ehci_s *cntl) // Wait for access to "doorbell" barrier(); u32 cmd, sts; - u64 end = calc_future_tsc(100); + u32 end = timer_calc(100); for (;;) { sts = readl(&cntl->regs->usbsts); if (!(sts & STS_IAA)) { @@ -197,7 +197,7 @@ ehci_waittick(struct usb_ehci_s *cntl) if (!(cmd & CMD_IAAD)) break; } - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return; } @@ -210,7 +210,7 @@ ehci_waittick(struct usb_ehci_s *cntl) sts = readl(&cntl->regs->usbsts); if (sts & STS_IAA) break; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return; } @@ -267,12 +267,12 @@ configure_ehci(void *data) // Reset the HC u32 cmd = readl(&cntl->regs->usbcmd); writel(&cntl->regs->usbcmd, (cmd & ~(CMD_ASE | CMD_PSE)) | CMD_HCRESET); - u64 end = calc_future_tsc(250); + u32 end = timer_calc(250); for (;;) { cmd = readl(&cntl->regs->usbcmd); if (!(cmd & CMD_HCRESET)) break; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); goto fail; } @@ -529,13 +529,13 @@ ehci_reset_pipe(struct ehci_pipe *pipe) static int ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, int timeout) { - u64 end = calc_future_tsc(timeout); + u32 end = timer_calc(timeout); u32 status; for (;;) { status = td->token; if (!(status & QTD_STS_ACTIVE)) break; - if (check_tsc(end)) { + if (timer_check(end)) { u32 cur = GET_LOWFLAT(pipe->qh.current); u32 tok = GET_LOWFLAT(pipe->qh.token); u32 next = GET_LOWFLAT(pipe->qh.qtd_next); diff --git a/src/usb-hub.c b/src/usb-hub.c index 894ed7d..0b55d15 100644 --- a/src/usb-hub.c +++ b/src/usb-hub.c @@ -80,7 +80,7 @@ usb_hub_detect(struct usbhub_s *hub, u32 port)
// Check periodically for a device connect. struct usb_port_status sts; - u64 end = calc_future_tsc(USB_TIME_SIGATT); + u32 end = timer_calc(USB_TIME_SIGATT); for (;;) { ret = get_port_status(hub, port, &sts); if (ret) @@ -88,7 +88,7 @@ usb_hub_detect(struct usbhub_s *hub, u32 port) if (sts.wPortStatus & USB_PORT_STAT_CONNECTION) // Device connected. break; - if (check_tsc(end)) + if (timer_check(end)) // No device found. return -1; msleep(5); @@ -122,14 +122,14 @@ usb_hub_reset(struct usbhub_s *hub, u32 port)
// Wait for reset to complete. struct usb_port_status sts; - u64 end = calc_future_tsc(USB_TIME_DRST * 2); + u32 end = timer_calc(USB_TIME_DRST * 2); for (;;) { ret = get_port_status(hub, port, &sts); if (ret) goto fail; if (!(sts.wPortStatus & USB_PORT_STAT_RESET)) break; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); goto fail; } diff --git a/src/usb-ohci.c b/src/usb-ohci.c index b2e1d7f..e5c9fa5 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -62,13 +62,13 @@ ohci_hub_reset(struct usbhub_s *hub, u32 port) struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); writel(&cntl->regs->roothub_portstatus[port], RH_PS_PRS); u32 sts; - u64 end = calc_future_tsc(USB_TIME_DRSTR * 2); + u32 end = timer_calc(USB_TIME_DRSTR * 2); for (;;) { sts = readl(&cntl->regs->roothub_portstatus[port]); if (!(sts & RH_PS_PRS)) // XXX - need to ensure USB_TIME_DRSTR time in reset? break; - if (check_tsc(end)) { + if (timer_check(end)) { // Timeout. warn_timeout(); ohci_hub_disconnect(hub, port); @@ -124,11 +124,11 @@ ohci_waittick(struct usb_ohci_s *cntl) barrier(); struct ohci_hcca *hcca = (void*)cntl->regs->hcca; u32 startframe = hcca->frame_no; - u64 end = calc_future_tsc(1000 * 5); + u32 end = timer_calc(1000 * 5); for (;;) { if (hcca->frame_no != startframe) break; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return; } @@ -181,13 +181,13 @@ start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) msleep(USB_TIME_DRSTR);
// Do software init (min 10us, max 2ms) - u64 end = calc_future_tsc_usec(10); + u32 end = timer_calc_usec(10); writel(&cntl->regs->cmdstatus, OHCI_HCR); for (;;) { u32 status = readl(&cntl->regs->cmdstatus); if (! status & OHCI_HCR) break; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return -1; } @@ -422,11 +422,11 @@ static int wait_ed(struct ohci_ed *ed) { // XXX - 500ms just a guess - u64 end = calc_future_tsc(500); + u32 end = timer_calc(500); for (;;) { if (ed->hwHeadP == ed->hwTailP) return 0; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return -1; } diff --git a/src/usb-uhci.c b/src/usb-uhci.c index 5fa05a8..d8f9b1e 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -111,11 +111,11 @@ uhci_waittick(u16 iobase) { barrier(); u16 startframe = inw(iobase + USBFRNUM); - u64 end = calc_future_tsc(1000 * 5); + u32 end = timer_calc(1000 * 5); for (;;) { if (inw(iobase + USBFRNUM) != startframe) break; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return; } @@ -386,12 +386,12 @@ uhci_alloc_pipe(struct usbdevice_s *usbdev static int wait_pipe(struct uhci_pipe *pipe, int timeout) { - u64 end = calc_future_tsc(timeout); + u32 end = timer_calc(timeout); for (;;) { u32 el_link = GET_LOWFLAT(pipe->qh.element); if (el_link & UHCI_PTR_TERM) return 0; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); u16 iobase = GET_LOWFLAT(pipe->iobase); struct uhci_td *td = (void*)(el_link & ~UHCI_PTR_BITS); @@ -410,13 +410,13 @@ wait_pipe(struct uhci_pipe *pipe, int timeout) static int wait_td(struct uhci_td *td) { - u64 end = calc_future_tsc(5000); // XXX - lookup real time. + u32 end = timer_calc(5000); // XXX - lookup real time. u32 status; for (;;) { status = td->status; if (!(status & TD_CTRL_ACTIVE)) break; - if (check_tsc(end)) { + if (timer_check(end)) { warn_timeout(); return -1; } diff --git a/src/util.h b/src/util.h index 10c2364..6944cfb 100644 --- a/src/util.h +++ b/src/util.h @@ -282,19 +282,19 @@ void useRTC(void); void releaseRTC(void);
// timer.c -u32 ticks_to_ms(u32 ticks); -u32 ticks_from_ms(u32 ms); -void pmtimer_setup(u16 ioport); -int check_tsc(u32 end); void timer_setup(void); +void pmtimer_setup(u16 ioport); +u32 timer_calc(u32 msecs); +u32 timer_calc_usec(u32 usecs); +int timer_check(u32 end); void ndelay(u32 count); void udelay(u32 count); void mdelay(u32 count); void nsleep(u32 count); void usleep(u32 count); void msleep(u32 count); -u32 calc_future_tsc(u32 msecs); -u32 calc_future_tsc_usec(u32 usecs); +u32 ticks_to_ms(u32 ticks); +u32 ticks_from_ms(u32 ms); u32 irqtimer_calc_ticks(u32 count); u32 irqtimer_calc(u32 msecs); int irqtimer_check(u32 end);
Split timer_setup() - put the tsc calibration code in its own function.
Group all the timer setup functions together.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/timer.c | 84 +++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 35 deletions(-)
diff --git a/src/timer.c b/src/timer.c index b60a047..cc9e9f1 100644 --- a/src/timer.c +++ b/src/timer.c @@ -19,13 +19,6 @@ #define PMTIMER_TO_PIT 3 // Ratio of pmtimer rate to pit rate #define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer
- -/**************************************************************** - * Internal timers - ****************************************************************/ - -#define CALIBRATE_COUNT 0x800 // Approx 1.7ms - u32 TimerKHz VARFSEG; u8 no_tsc VARFSEG;
@@ -35,27 +28,17 @@ u32 pmtimer_last VARLOW;
u8 ShiftTSC VARFSEG;
-void -timer_setup(void) -{ - u32 eax, ebx, ecx, edx, cpuid_features = 0;
- if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) { - dprintf(3, "pmtimer already configured; will not calibrate TSC\n"); - return; - } - - cpuid(0, &eax, &ebx, &ecx, &edx); - if (eax > 0) - cpuid(1, &eax, &ebx, &ecx, &cpuid_features); +/**************************************************************** + * Timer setup + ****************************************************************/
- if (!(cpuid_features & CPUID_TSC)) { - no_tsc = 1; - TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); - dprintf(3, "386/486 class CPU. Using TSC emulation\n"); - return; - } +#define CALIBRATE_COUNT 0x800 // Approx 1.7ms
+// Calibrate the CPU time-stamp-counter +static void +tsctimer_setup(void) +{ // Setup "timer2" u8 orig = inb(PORT_PS2_CTRLB); outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB); @@ -88,6 +71,45 @@ timer_setup(void) dprintf(1, "CPU Mhz=%u\n", (TimerKHz << ShiftTSC) / 1000); }
+// Setup internal timers. +void +timer_setup(void) +{ + if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) { + dprintf(3, "pmtimer already configured; will not calibrate TSC\n"); + return; + } + + 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)) { + no_tsc = 1; + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); + dprintf(3, "386/486 class CPU. Using TSC emulation\n"); + return; + } + + tsctimer_setup(); +} + +void +pmtimer_setup(u16 ioport) +{ + if (!CONFIG_PMTIMER) + return; + dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport); + pmtimer_ioport = ioport; + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000); +} + + +/**************************************************************** + * Internal timer reading + ****************************************************************/ + /* TSC emulation timekeepers */ u32 TSC_8254 VARLOW; int Last_TSC_8254 VARLOW; @@ -109,16 +131,8 @@ pittimer_read(void) return ret; }
-void pmtimer_setup(u16 ioport) -{ - if (!CONFIG_PMTIMER) - return; - dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport); - pmtimer_ioport = ioport; - TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000); -} - -static u32 pmtimer_read(void) +static u32 +pmtimer_read(void) { u16 ioport = GET_GLOBAL(pmtimer_ioport); u32 wraps = GET_LOW(pmtimer_wraps);
These two functions both need to add in extra high bits to their timers, and this code is the bulk of these functions. Factor out the duplicate code.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/timer.c | 75 +++++++++++++++++++++++-------------------------------------- 1 file changed, 28 insertions(+), 47 deletions(-)
diff --git a/src/timer.c b/src/timer.c index cc9e9f1..c0cda79 100644 --- a/src/timer.c +++ b/src/timer.c @@ -17,15 +17,9 @@
#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
u32 TimerKHz VARFSEG; -u8 no_tsc VARFSEG; - -u16 pmtimer_ioport VARFSEG; -u32 pmtimer_wraps VARLOW; -u32 pmtimer_last VARLOW; - +u16 TimerPort VARFSEG; u8 ShiftTSC VARFSEG;
@@ -75,7 +69,7 @@ tsctimer_setup(void) void timer_setup(void) { - if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) { + if (CONFIG_PMTIMER && TimerPort) { dprintf(3, "pmtimer already configured; will not calibrate TSC\n"); return; } @@ -86,7 +80,7 @@ timer_setup(void) cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
if (!(cpuid_features & CPUID_TSC)) { - no_tsc = 1; + TimerPort = PORT_PIT_COUNTER0; TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); dprintf(3, "386/486 class CPU. Using TSC emulation\n"); return; @@ -101,7 +95,7 @@ pmtimer_setup(u16 ioport) if (!CONFIG_PMTIMER) return; dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport); - pmtimer_ioport = ioport; + TimerPort = ioport; TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000); }
@@ -110,54 +104,39 @@ pmtimer_setup(u16 ioport) * Internal timer reading ****************************************************************/
-/* TSC emulation timekeepers */ -u32 TSC_8254 VARLOW; -int Last_TSC_8254 VARLOW; +u32 TimerLast VARLOW;
+// Add extra high bits to timers that have less than 32bits of precision. static u32 -pittimer_read(void) +timer_adjust_bits(u32 value, u32 validbits) { - /* read timer 0 current count */ - u32 ret = GET_LOW(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); - int cnt = (inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8)); - int d = GET_LOW(Last_TSC_8254) - cnt; - /* Determine the ticks count from last invocation of this function */ - ret += (d > 0) ? d : (PIT_TICK_INTERVAL + d); - SET_LOW(Last_TSC_8254, cnt); - SET_LOW(TSC_8254, ret); - return ret; -} - -static u32 -pmtimer_read(void) -{ - u16 ioport = GET_GLOBAL(pmtimer_ioport); - u32 wraps = GET_LOW(pmtimer_wraps); - u32 pmtimer = inl(ioport) & 0xffffff; - - if (pmtimer < GET_LOW(pmtimer_last)) { - wraps++; - SET_LOW(pmtimer_wraps, wraps); - } - SET_LOW(pmtimer_last, pmtimer); - - dprintf(9, "pmtimer: %u:%u\n", wraps, pmtimer); - return wraps << 24 | pmtimer; + u32 last = GET_LOW(TimerLast); + value = (last & ~validbits) | (value & validbits); + if (value < last) + value += validbits + 1; + SET_LOW(TimerLast, value); + return value; }
+// Sample the current timer value. static u32 timer_read(void) { - if (unlikely(GET_GLOBAL(no_tsc))) - return pittimer_read(); - if (CONFIG_PMTIMER && GET_GLOBAL(pmtimer_ioport)) - return pmtimer_read(); + u16 port = GET_GLOBAL(TimerPort); + if (port) { + if (CONFIG_PMTIMER && port != PORT_PIT_COUNTER0) + // Read from PMTIMER + return timer_adjust_bits(inl(port), 0xffffff); + // Read from PIT. + outb(PM_SEL_READBACK | PM_READ_VALUE | PM_READ_COUNTER0, PORT_PIT_MODE); + u16 v = inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8); + return timer_adjust_bits(v, 0xffff); + } + // Read from CPU TSC return rdtscll() >> GET_GLOBAL(ShiftTSC); }
+// Check if the current time is past a previously calculated end time. int timer_check(u32 end) { @@ -221,6 +200,8 @@ timer_calc_usec(u32 usecs) * IRQ based timer ****************************************************************/
+#define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer + // Return the number of milliseconds in 'ticks' number of timer irqs. u32 ticks_to_ms(u32 ticks)