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:
parent
78d2d884d3
commit
d6a6a0a7cc
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user