diff --git a/arch/x86_64/include/hpet.h b/arch/x86_64/include/hpet.h index 40627c82efc..0d4dff70f42 100644 --- a/arch/x86_64/include/hpet.h +++ b/arch/x86_64/include/hpet.h @@ -55,8 +55,8 @@ /* General Configuration Register */ -#define HPET_GCONF_LEGERT (1 << 0) /* Bit 0: LegacyReplacement Route */ -#define HPET_GCONF_ENABLE (1 << 1) /* Bit 1: Overall Enable */ +#define HPET_GCONF_ENABLE (1 << 0) /* Bit 0: Overall Enable */ +#define HPET_GCONF_LEGERT (1 << 1) /* Bit 1: LegacyReplacement Route */ /* General Interrupt Status Register */ @@ -86,7 +86,7 @@ #define HPET_TFSB_INT_VAL_SHIFT (0) #define HPET_TFSB_INT_VAL_MASK (0x00000000ffffffff) -#define HPET_TFSB_INT_ADDR_SHIFT (31) +#define HPET_TFSB_INT_ADDR_SHIFT (32) #define HPET_TFSB_INT_ADDR_MASK (0xffffffff00000000) /* HPET register space */ diff --git a/arch/x86_64/src/intel64/Kconfig b/arch/x86_64/src/intel64/Kconfig index 3d28782b555..e8a17fde9ec 100644 --- a/arch/x86_64/src/intel64/Kconfig +++ b/arch/x86_64/src/intel64/Kconfig @@ -116,6 +116,18 @@ config INTEL64_HPET_BASE Configure base address for HPET. In the future, we can get this address from the ACPI table if ACPI support is enabled. +config INTEL64_HPET_32BIT + bool "HPET is 32bit" + default n + ---help--- + Configure HPET in 32-bit mode. + +config INTEL64_HPET_FSB + bool "HPET use FSB for interrupt" + default n + ---help--- + Use FSB for interrupt (works like MSI interrupts). + config ARCH_INTEL64_HPET_ALARM_CHAN int "HPET timer alarm channel" depends on ARCH_INTEL64_HPET_ALARM diff --git a/arch/x86_64/src/intel64/intel64_hpet.c b/arch/x86_64/src/intel64/intel64_hpet.c index c906fa3b869..9bf3ce35b79 100644 --- a/arch/x86_64/src/intel64/intel64_hpet.c +++ b/arch/x86_64/src/intel64/intel64_hpet.c @@ -35,6 +35,14 @@ #include "intel64_hpet.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define X86_64_MAR_DEST 0xfee00000 +#define X86_64_MDR_TYPE 0x4000 +#define X86_64_MDR_CPUID_SHIFT 12 + /**************************************************************************** * Private Types ****************************************************************************/ @@ -341,6 +349,11 @@ static int intel64_hpet_setisr(struct intel64_tim_dev_s *dev, uint8_t timer, regval &= ~HPET_TCONF_PERCAP; } +#ifdef CONFIG_INTEL64_HPET_FSB + /* FSB works only with edge triggered mode */ + + regval &= ~HPET_TCONF_INTTYPE; +#else /* Route interrupts */ regval |= HPET_TCONF_INTROUTE(irq - IRQ0); @@ -352,10 +365,15 @@ static int intel64_hpet_setisr(struct intel64_tim_dev_s *dev, uint8_t timer, */ regval |= HPET_TCONF_INTTYPE; +#endif /* Set 64-bit mode */ +#ifdef CONFIG_INTEL64_HPET_32BIT + regval |= HPET_TCONF_32MODE; +#else regval &= ~HPET_TCONF_32MODE; +#endif /* Write Timer configuration */ @@ -419,6 +437,72 @@ static void intel64_hpet_disint(struct intel64_tim_dev_s *dev, uint8_t tim) intel64_hpet_putreg(hpet, HPET_TCONF_OFFSET(tim), regval); } +#ifdef CONFIG_INTEL64_HPET_FSB +/**************************************************************************** + * Name: intel64_hpet_fsb + * + * Description: + * Configure HPET with FSB interrupts. + * + ****************************************************************************/ + +static void intel64_hpet_fsb(struct intel64_hpet_s *hpet) +{ + uint64_t regval; + uint64_t fsb_val; + uint64_t fsb_addr; + int vect; + int irq; + int i; + + /* Disable legacy routing */ + + intel64_hpet_putreg(hpet, HPET_GCONF_OFFSET, 0); + + /* Configure timers */ + + for (i = 0; i < hpet->timers; i++) + { + /* Allocate MSI vector */ + + vect = 1; + irq = up_alloc_irq_msi(&vect); + if (irq < 0 && vect != 1) + { + tmrerr("failed to allocate MSI for timer %d\n", i); + ASSERT(0); + } + + /* Configure FSB */ + + fsb_addr = X86_64_MAR_DEST | + (up_apic_cpu_id() << X86_64_MDR_CPUID_SHIFT); + fsb_val = X86_64_MDR_TYPE | irq; + + regval = fsb_addr << HPET_TFSB_INT_ADDR_SHIFT; + regval |= fsb_val << HPET_TFSB_INT_VAL_SHIFT; + + intel64_hpet_putreg(hpet, HPET_TFSB_OFFSET(i), regval); + + /* Enable FSB */ + + regval = intel64_hpet_getreg(hpet, HPET_TCONF_OFFSET(i)); + if (!(regval & HPET_TCONF_FSBCAP)) + { + tmrerr("FSB not supported for timer %d\n", i); + ASSERT(0); + } + + intel64_hpet_putreg(hpet, HPET_TCONF_OFFSET(i), + regval | HPET_TCONF_FSBEN); + + /* Store irq number */ + + hpet->chans[i].irq = irq; + } +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -462,6 +546,11 @@ struct intel64_tim_dev_s *intel64_hpet_init(uint64_t base) hpet->ops = &g_intel64_hpet_ops; +#ifdef CONFIG_INTEL64_HPET_FSB + /* Allocate and configure FSB for timers */ + + intel64_hpet_fsb(hpet); +#else if (regval & HPET_GCAPID_LEGROUTE) { /* Configure legacy mode. @@ -479,6 +568,7 @@ struct intel64_tim_dev_s *intel64_hpet_init(uint64_t base) ASSERT(0); } +#endif /* Enable HPET */ diff --git a/arch/x86_64/src/intel64/intel64_oneshot.c b/arch/x86_64/src/intel64/intel64_oneshot.c index 6646c3934bc..3abc96ef364 100644 --- a/arch/x86_64/src/intel64/intel64_oneshot.c +++ b/arch/x86_64/src/intel64/intel64_oneshot.c @@ -79,16 +79,24 @@ static int intel64_oneshot_handler(int irg_num, void * context, void *arg) void *oneshot_arg; oneshot_handler_t oneshot_handler; - tmrinfo("Expired...\n"); - DEBUGASSERT(oneshot != NULL && oneshot->handler); + DEBUGASSERT(oneshot != NULL); - if (INTEL64_TIM_GETINT(oneshot->tch, oneshot->chan)) + /* Additional check for spurious interrupts. We can't check interrupt + * status here, because it doesn't work for FSB which is edge tirggered + */ + + if (oneshot->running) { + DEBUGASSERT(oneshot->handler); + tmrinfo("Expired...\n"); + +#ifndef CONFIG_INTEL64_HPET_FSB /* Disable any further interrupts. */ INTEL64_TIM_DISABLEINT(oneshot->tch, oneshot->chan); INTEL64_TIM_SETISR(oneshot->tch, oneshot->chan, NULL, NULL, false); INTEL64_TIM_ACKINT(oneshot->tch, oneshot->chan); +#endif /* The timer is no longer running */ @@ -103,6 +111,10 @@ static int intel64_oneshot_handler(int irg_num, void * context, void *arg) oneshot_handler(oneshot_arg); } + else + { + tmrinfo("Spurious...\n"); + } return OK; } @@ -204,6 +216,27 @@ int intel64_oneshot_initialize(struct intel64_oneshot_s *oneshot, int chan, oneshot->handler = NULL; oneshot->arg = NULL; +#ifdef CONFIG_INTEL64_HPET_FSB + /* IMPORTENT: HPET in edge triggered mode is broken on some hardware + * and generate spurious interrupts when we enable timer. On the other + * hand FSB works only with edge triggered mode. + * + * We use the following work around for this: + * 1. enable interrupts now for FSB so we can catch initial spurious + * interrupt. + * 2. NEVER disable interrupts, so we avoid spurious interrupt when we + * re-enabled interrupt. + * 3. The previous step requires that we must filter out unwanted + * interrupts for 32-bit mode HPET, that will happen with HPET + * period interval. + */ + + INTEL64_TIM_SETISR(oneshot->tch, oneshot->chan, intel64_oneshot_handler, + oneshot, false); + INTEL64_TIM_SETCOMPARE(oneshot->tch, oneshot->chan, 0); + INTEL64_TIM_ENABLEINT(oneshot->tch, oneshot->chan); +#endif + /* Assign a callback handler to the oneshot */ return intel64_allocate_handler(oneshot); @@ -304,21 +337,25 @@ int intel64_oneshot_start(struct intel64_oneshot_s *oneshot, compare = (usec * (uint64_t)oneshot->frequency) / USEC_PER_SEC; +#ifndef CONFIG_INTEL64_HPET_FSB /* Set up to receive the callback when the interrupt occurs */ INTEL64_TIM_SETISR(oneshot->tch, oneshot->chan, intel64_oneshot_handler, oneshot, false); +#endif /* Set comparator ahed of the current counter */ compare += INTEL64_TIM_GETCOUNTER(oneshot->tch); INTEL64_TIM_SETCOMPARE(oneshot->tch, oneshot->chan, compare); +#ifndef CONFIG_INTEL64_HPET_FSB /* Enable interrupts. We should get the callback when the interrupt * occurs. */ INTEL64_TIM_ENABLEINT(oneshot->tch, oneshot->chan); +#endif oneshot->running = true; spin_unlock_irqrestore(&g_oneshot_spin, flags); @@ -384,10 +421,12 @@ int intel64_oneshot_cancel(struct intel64_oneshot_s *oneshot, counter = INTEL64_TIM_GETCOUNTER(oneshot->tch); compare = INTEL64_TIM_GETCOMPARE(oneshot->tch, oneshot->chan); +#ifndef CONFIG_INTEL64_HPET_FSB /* Now we can disable the interrupt and stop the timer. */ INTEL64_TIM_DISABLEINT(oneshot->tch, oneshot->chan); INTEL64_TIM_SETISR(oneshot->tch, oneshot->chan, NULL, NULL, false); +#endif oneshot->running = false; oneshot->handler = NULL;