diff --git a/headers/private/kernel/arch/x86/arch_hpet.h b/headers/private/kernel/arch/x86/arch_hpet.h index dec91399d4..163ed42882 100644 --- a/headers/private/kernel/arch/x86/arch_hpet.h +++ b/headers/private/kernel/arch/x86/arch_hpet.h @@ -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; diff --git a/src/system/kernel/arch/x86/timers/x86_hpet.cpp b/src/system/kernel/arch/x86/timers/x86_hpet.cpp index 2d5072cee7..95efd32d7d 100644 --- a/src/system/kernel/arch/x86/timers/x86_hpet.cpp +++ b/src/system/kernel/arch/x86/timers/x86_hpet.cpp @@ -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; }