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
+45 -41
View File
@@ -11,18 +11,20 @@
/* Doing it this way is Required since the HPET only supports 32/64-bit aligned reads. */ /* Doing it this way is Required since the HPET only supports 32/64-bit aligned reads. */
/* Global Capability Register Masks */ /* Global Capability Register Masks */
#define HPET_CAP_MASK_REVID 0x000000FFL #define HPET_CAP_MASK_REVID 0x00000000000000FFULL
#define HPET_CAP_MASK_NUMTIMERS 0x00001F00L #define HPET_CAP_MASK_NUMTIMERS 0x0000000000001F00ULL
#define HPET_CAP_MASK_WIDTH 0x00002000L #define HPET_CAP_MASK_WIDTH 0x0000000000002000ULL
#define HPET_CAP_MASK_LEGACY 0x00008000L #define HPET_CAP_MASK_LEGACY 0x0000000000008000ULL
#define HPET_CAP_MASK_VENDOR_ID 0xFFFF0000L #define HPET_CAP_MASK_VENDOR_ID 0x00000000FFFF0000ULL
#define HPET_CAP_MASK_PERIOD 0xFFFFFFFF00000000ULL
/* Retrieve Global Capabilities */ /* Retrieve Global Capabilities */
#define HPET_GET_REVID(regs) ((regs)->capability & HPET_CAP_MASK_REVID) #define HPET_GET_REVID(regs) ((regs)->capabilities & HPET_CAP_MASK_REVID)
#define HPET_GET_NUM_TIMERS(regs) (((regs)->capability & HPET_CAP_MASK_NUMTIMERS) >> 8) #define HPET_GET_NUM_TIMERS(regs) (((regs)->capabilities & HPET_CAP_MASK_NUMTIMERS) >> 8)
#define HPET_IS_64BIT(regs) (((regs)->capability & HPET_CAP_MASK_WIDTH) >> 13) #define HPET_IS_64BIT(regs) (((regs)->capabilities & HPET_CAP_MASK_WIDTH) >> 13)
#define HPET_IS_LEGACY_CAPABLE(regs) (((regs)->capability & HPET_CAP_MASK_LEGACY) >> 15) #define HPET_IS_LEGACY_CAPABLE(regs) (((regs)->capabilities & HPET_CAP_MASK_LEGACY) >> 15)
#define HPET_GET_VENDOR_ID(regs) (((regs)->capability & HPET_CAP_MASK_VENDOR_ID) >> 16) #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 */ /* Global Config Register Masks */
#define HPET_CONF_MASK_ENABLED 0x00000001 #define HPET_CONF_MASK_ENABLED 0x00000001
@@ -32,58 +34,60 @@
#define HPET_IS_ENABLED(regs) ((regs)->config & HPET_CONF_MASK_ENABLED) #define HPET_IS_ENABLED(regs) ((regs)->config & HPET_CONF_MASK_ENABLED)
#define HPET_IS_LEGACY(regs) (((regs)->config & HPET_CONF_MASK_LEGACY) >> 1) #define HPET_IS_LEGACY(regs) (((regs)->config & HPET_CONF_MASK_LEGACY) >> 1)
/* Timer Configuration */ /* Timer Configuration and Capabilities*/
#define HPET_TN_INT_TYPE_CNF 0x00000002 #define HPET_CAP_TIMER_MASK 0xFFFFFFFF00000000ULL
#define HPET_TN_INT_ENB_CNF 0x00000004 #define HPET_CONF_TIMER_INT_ROUTE_MASK 0x3e00UL
#define HPET_TN_TYPE_CNF 0x00000008 #define HPET_CONF_TIMER_INT_ROUTE_SHIFT 9
#define HPET_TN_PER_INT_CAP 0x00000010 #define HPET_CONF_TIMER_INT_TYPE 0x00000002UL
#define HPET_TN_SIZE_CAP 0x00000020 #define HPET_CONF_TIMER_INT_ENABLE 0x00000004UL
#define HPET_TN_VAL_SET_CNF 0x00000040 #define HPET_CONF_TIMER_TYPE 0x00000008UL
#define HPET_TN_32MODE_CNF 0x00000100 #define HPET_CONF_TIMER_VAL_SET 0x00000040UL
#define HPET_TN_FSB_EN_CNF 0x00004000 #define HPET_CONF_TIMER_32MODE 0x00000100UL
#define HPET_TN_FSB_INT_DEL_CAP 0x00008000 #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" #define ACPI_HPET_SIGNATURE "HPET"
struct hpet_timer { struct hpet_timer {
/* Timer Configuration/Capability bits, Reversed because x86 is LSB */ /* Timer Configuration/Capability bits, Reversed because x86 is LSB */
volatile uint32 config; volatile uint64 config;
volatile uint32 interrupts; /* R/W: Each bit represents one allowed interrupt for this timer. */ /* R/W: Each bit represents one allowed interrupt for this timer. */
/* If interrupt 16 is allowed, bit 16 will be 1. */ /* If interrupt 16 is allowed, bit 16 will be 1. */
union {
volatile uint64 comparator; /* R/W: Comparator value */ volatile uint64 comparator64; /* R/W: Comparator value */
/* non-periodic mode: fires once when main counter = this comparator */ 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 */ /* 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 uint64 fsb_route[2]; /* R/W: FSB Interrupt Route values */
volatile uint32 fsb_addr; /* R/W: Where fsb_value should be written */
volatile uint64 reserved;
}; };
struct hpet_regs { struct hpet_regs {
/* Capability bits */ volatile uint64 capabilities; /* Read Only */
volatile uint32 capability; /* Read Only: Capabilities */
volatile uint32 period; /* Read Only: Period */
volatile uint64 reserved1; volatile uint64 reserved1;
volatile uint64 config; /* R/W: Config Bits */ volatile uint64 config; /* R/W: Config Bits */
volatile uint64 reserved2; volatile uint64 reserved2;
/* Interrupt Status bits */ /* Interrupt Status bits */
volatile uint64 timer_interrupts; /* Interrupt Config bits for timers 0-31 */ volatile uint64 interrupt_status; /* Interrupt Config bits for timers 0-31 */
/* Level Tigger: 0 = off, 1 = set by hardware, timer is active */ /* Level Tigger: 0 = off, 1 = set by hardware, timer is active */
/* Edge Trigger: ignored */ /* Edge Trigger: ignored */
/* Writing 0 will not clear these. Must write 1 again. */ /* Writing 0 will not clear these. Must write 1 again. */
volatile uint64 reserved3[25];
volatile uint8 reserved3[200]; union {
volatile uint64 counter64; /* R/W */
volatile uint64 counter; /* R/W */ volatile uint32 counter32;
} u0;
volatile uint64 reserved4; volatile uint64 reserved4;
+71 -41
View File
@@ -21,6 +21,7 @@
#endif #endif
static struct hpet_regs *sHPETRegs; static struct hpet_regs *sHPETRegs;
static volatile struct hpet_timer *sTimer;
static int hpet_get_prio(); static int hpet_get_prio();
static status_t hpet_set_hardware_timer(bigtime_t relativeTimeout); static status_t hpet_set_hardware_timer(bigtime_t relativeTimeout);
@@ -52,10 +53,10 @@ hpet_timer_interrupt(void *arg)
static inline bigtime_t static inline bigtime_t
convert_timeout(const bigtime_t &relativeTimeout) hpet_convert_timeout(const bigtime_t &relativeTimeout)
{ {
return ((relativeTimeout * 1000000000LL) 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(); cpu_status state = disable_interrupts();
//dprintf("setting value %d, cur is %d\n", timerValue, sHPETRegs->counter); bigtime_t timerValue = hpet_convert_timeout(relativeTimeout);
sHPETRegs->timer[2].comparator = convert_timeout(relativeTimeout);
//dprintf("setting value %lld, cur is %lld\n", timerValue, sHPETRegs->counter64);
// Clear the interrupt (set to 0)
sHPETRegs->timer[2].config &= (~31 << 9); sTimer->u0.comparator64 = timerValue;
// 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;
restore_interrupts(state); restore_interrupts(state);
@@ -86,32 +81,34 @@ static status_t
hpet_clear_hardware_timer() hpet_clear_hardware_timer()
{ {
// Disable timer // Disable timer
sHPETRegs->timer[2].config &= ~HPET_TN_INT_ENB_CNF; sTimer->config &= ~HPET_CONF_TIMER_INT_ENABLE;
return B_OK; return B_OK;
} }
static status_t static status_t
hpet_set_enabled(struct hpet_regs *regs, bool enabled) hpet_set_enabled(bool enabled)
{ {
if (enabled) if (enabled)
regs->config |= HPET_CONF_MASK_ENABLED; sHPETRegs->config |= HPET_CONF_MASK_ENABLED;
else else
regs->config &= ~HPET_CONF_MASK_ENABLED; sHPETRegs->config &= ~HPET_CONF_MASK_ENABLED;
return B_OK; return B_OK;
} }
static status_t 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; return B_NOT_SUPPORTED;
}
if (enabled) if (enabled)
regs->config |= HPET_CONF_MASK_LEGACY; sHPETRegs->config |= HPET_CONF_MASK_LEGACY;
else else
regs->config &= ~HPET_CONF_MASK_LEGACY; sHPETRegs->config &= ~HPET_CONF_MASK_LEGACY;
return B_OK; return B_OK;
} }
@@ -119,16 +116,50 @@ hpet_set_legacy(struct hpet_regs *regs, bool enabled)
#ifdef TRACE_HPET #ifdef TRACE_HPET
static void 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(" routable interrupts:\n");
dprintf(" interrupts: 0x%lx\n", timer->interrupts); uint32 interrupts = (uint32)HPET_GET_CAP_TIMER_ROUTE(timer);
dprintf(" fsb_value: 0x%lx\n", timer->fsb_value); for (int i = 0; i < 32; i++) {
dprintf(" fsb_addr: 0x%lx\n", timer->fsb_addr); 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 #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 static status_t
hpet_init(struct kernel_args *args) 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", TRACE(("hpet_init: HPET is at %p. Vendor ID: %llx. Period: %lld\n",
sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs), sHPETRegs->period)); sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs), HPET_GET_PERIOD(sHPETRegs)));
/* There is no hpet legacy support, so error out on init */ status_t status = hpet_set_enabled(false);
if (!HPET_IS_LEGACY_CAPABLE(sHPETRegs)) { if (status != B_OK)
dprintf("hpet_init: HPET doesn't support legacy mode. Skipping.\n"); return status;
return B_ERROR;
} status = hpet_set_legacy(true);
status_t status = hpet_set_legacy(sHPETRegs, true);
if (status != B_OK) if (status != B_OK)
return status; return status;
uint32 numTimers = HPET_GET_NUM_TIMERS(sHPETRegs) + 1; 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", TRACE(("hpet_init: HPET supports %lu timers, and is %s bits wide.\n",
numTimers, HPET_IS_64BIT(sHPETRegs) ? "64" : "32")); numTimers, HPET_IS_64BIT(sHPETRegs) ? "64" : "32"));
TRACE(("hpet_init: configuration: 0x%llx, timer_interrupts: 0x%llx\n", TRACE(("hpet_init: configuration: 0x%llx, timer_interrupts: 0x%llx\n",
sHPETRegs->config, sHPETRegs->timer_interrupts)); sHPETRegs->config, sHPETRegs->interrupt_status));
if (numTimers < 3) { if (numTimers < 3) {
dprintf("hpet_init: HPET does not have at least 3 timers. Skipping.\n"); 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 #ifdef TRACE_HPET
for (uint32 c = 0; c < numTimers; c++) { for (uint32 c = 0; c < numTimers; c++) {
TRACE(("hpet_init: timer %lu:\n", 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, install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE,
&hpet_timer_interrupt, NULL, B_NO_LOCK_VECTOR); &hpet_timer_interrupt, NULL, B_NO_LOCK_VECTOR);
install_io_interrupt_handler(0, &hpet_timer_interrupt, NULL, install_io_interrupt_handler(0, &hpet_timer_interrupt, NULL,
B_NO_LOCK_VECTOR); B_NO_LOCK_VECTOR);
status = hpet_set_enabled(sHPETRegs, true); hpet_init_timer(&sHPETRegs->timer[0]);
status = hpet_set_enabled(true);
return status; return status;
} }