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.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/clock.c | 29 +++++++++++++++++++++++++++++ src/pciinit.c | 5 +++++ src/util.h | 1 + 3 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/src/clock.c b/src/clock.c index 69e9f17..59f269b 100644 --- a/src/clock.c +++ b/src/clock.c @@ -129,11 +129,40 @@ emulate_tsc(void) return ret; }
+u16 pmtimer_ioport VAR16VISIBLE; +u32 pmtimer_wraps VARLOW; +u32 pmtimer_last VARLOW; + +void pmtimer_init(u16 ioport, u32 khz) +{ + 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); + + 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 (GET_GLOBAL(pmtimer_ioport)) + return pmtimer_get(); return rdtscll(); }
diff --git a/src/pciinit.c b/src/pciinit.c index 68f302a..31115ee 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -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[] = { diff --git a/src/util.h b/src/util.h index 89e928c..1603a57 100644 --- a/src/util.h +++ b/src/util.h @@ -312,6 +312,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);
On Mon, Aug 13, 2012 at 03:04:10PM +0200, Gerd Hoffmann wrote:
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.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
src/clock.c | 29 +++++++++++++++++++++++++++++ src/pciinit.c | 5 +++++ src/util.h | 1 + 3 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/src/clock.c b/src/clock.c index 69e9f17..59f269b 100644 --- a/src/clock.c +++ b/src/clock.c @@ -129,11 +129,40 @@ emulate_tsc(void) return ret; }
+u16 pmtimer_ioport VAR16VISIBLE; +u32 pmtimer_wraps VARLOW; +u32 pmtimer_last VARLOW;
+void pmtimer_init(u16 ioport, u32 khz) +{
- 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);
- 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 (GET_GLOBAL(pmtimer_ioport))
return rdtscll();return pmtimer_get();
}
diff --git a/src/pciinit.c b/src/pciinit.c index 68f302a..31115ee 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -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[] = { diff --git a/src/util.h b/src/util.h index 89e928c..1603a57 100644 --- a/src/util.h +++ b/src/util.h @@ -312,6 +312,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);
Looks good.
On Mon, Aug 13, 2012 at 03:04:10PM +0200, Gerd Hoffmann wrote:
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.
It looks okay to me. It'd be nice to have a CONFIG_PMTIMER for it, but it can be added on top.
-Kevin