Fixed various problems which didn't let HPET timers work on my machine, most

notably:
- in hpet_clear_hardware_timer() we cleared the HPET_CONF_TIMER_INT_ENABLE
flag, and never set it back anymore (now we do, in hpet_set_hardware_timer())
- the incorrect interrupt configuration (although it shouldn't matter in legacy mode).
Also test the HPET counter before trying to use it.
Improved debug information.
HPET timers are still at priority 0, since I coulnd't yet test on other
machines (besides mine, QEMU and XenServer 5.5), and doen't work for
some reason with SMP enabled.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35611 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stefano Ceccherini 2010-02-24 21:52:04 +00:00
parent 7ee2366644
commit 36eb70516c

View File

@ -3,6 +3,7 @@
* Distributed under the terms of the MIT License.
*/
#include <debug.h>
#include <timer.h>
#include <arch/x86/timer.h>
#include <arch/x86/arch_hpet.h>
@ -22,10 +23,13 @@
#define TRACE(x) ;
#endif
#define TEST_HPET
static struct hpet_regs *sHPETRegs;
static volatile struct hpet_timer *sTimer;
static uint64 sHPETPeriod;
static int hpet_get_prio();
static int hpet_get_priority();
static status_t hpet_set_hardware_timer(bigtime_t relativeTimeout);
static status_t hpet_clear_hardware_timer();
static status_t hpet_init(struct kernel_args *args);
@ -33,7 +37,7 @@ static status_t hpet_init(struct kernel_args *args);
struct timer_info gHPETTimer = {
"HPET",
&hpet_get_prio,
&hpet_get_priority,
&hpet_set_hardware_timer,
&hpet_clear_hardware_timer,
&hpet_init
@ -41,15 +45,16 @@ struct timer_info gHPETTimer = {
static int
hpet_get_prio()
hpet_get_priority()
{
return 0; //TODO: Should be 3, so it gets picked first
return 0; // TODO: Should have the highest priority
}
static int32
hpet_timer_interrupt(void *arg)
{
//dprintf_no_syslog("HPET timer_interrupt!!!!\n");
return timer_interrupt();
}
@ -57,8 +62,8 @@ hpet_timer_interrupt(void *arg)
static inline bigtime_t
hpet_convert_timeout(const bigtime_t &relativeTimeout)
{
return ((relativeTimeout * 1000000000LL)
/ HPET_GET_PERIOD(sHPETRegs)) + sHPETRegs->u0.counter64;
return ((relativeTimeout * 1000000000ULL)
/ sHPETPeriod) + sHPETRegs->u0.counter64;
}
@ -67,9 +72,14 @@ hpet_set_hardware_timer(bigtime_t relativeTimeout)
{
cpu_status state = disable_interrupts();
bigtime_t timerValue = hpet_convert_timeout(relativeTimeout);
// enable timer interrupt
sTimer->config |= HPET_CONF_TIMER_INT_ENABLE;
//dprintf("setting value %lld, cur is %lld\n", timerValue, sHPETRegs->counter64);
// TODO:
if (relativeTimeout < 3000)
relativeTimeout = 3000;
bigtime_t timerValue = hpet_convert_timeout(relativeTimeout);
sTimer->u0.comparator64 = timerValue;
@ -82,7 +92,7 @@ hpet_set_hardware_timer(bigtime_t relativeTimeout)
static status_t
hpet_clear_hardware_timer()
{
// Disable timer
// Disable timer interrupt
sTimer->config &= ~HPET_CONF_TIMER_INT_ENABLE;
return B_OK;
}
@ -120,20 +130,32 @@ hpet_set_legacy(bool enabled)
static void
hpet_dump_timer(volatile struct hpet_timer *timer)
{
dprintf(" routable interrupts:\n");
dprintf("HPET Timer %ld:\n", (timer - sHPETRegs->timer));
dprintf("\troutable IRQs: ");
uint32 interrupts = (uint32)HPET_GET_CAP_TIMER_ROUTE(timer);
for (int i = 0; i < 32; i++) {
if (interrupts & (1 << i))
dprintf("%d ", i);
}
dprintf("\n");
dprintf(" config: 0x%llx\n", timer->config);
dprintf(" configured interrupt: %lld\n",
HPET_GET_CONF_TIMER_INT_ROUTE(timer));
dprintf("\tconfiguration: 0x%llx\n", timer->config);
dprintf("\tFSB Enabled: %s\n",
timer->config & HPET_CONF_TIMER_FSB_ENABLE ? "Yes" : "No");
dprintf("\tInterrupt Enabled: %s\n",
timer->config & HPET_CONF_TIMER_INT_ENABLE ? "Yes" : "No");
dprintf("\tTimer type: %s\n",
timer->config & HPET_CONF_TIMER_TYPE ? "Periodic" : "OneShot");
dprintf("\tInterrupt Type: %s\n",
timer->config & HPET_CONF_TIMER_INT_TYPE ? "Level" : "Edge");
dprintf(" fsb_route[0]: 0x%llx\n", timer->fsb_route[0]);
dprintf(" fsb_route[1]: 0x%llx\n", timer->fsb_route[1]);
dprintf("\tconfigured IRQ: %lld\n",
HPET_GET_CONF_TIMER_INT_ROUTE(timer));
if (timer->config & HPET_CONF_TIMER_FSB_ENABLE) {
dprintf("\tfsb_route[0]: 0x%llx\n", timer->fsb_route[0]);
dprintf("\tfsb_route[1]: 0x%llx\n", timer->fsb_route[1]);
}
}
#endif
@ -143,25 +165,42 @@ hpet_init_timer(volatile struct hpet_timer *timer)
{
sTimer = timer;
// Configure timer for interrupt 0
sTimer->config &= (~HPET_CONF_TIMER_INT_ROUTE_MASK
<< HPET_CONF_TIMER_INT_ROUTE_SHIFT);
uint32 interrupt = 0;
sTimer->config |= (interrupt << HPET_CONF_TIMER_INT_ROUTE_SHIFT)
& HPET_CONF_TIMER_INT_ROUTE_MASK;
// Non-periodic mode, edge triggered
sTimer->config &= ~(HPET_CONF_TIMER_TYPE | HPET_CONF_TIMER_INT_TYPE);
sTimer->config &= ~HPET_CONF_TIMER_FSB_ENABLE;
sTimer->config &= ~HPET_CONF_TIMER_32MODE;
// Enable timer
sTimer->config |= HPET_CONF_TIMER_INT_ENABLE;
#ifdef TRACE_HPET
dprintf("hpet_init_timer: timer %p, final configuration:\n", timer);
hpet_dump_timer(sTimer);
#endif
}
static status_t
hpet_test()
{
uint64 initialValue = sHPETRegs->u0.counter64;
spin(10);
uint64 finalValue = sHPETRegs->u0.counter64;
if (initialValue == finalValue) {
dprintf("hpet_test: counter does not increment\n");
return B_ERROR;
}
return B_OK;
}
static status_t
hpet_init(struct kernel_args *args)
{
@ -183,8 +222,11 @@ hpet_init(struct kernel_args *args)
}
}
TRACE(("hpet_init: HPET is at %p. Vendor ID: %llx. Period: %lld\n",
sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs), HPET_GET_PERIOD(sHPETRegs)));
sHPETPeriod = HPET_GET_PERIOD(sHPETRegs);
TRACE(("hpet_init: HPET is at %p.\n\tVendor ID: %llx, rev: %llx, period: %lld\n",
sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs), HPET_GET_REVID(sHPETRegs),
sHPETPeriod));
status_t status = hpet_set_enabled(false);
if (status != B_OK)
@ -206,23 +248,31 @@ hpet_init(struct kernel_args *args)
dprintf("hpet_init: HPET does not have at least 3 timers. Skipping.\n");
return B_ERROR;
}
#ifdef TRACE_HPET
for (uint32 c = 0; c < numTimers; c++) {
TRACE(("hpet_init: timer %lu:\n", c));
for (uint32 c = 0; c < numTimers; c++)
hpet_dump_timer(&sHPETRegs->timer[c]);
}
#endif
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_init_timer(&sHPETRegs->timer[2]);
hpet_init_timer(&sHPETRegs->timer[0]);
status = hpet_set_enabled(true);
if (status != B_OK)
return status;
#ifdef TEST_HPET
status = hpet_test();
if (status != B_OK)
return status;
#endif
int32 configuredIRQ = HPET_GET_CONF_TIMER_INT_ROUTE(sTimer);
//install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE,
//&hpet_timer_interrupt, NULL, B_NO_LOCK_VECTOR);
install_io_interrupt_handler(configuredIRQ, &hpet_timer_interrupt,
NULL, B_NO_LOCK_VECTOR);
return status;
}