arch/intel64/hpet: add FSB interrupts support and support for 32-bit mode

These are changes to make HPET work with ACRN hypervisor:

- FSB interrupt delivery (which works like PCI MSI)
- 32-bit mode support

Signed-off-by: p-szafonimateusz <p-szafonimateusz@xiaomi.com>
This commit is contained in:
p-szafonimateusz 2024-06-27 09:26:23 +02:00 committed by Alan C. Assis
parent 78d2d884d3
commit d6a6a0a7cc
4 changed files with 147 additions and 6 deletions

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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;