The actual implementation of HPET timers (not used yet, as it seems not to work correctly), by Dustin Howett (GSOC)
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27135 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2008, Dustin Howett, [email protected]. All rights reserved.
|
* Copyright 2008, Dustin Howett, [email protected]. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
@@ -8,9 +7,21 @@
|
|||||||
#include <arch/x86/timer.h>
|
#include <arch/x86/timer.h>
|
||||||
#include <arch/x86/arch_hpet.h>
|
#include <arch/x86/arch_hpet.h>
|
||||||
|
|
||||||
|
#include <boot/kernel_args.h>
|
||||||
|
|
||||||
|
#include <arch/int.h>
|
||||||
|
#include <arch/cpu.h>
|
||||||
|
|
||||||
#include "hpet.h"
|
#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;
|
static struct hpet_regs *sHPETRegs;
|
||||||
|
|
||||||
struct timer_info gHPETTimer = {
|
struct timer_info gHPETTimer = {
|
||||||
@@ -29,9 +40,60 @@ hpet_get_prio(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* TODO: Implement something similar to smp_acpi_probe from boot/.../smp.cpp-
|
static int32
|
||||||
we need to get the hpet base address without talking to the acpi module,
|
hpet_timer_interrupt(struct hpet_timer *timer)
|
||||||
because there's no guarantee that it's actually present at this early point. */
|
{
|
||||||
|
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
|
static int
|
||||||
hpet_set_enabled(struct hpet_regs *regs, bool enabled)
|
hpet_set_enabled(struct hpet_regs *regs, bool enabled)
|
||||||
@@ -47,8 +109,8 @@ hpet_set_enabled(struct hpet_regs *regs, bool enabled)
|
|||||||
static int
|
static int
|
||||||
hpet_set_legacy(struct hpet_regs *regs, bool enabled)
|
hpet_set_legacy(struct hpet_regs *regs, bool enabled)
|
||||||
{
|
{
|
||||||
if (!HPET_IS_LEGACY_CAPABLE(regs))
|
// if (!HPET_IS_LEGACY_CAPABLE(regs))
|
||||||
return B_NOT_SUPPORTED;
|
// return B_NOT_SUPPORTED;
|
||||||
|
|
||||||
if (enabled)
|
if (enabled)
|
||||||
regs->config |= HPET_CONF_MASK_LEGACY;
|
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
|
static status_t
|
||||||
hpet_init(struct kernel_args *args)
|
hpet_init(struct kernel_args *args)
|
||||||
{
|
{
|
||||||
/* hpet_acpi_probe() through a similar "scan spots" table to that of smp.cpp.
|
/* hpet_acpi_probe() through a similar "scan spots" table to that of smp.cpp.
|
||||||
Seems to be the most elegant solution right now. */
|
Seems to be the most elegant solution right now. */
|
||||||
|
|
||||||
/* There is no hpet support proper, so error out on init */
|
if (args->arch_args.hpet == NULL) {
|
||||||
return B_ERROR;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user