diff --git a/src/system/kernel/arch/x86/timers/x86_hpet.c b/src/system/kernel/arch/x86/timers/x86_hpet.c index 01fdd16949..05f68d394b 100644 --- a/src/system/kernel/arch/x86/timers/x86_hpet.c +++ b/src/system/kernel/arch/x86/timers/x86_hpet.c @@ -1,4 +1,3 @@ - /* * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. * Distributed under the terms of the MIT License. @@ -8,9 +7,21 @@ #include #include +#include + +#include +#include + #include "hpet.h" -static int32 sHPETAddr; +#define TRACE_HPET +#ifdef TRACE_HPET + #define TRACE(x) dprintf x +#else + #define TRACE(x) ; +#endif + +static uint32 sHPETAddr; static struct hpet_regs *sHPETRegs; struct timer_info gHPETTimer = { @@ -29,9 +40,60 @@ hpet_get_prio(void) } -/* TODO: Implement something similar to smp_acpi_probe from boot/.../smp.cpp- - we need to get the hpet base address without talking to the acpi module, - because there's no guarantee that it's actually present at this early point. */ +static int32 +hpet_timer_interrupt(struct hpet_timer *timer) +{ + return timer_interrupt(); +} + + +static status_t +hpet_set_hardware_timer(bigtime_t relativeTimeout) +{ + cpu_status state; + uint64 timerValue; + + //dprintf("disabling interrupts\n"); + + state = disable_interrupts(); + + //dprintf("getting value\n"); + timerValue = relativeTimeout; + timerValue *= 1000000; + timerValue /= sHPETRegs->period; + //dprintf("adding hpet counter value\n"); + timerValue += sHPETRegs->counter; + + //dprintf("setting value %d, cur is %d\n", timerValue, sHPETRegs->counter); + sHPETRegs->timer2.comparator = timerValue; + + // Clear the interrupt (set to 0) + //dprintf("clearing interrupts\n"); + sHPETRegs->timer2.config &= (~31 << 9); + + // Non-periodic mode, edge triggered + //dprintf("edge mode\n"); + sHPETRegs->timer2.config &= ~(0x8 & 0x2); + + // Enable timer + //dprintf("enable\n"); + sHPETRegs->timer2.config |= 0x4; + + //dprintf("reenable interrupts\n"); + restore_interrupts(state); + + return B_OK; +} + + +static status_t +hpet_clear_hardware_timer(void) +{ + // Disable timer + sHPETRegs->timer2.config &= ~0x4; + return B_OK; +} + static int hpet_set_enabled(struct hpet_regs *regs, bool enabled) @@ -47,8 +109,8 @@ hpet_set_enabled(struct hpet_regs *regs, bool enabled) static int hpet_set_legacy(struct hpet_regs *regs, bool enabled) { - if (!HPET_IS_LEGACY_CAPABLE(regs)) - return B_NOT_SUPPORTED; +// if (!HPET_IS_LEGACY_CAPABLE(regs)) +// return B_NOT_SUPPORTED; if (enabled) regs->config |= HPET_CONF_MASK_LEGACY; @@ -58,33 +120,47 @@ hpet_set_legacy(struct hpet_regs *regs, bool enabled) } -static int32 -hpet_timer_interrupt(void *data) -{ - return timer_interrupt(); -} - - -static status_t -hpet_set_hardware_timer(bigtime_t relative_timeout) -{ - return B_ERROR; -} - - -static status_t -hpet_clear_hardware_timer(void) -{ - return B_ERROR; -} - - static status_t hpet_init(struct kernel_args *args) { /* hpet_acpi_probe() through a similar "scan spots" table to that of smp.cpp. Seems to be the most elegant solution right now. */ - /* There is no hpet support proper, so error out on init */ - return B_ERROR; + if (args->arch_args.hpet == NULL) { + return B_ERROR; + } + + if (sHPETRegs == NULL) { + sHPETRegs = (struct hpet_regs *)args->arch_args.hpet; + if (map_physical_memory("hpet", (void *)args->arch_args.hpet_phys, + B_PAGE_SIZE, B_EXACT_ADDRESS, B_KERNEL_READ_AREA | + B_KERNEL_WRITE_AREA, (void **)&sHPETRegs) < B_OK) { + // Would it be better to panic here? + dprintf("hpet_init: Failed to map memory for the HPET registers."); + return B_ERROR; + } + } + + TRACE(("hpet_init: HPET is at %x. Vendor ID: %x.\n", sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs))); + + /* There is no hpet legacy support, so error out on init */ + if (!HPET_IS_LEGACY_CAPABLE(sHPETRegs)) { + dprintf("hpet_init: HPET does support legacy mode. Skipping.\n"); + return B_ERROR; + } + + hpet_set_legacy(sHPETRegs, true); + TRACE(("hpet_init: HPET does%s support legacy mode.\n", HPET_IS_LEGACY_CAPABLE(sHPETRegs) ? "" : " not")); + TRACE(("hpet_init: HPET supports %d timers, and is %s bits wide.\n", HPET_GET_NUM_TIMERS(sHPETRegs) + 1, HPET_IS_64BIT(sHPETRegs) ? "64" : "32")); + + if (HPET_GET_NUM_TIMERS(sHPETRegs) < 2) { + dprintf("hpet_init: HPET does not have at least 3 timers. Skipping.\n"); + return B_ERROR; + } + + install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE, &hpet_timer_interrupt, NULL, B_NO_LOCK_VECTOR); + install_io_interrupt_handler(0, &hpet_timer_interrupt, NULL, B_NO_LOCK_VECTOR); + + hpet_set_enabled(sHPETRegs, true); + return B_OK; }