]> xenbits.xensource.com Git - seabios.git/commitdiff
add acpi pmtimer support
authorGerd Hoffmann <kraxel@redhat.com>
Thu, 6 Sep 2012 06:01:00 +0000 (08:01 +0200)
committerKevin O'Connor <kevin@koconnor.net>
Tue, 11 Sep 2012 00:35:53 +0000 (20:35 -0400)
This patch makes seabios use the acpi pmtimer instead of tsc for
timekeeping.  The pmtimer has a fixed frequency and doesn't need
calibration, thus it doesn't suffer from calibration errors due to a
loaded host machine.

[ v4: mask port ioport read ]
[ v2: add CONFIG_PMTIMER ]

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
src/Kconfig
src/clock.c
src/pciinit.c
src/util.h

index 6de3e7152545b762ac6da8a5a4384e84ec805a81..b5dd63bd783f7bcea2cd8ea31287766818b43116 100644 (file)
@@ -222,6 +222,12 @@ menu "Hardware support"
         default y
         help
             Initialize the Memory Type Range Registers (on emulators).
+    config PMTIMER
+        depends on !COREBOOT
+        bool "Use ACPI timer"
+        default y
+        help
+            Use the ACPI timer instead of the TSC for timekeeping (on qemu).
 endmenu
 
 menu "BIOS interfaces"
index 69e9f178beb73f492951c11d1bcd7ac01cdb6213..71b913e599e0d489a8c85f1f53edebd63d0bfc10 100644 (file)
@@ -129,11 +129,42 @@ emulate_tsc(void)
     return ret;
 }
 
+u16 pmtimer_ioport VAR16VISIBLE;
+u32 pmtimer_wraps VARLOW;
+u32 pmtimer_last VARLOW;
+
+void pmtimer_init(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();
 }
 
index 68f302a2ff6c9af29767ccd57124d6027ef1778a..31115ee509fc438fc82d5ea8eaae25d98db71554 100644 (file)
@@ -180,6 +180,9 @@ static const struct pci_device_id pci_class_tbl[] = {
     PCI_DEVICE_END,
 };
 
+/* PM Timer ticks per second (HZ) */
+#define PM_TIMER_FREQUENCY  3579545
+
 /* PIIX4 Power Management device (for ACPI) */
 static void piix4_pm_init(struct pci_device *pci, void *arg)
 {
@@ -191,6 +194,8 @@ static void piix4_pm_init(struct pci_device *pci, void *arg)
     pci_config_writeb(bdf, 0x80, 0x01); /* enable PM io space */
     pci_config_writel(bdf, 0x90, PORT_SMB_BASE | 1);
     pci_config_writeb(bdf, 0xd2, 0x09); /* enable SMBus io space */
+
+    pmtimer_init(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000);
 }
 
 static const struct pci_device_id pci_device_tbl[] = {
index 062eea3f3288f49427715a29f0cbbbf155a4161c..7723bb17c83cb50edad015211eaec40f6b9e12a9 100644 (file)
@@ -282,6 +282,7 @@ 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 pmtimer_init(u16 ioport, u32 khz);
 int check_tsc(u64 end);
 void timer_setup(void);
 void ndelay(u32 count);