Implement tsc based delay timers, and use them throughout code.
authorKevin O'Connor <kevin@koconnor.net>
Fri, 28 Nov 2008 21:40:06 +0000 (16:40 -0500)
committerKevin O'Connor <kevin@koconnor.net>
Fri, 28 Nov 2008 21:40:06 +0000 (16:40 -0500)
Calibrate the timestamp-counter using the PIT timer2 mechanism.
Implement ndelay/udelay/mdelay using tsc.
Remove obsolete sleeping mechanisms.

src/ata.c
src/clock.c
src/post_menu.c
src/ps2port.c
src/smpdetect.c
src/util.h

index a15dc0f8d7d18f81c1c0b6e78d1a23ef29f3904d..2e7c4cc22260bcbb638b6e65b1408bf3c79f01a2 100644 (file)
--- 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);
index e786fb4bffa8a2434fdd9312df149004127c306c..7261f2db5e3a16f991794839d9c00e6dbdac38dd 100644 (file)
 #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.
index e4a273ebfc15e3980b4afb53ba4361e23a7969dc..c576320b0b898cd213261b27fcfb2c5a5f2248b1 100644 (file)
@@ -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();
index b72def6cbfb1bfef28db72cf76d3e2e0377857e7..0efbb2f0212c29cb2349e773833afa41886690b2 100644 (file)
 
 #define I8042_BUFFER_SIZE       16
 
-static void
-udelay(int usecs)
-{
-    // XXX - implement real udelay
-    outb(0x00, PORT_DIAG);
-}
-
 static int
 i8042_wait_read(void)
 {
index db47943a8e7c43282fe8228fe8f8bebb940e1152..eda0ecfd87ea6be7866eef9188c48eedbd47d5a3 100644 (file)
@@ -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);
     }
index 6953775cf2c3571652f176a8bada6bb8ff46db32..dadc517fdd52bab2816455b17aa02a8426b63f7f 100644 (file)
@@ -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);