From bc2aecdae66876896f3c77f3e532f9ce262310de Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Fri, 28 Nov 2008 16:40:06 -0500 Subject: [PATCH] Implement tsc based delay timers, and use them throughout code. Calibrate the timestamp-counter using the PIT timer2 mechanism. Implement ndelay/udelay/mdelay using tsc. Remove obsolete sleeping mechanisms. --- src/ata.c | 26 ++------- src/clock.c | 143 ++++++++++++++++++++++++++++++++++++------------ src/post_menu.c | 8 +-- src/ps2port.c | 7 --- src/smpdetect.c | 2 +- src/util.h | 11 +++- 6 files changed, 129 insertions(+), 68 deletions(-) diff --git a/src/ata.c b/src/ata.c index a15dc0f..2e7c4cc 100644 --- a/src/ata.c +++ b/src/ata.c @@ -85,30 +85,14 @@ pause_await_ide(u8 when_done, u16 iobase1, u16 iobase2, u16 timeout) return await_ide(when_done, iobase1, timeout); } -// Delay for x nanoseconds -static void -nsleep(u32 delay) -{ - // XXX - how to implement ndelay? - while (delay--) - nop(); -} - // Wait for ide state - pause for 400ns first. static __always_inline int ndelay_await_ide(u8 when_done, u16 iobase1, u16 timeout) { - nsleep(400); + ndelay(400); return await_ide(when_done, iobase1, timeout); } -// Delay for x milliseconds -static void -msleep(u32 delay) -{ - usleep(delay * 1000); -} - // Reset a drive void ata_reset(int driveid) @@ -133,7 +117,7 @@ ata_reset(int driveid) // 8.2.1 (g) -- check for sc==sn==0x01 // select device outb(slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0, iobase1+ATA_CB_DH); - msleep(50); + mdelay(50); u8 sc = inb(iobase1+ATA_CB_SC); u8 sn = inb(iobase1+ATA_CB_SN); @@ -199,7 +183,7 @@ send_cmd(int driveid, struct ata_pio_command *cmd) outb(cmd->device, iobase1 + ATA_CB_DH); if ((device ^ cmd->device) & (1 << 4)) // Wait for device to become active. - msleep(50); + mdelay(50); if (cmd->command & 0x04) { outb(0x00, iobase1 + ATA_CB_FR); @@ -849,7 +833,7 @@ ata_detect() // Look for device outb(slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0, iobase1+ATA_CB_DH); - msleep(50); + mdelay(50); outb(0x55, iobase1+ATA_CB_SC); outb(0xaa, iobase1+ATA_CB_SN); outb(0xaa, iobase1+ATA_CB_SC); @@ -870,7 +854,7 @@ ata_detect() // check for ATA or ATAPI outb(slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0, iobase1+ATA_CB_DH); - msleep(50); + mdelay(50); sc = inb(iobase1+ATA_CB_SC); sn = inb(iobase1+ATA_CB_SN); dprintf(6, "ata_detect(2) drive=%d sc=%x sn=%x\n", driveid, sc, sn); diff --git a/src/clock.c b/src/clock.c index e786fb4..7261f2d 100644 --- a/src/clock.c +++ b/src/clock.c @@ -19,6 +19,101 @@ #define RTC_B_AIE 0x20 #define RTC_B_UIE 0x10 +// 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) + + +/**************************************************************** + * TSC timer + ****************************************************************/ + +#define PIT_TICK_RATE 1193182 // Underlying HZ of PIT +#define CALIBRATE_COUNT 0x800 // Approx 1.7ms + +extern u32 cpu_khz; +#if MODE16 +u32 cpu_khz VISIBLE16; +#endif + +static void +calibrate_tsc() +{ + // 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_VAR(CS, cpu_khz, hz / 1000); + + dprintf(1, "CPU Mhz=%u\n", hz / 1000000); +} + +static void +tscsleep(u64 diff) +{ + u64 start = rdtscll(); + u64 end = start + diff; + while (rdtscll() < end) + cpu_relax(); +} + +void +ndelay(u32 count) +{ + u32 khz = GET_VAR(CS, cpu_khz); + tscsleep(count * khz / 1000000); +} +void +udelay(u32 count) +{ + u32 khz = GET_VAR(CS, cpu_khz); + tscsleep(count * khz / 1000); +} +void +mdelay(u32 count) +{ + u32 khz = GET_VAR(CS, cpu_khz); + tscsleep(count * khz); +} + /**************************************************************** * Init @@ -28,7 +123,7 @@ static void pit_setup() { // timer0: binary count, 16bit count, mode 2 - outb(0x34, PORT_PIT_MODE); + outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE); // maximum count of 0000H = 18.2Hz outb(0x0, PORT_PIT_COUNTER0); outb(0x0, PORT_PIT_COUNTER0); @@ -44,6 +139,7 @@ void timer_setup() { dprintf(3, "init timer\n"); + calibrate_tsc(); pit_setup(); u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS)); @@ -362,46 +458,25 @@ clear_usertimer() outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B); } -// Sleep for n microseconds. -int -usleep(u32 count) -{ - if (MODE16) { - // In 16bit mode, use the rtc to wait for the specified time. - u8 statusflag = 0; - int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag); - if (ret) - return -1; - irq_enable(); - while (!statusflag) - cpu_relax(); - irq_disable(); - return 0; - } else { - // In 32bit mode, we need to call into 16bit mode to sleep. - struct bregs br; - memset(&br, 0, sizeof(br)); - br.ah = 0x86; - br.cx = count >> 16; - br.dx = count; - call16_int(0x15, &br); - if (br.flags & F_CF) - return -1; - return 0; - } -} - #define RET_ECLOCKINUSE 0x83 // Wait for CX:DX microseconds void handle_1586(struct bregs *regs) { - int ret = usleep((regs->cx << 16) | regs->dx); - if (ret) + // Use the rtc to wait for the specified time. + u8 statusflag = 0; + u32 count = (regs->cx << 16) | regs->dx; + int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag); + if (ret) { set_code_fail(regs, RET_ECLOCKINUSE); - else - set_success(regs); + return; + } + irq_enable(); + while (!statusflag) + cpu_relax(); + irq_disable(); + set_success(regs); } // Set Interval requested. diff --git a/src/post_menu.c b/src/post_menu.c index e4a273e..c576320 100644 --- a/src/post_menu.c +++ b/src/post_menu.c @@ -6,7 +6,7 @@ // This file may be distributed under the terms of the GNU GPLv3 license. #include "biosvar.h" // GET_EBDA -#include "util.h" // usleep +#include "util.h" // mdelay #include "bregs.h" // struct bregs static u8 @@ -29,11 +29,11 @@ get_keystroke() } static void -udelay_and_check_for_keystroke(u32 usec, int count) +mdelay_and_check_for_keystroke(u32 msec, int count) { int i; for (i = 1; i <= count; i++) { - usleep(usec); + mdelay(msec); if (check_for_keystroke()) break; } @@ -50,7 +50,7 @@ interactive_bootmenu() printf("Press F12 for boot menu.\n\n"); - udelay_and_check_for_keystroke(500000, 5); + mdelay_and_check_for_keystroke(500, 5); if (! check_for_keystroke()) return; u8 scan_code = get_keystroke(); diff --git a/src/ps2port.c b/src/ps2port.c index b72def6..0efbb2f 100644 --- a/src/ps2port.c +++ b/src/ps2port.c @@ -20,13 +20,6 @@ #define I8042_BUFFER_SIZE 16 -static void -udelay(int usecs) -{ - // XXX - implement real udelay - outb(0x00, PORT_DIAG); -} - static int i8042_wait_read(void) { diff --git a/src/smpdetect.c b/src/smpdetect.c index db47943..eda0ecf 100644 --- a/src/smpdetect.c +++ b/src/smpdetect.c @@ -97,7 +97,7 @@ smp_probe(void) u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12; writel(APIC_BASE + APIC_ICR_LOW, 0x000C4600 | sipi_vector); - usleep(10*1000); + mdelay(10); smp_cpus = readw((void *)BUILD_CPU_COUNT_ADDR); } diff --git a/src/util.h b/src/util.h index 6953775..dadc517 100644 --- a/src/util.h +++ b/src/util.h @@ -58,6 +58,13 @@ static inline void cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) : "0" (index)); } +static inline u64 rdtscll(void) +{ + u64 val; + asm volatile("rdtsc" : "=A" (val)); + return val; +} + void *memset(void *s, int c, size_t n); void *memcpy(void *d1, const void *s1, size_t len); void *memcpy_far(void *far_d1, const void *far_s1, size_t len); @@ -114,7 +121,9 @@ void lpt_setup(); // clock.c void timer_setup(); -int usleep(u32 count); +void ndelay(u32 count); +void udelay(u32 count); +void mdelay(u32 count); void handle_1583(struct bregs *regs); void handle_1586(struct bregs *regs); -- 2.25.1