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:
parent
02c89a146b
commit
ea40a61a84
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user