Some big changes, still not working on my laptop, since hpet interrupts

aren't routed correctly over the 8259, it seems.
- Removed passing the hpet_regs around, since there's a static variable.
- Added lots of debug dprintfs.
- Fixed setting the timer interrupt to edge
- Timer is initialized once.
- Use the timer 0 instead of 2.
- Renamed register definitions to be more readable
- Use 64 bits registers and unions where applicable.
- Other things I don't remember


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33345 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stefano Ceccherini 2009-09-29 08:36:36 +00:00
parent 02c89a146b
commit ea40a61a84
2 changed files with 116 additions and 82 deletions

View File

@ -11,18 +11,20 @@
/* Doing it this way is Required since the HPET only supports 32/64-bit aligned reads. */
/* Global Capability Register Masks */
#define HPET_CAP_MASK_REVID 0x000000FFL
#define HPET_CAP_MASK_NUMTIMERS 0x00001F00L
#define HPET_CAP_MASK_WIDTH 0x00002000L
#define HPET_CAP_MASK_LEGACY 0x00008000L
#define HPET_CAP_MASK_VENDOR_ID 0xFFFF0000L
#define HPET_CAP_MASK_REVID 0x00000000000000FFULL
#define HPET_CAP_MASK_NUMTIMERS 0x0000000000001F00ULL
#define HPET_CAP_MASK_WIDTH 0x0000000000002000ULL
#define HPET_CAP_MASK_LEGACY 0x0000000000008000ULL
#define HPET_CAP_MASK_VENDOR_ID 0x00000000FFFF0000ULL
#define HPET_CAP_MASK_PERIOD 0xFFFFFFFF00000000ULL
/* Retrieve Global Capabilities */
#define HPET_GET_REVID(regs) ((regs)->capability & HPET_CAP_MASK_REVID)
#define HPET_GET_NUM_TIMERS(regs) (((regs)->capability & HPET_CAP_MASK_NUMTIMERS) >> 8)
#define HPET_IS_64BIT(regs) (((regs)->capability & HPET_CAP_MASK_WIDTH) >> 13)
#define HPET_IS_LEGACY_CAPABLE(regs) (((regs)->capability & HPET_CAP_MASK_LEGACY) >> 15)
#define HPET_GET_VENDOR_ID(regs) (((regs)->capability & HPET_CAP_MASK_VENDOR_ID) >> 16)
#define HPET_GET_REVID(regs) ((regs)->capabilities & HPET_CAP_MASK_REVID)
#define HPET_GET_NUM_TIMERS(regs) (((regs)->capabilities & HPET_CAP_MASK_NUMTIMERS) >> 8)
#define HPET_IS_64BIT(regs) (((regs)->capabilities & HPET_CAP_MASK_WIDTH) >> 13)
#define HPET_IS_LEGACY_CAPABLE(regs) (((regs)->capabilities & HPET_CAP_MASK_LEGACY) >> 15)
#define HPET_GET_VENDOR_ID(regs) (((regs)->capabilities & HPET_CAP_MASK_VENDOR_ID) >> 16)
#define HPET_GET_PERIOD(regs) (((regs)->capabilities & HPET_CAP_MASK_PERIOD) >> 32)
/* Global Config Register Masks */
#define HPET_CONF_MASK_ENABLED 0x00000001
@ -32,58 +34,60 @@
#define HPET_IS_ENABLED(regs) ((regs)->config & HPET_CONF_MASK_ENABLED)
#define HPET_IS_LEGACY(regs) (((regs)->config & HPET_CONF_MASK_LEGACY) >> 1)
/* Timer Configuration */
#define HPET_TN_INT_TYPE_CNF 0x00000002
#define HPET_TN_INT_ENB_CNF 0x00000004
#define HPET_TN_TYPE_CNF 0x00000008
#define HPET_TN_PER_INT_CAP 0x00000010
#define HPET_TN_SIZE_CAP 0x00000020
#define HPET_TN_VAL_SET_CNF 0x00000040
#define HPET_TN_32MODE_CNF 0x00000100
#define HPET_TN_FSB_EN_CNF 0x00004000
#define HPET_TN_FSB_INT_DEL_CAP 0x00008000
/* Timer Configuration and Capabilities*/
#define HPET_CAP_TIMER_MASK 0xFFFFFFFF00000000ULL
#define HPET_CONF_TIMER_INT_ROUTE_MASK 0x3e00UL
#define HPET_CONF_TIMER_INT_ROUTE_SHIFT 9
#define HPET_CONF_TIMER_INT_TYPE 0x00000002UL
#define HPET_CONF_TIMER_INT_ENABLE 0x00000004UL
#define HPET_CONF_TIMER_TYPE 0x00000008UL
#define HPET_CONF_TIMER_VAL_SET 0x00000040UL
#define HPET_CONF_TIMER_32MODE 0x00000100UL
#define HPET_CONF_TIMER_FSB_ENABLE 0x00004000UL
#define HPET_CAP_TIMER_PER_INT 0x00000010UL
#define HPET_CAP_TIMER_SIZE 0x00000020UL
#define HPET_CAP_TIMER_FSB_INT_DEL 0x00008000UL
#define HPET_GET_CAP_TIMER_ROUTE(timer) (((timer)->config & HPET_CAP_TIMER_MASK) >> 32)
#define HPET_GET_CONF_TIMER_INT_ROUTE(timer) (((timer)->config & HPET_CONF_TIMER_INT_ROUTE_MASK) >> HPET_CONF_TIMER_INT_ROUTE_SHIFT)
#define ACPI_HPET_SIGNATURE "HPET"
struct hpet_timer {
/* Timer Configuration/Capability bits, Reversed because x86 is LSB */
volatile uint32 config;
volatile uint32 interrupts; /* R/W: Each bit represents one allowed interrupt for this timer. */
volatile uint64 config;
/* R/W: Each bit represents one allowed interrupt for this timer. */
/* If interrupt 16 is allowed, bit 16 will be 1. */
volatile uint64 comparator; /* R/W: Comparator value */
/* non-periodic mode: fires once when main counter = this comparator */
union {
volatile uint64 comparator64; /* R/W: Comparator value */
volatile uint32 comparator32;
} u0; /* non-periodic mode: fires once when main counter = this comparator */
/* periodic mode: fires when timer reaches this value, is increased by the original value */
volatile uint32 fsb_value; /* R/W: FSB Interrupt Route values */
volatile uint32 fsb_addr; /* R/W: Where fsb_value should be written */
volatile uint64 reserved;
volatile uint64 fsb_route[2]; /* R/W: FSB Interrupt Route values */
};
struct hpet_regs {
/* Capability bits */
volatile uint32 capability; /* Read Only: Capabilities */
volatile uint32 period; /* Read Only: Period */
volatile uint64 capabilities; /* Read Only */
volatile uint64 reserved1;
volatile uint64 config; /* R/W: Config Bits */
volatile uint64 config; /* R/W: Config Bits */
volatile uint64 reserved2;
/* Interrupt Status bits */
volatile uint64 timer_interrupts; /* Interrupt Config bits for timers 0-31 */
/* Level Tigger: 0 = off, 1 = set by hardware, timer is active */
/* Edge Trigger: ignored */
/* Writing 0 will not clear these. Must write 1 again. */
volatile uint64 interrupt_status; /* Interrupt Config bits for timers 0-31 */
/* Level Tigger: 0 = off, 1 = set by hardware, timer is active */
/* Edge Trigger: ignored */
/* Writing 0 will not clear these. Must write 1 again. */
volatile uint64 reserved3[25];
volatile uint8 reserved3[200];
volatile uint64 counter; /* R/W */
union {
volatile uint64 counter64; /* R/W */
volatile uint32 counter32;
} u0;
volatile uint64 reserved4;

View File

@ -21,6 +21,7 @@
#endif
static struct hpet_regs *sHPETRegs;
static volatile struct hpet_timer *sTimer;
static int hpet_get_prio();
static status_t hpet_set_hardware_timer(bigtime_t relativeTimeout);
@ -52,10 +53,10 @@ hpet_timer_interrupt(void *arg)
static inline bigtime_t
convert_timeout(const bigtime_t &relativeTimeout)
hpet_convert_timeout(const bigtime_t &relativeTimeout)
{
return ((relativeTimeout * 1000000000LL)
/ sHPETRegs->period) + sHPETRegs->counter;
/ HPET_GET_PERIOD(sHPETRegs)) + sHPETRegs->u0.counter64;
}
@ -64,17 +65,11 @@ hpet_set_hardware_timer(bigtime_t relativeTimeout)
{
cpu_status state = disable_interrupts();
//dprintf("setting value %d, cur is %d\n", timerValue, sHPETRegs->counter);
sHPETRegs->timer[2].comparator = convert_timeout(relativeTimeout);
// Clear the interrupt (set to 0)
sHPETRegs->timer[2].config &= (~31 << 9);
// Non-periodic mode, edge triggered
sHPETRegs->timer[2].config &= ~(HPET_TN_TYPE_CNF & HPET_TN_INT_TYPE_CNF);
// Enable timer
sHPETRegs->timer[2].config |= HPET_TN_INT_ENB_CNF;
bigtime_t timerValue = hpet_convert_timeout(relativeTimeout);
//dprintf("setting value %lld, cur is %lld\n", timerValue, sHPETRegs->counter64);
sTimer->u0.comparator64 = timerValue;
restore_interrupts(state);
@ -86,32 +81,34 @@ static status_t
hpet_clear_hardware_timer()
{
// Disable timer
sHPETRegs->timer[2].config &= ~HPET_TN_INT_ENB_CNF;
sTimer->config &= ~HPET_CONF_TIMER_INT_ENABLE;
return B_OK;
}
static status_t
hpet_set_enabled(struct hpet_regs *regs, bool enabled)
hpet_set_enabled(bool enabled)
{
if (enabled)
regs->config |= HPET_CONF_MASK_ENABLED;
sHPETRegs->config |= HPET_CONF_MASK_ENABLED;
else
regs->config &= ~HPET_CONF_MASK_ENABLED;
sHPETRegs->config &= ~HPET_CONF_MASK_ENABLED;
return B_OK;
}
static status_t
hpet_set_legacy(struct hpet_regs *regs, bool enabled)
hpet_set_legacy(bool enabled)
{
if (!HPET_IS_LEGACY_CAPABLE(regs))
if (!HPET_IS_LEGACY_CAPABLE(sHPETRegs)) {
dprintf("hpet_init: HPET doesn't support legacy mode. Skipping.\n");
return B_NOT_SUPPORTED;
}
if (enabled)
regs->config |= HPET_CONF_MASK_LEGACY;
sHPETRegs->config |= HPET_CONF_MASK_LEGACY;
else
regs->config &= ~HPET_CONF_MASK_LEGACY;
sHPETRegs->config &= ~HPET_CONF_MASK_LEGACY;
return B_OK;
}
@ -119,16 +116,50 @@ hpet_set_legacy(struct hpet_regs *regs, bool enabled)
#ifdef TRACE_HPET
static void
dump_timer(volatile struct hpet_timer *timer)
hpet_dump_timer(volatile struct hpet_timer *timer)
{
dprintf(" config: 0x%lx\n", timer->config);
dprintf(" interrupts: 0x%lx\n", timer->interrupts);
dprintf(" fsb_value: 0x%lx\n", timer->fsb_value);
dprintf(" fsb_addr: 0x%lx\n", timer->fsb_addr);
dprintf(" routable interrupts:\n");
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(" fsb_route[0]: 0x%llx\n", timer->fsb_route[0]);
dprintf(" fsb_route[1]: 0x%llx\n", timer->fsb_route[1]);
}
#endif
static void
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);
// Non-periodic mode, edge triggered
sTimer->config &= ~(HPET_CONF_TIMER_TYPE | HPET_CONF_TIMER_INT_TYPE);
sTimer->config &= ~HPET_CONF_TIMER_FSB_ENABLE;
// 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_init(struct kernel_args *args)
{
@ -149,28 +180,24 @@ hpet_init(struct kernel_args *args)
}
}
TRACE(("hpet_init: HPET is at %p. Vendor ID: %lx. Period: %ld\n",
sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs), sHPETRegs->period));
TRACE(("hpet_init: HPET is at %p. Vendor ID: %llx. Period: %lld\n",
sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs), HPET_GET_PERIOD(sHPETRegs)));
/* There is no hpet legacy support, so error out on init */
if (!HPET_IS_LEGACY_CAPABLE(sHPETRegs)) {
dprintf("hpet_init: HPET doesn't support legacy mode. Skipping.\n");
return B_ERROR;
}
status_t status = hpet_set_legacy(sHPETRegs, true);
status_t status = hpet_set_enabled(false);
if (status != B_OK)
return status;
status = hpet_set_legacy(true);
if (status != B_OK)
return status;
uint32 numTimers = HPET_GET_NUM_TIMERS(sHPETRegs) + 1;
TRACE(("hpet_init: HPET does%s support legacy mode.\n",
HPET_IS_LEGACY_CAPABLE(sHPETRegs) ? "" : " not"));
TRACE(("hpet_init: HPET supports %lu timers, and is %s bits wide.\n",
numTimers, HPET_IS_64BIT(sHPETRegs) ? "64" : "32"));
TRACE(("hpet_init: configuration: 0x%llx, timer_interrupts: 0x%llx\n",
sHPETRegs->config, sHPETRegs->timer_interrupts));
sHPETRegs->config, sHPETRegs->interrupt_status));
if (numTimers < 3) {
dprintf("hpet_init: HPET does not have at least 3 timers. Skipping.\n");
@ -180,15 +207,18 @@ hpet_init(struct kernel_args *args)
#ifdef TRACE_HPET
for (uint32 c = 0; c < numTimers; c++) {
TRACE(("hpet_init: timer %lu:\n", c));
dump_timer(&sHPETRegs->timer[c]);
hpet_dump_timer(&sHPETRegs->timer[c]);
}
#endif
#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);
status = hpet_set_enabled(sHPETRegs, true);
hpet_init_timer(&sHPETRegs->timer[0]);
status = hpet_set_enabled(true);
return status;
}