You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2024/09/13 12:28:14 UTC

(nuttx) branch master updated: arch/intel64/hpet: add FSB interrupts support and support for 32-bit mode

This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new d6a6a0a7cc arch/intel64/hpet: add FSB interrupts support and support for 32-bit mode
d6a6a0a7cc is described below

commit d6a6a0a7cc3cd7e64feb84ebd3e5b42440ef89f0
Author: p-szafonimateusz <p-...@xiaomi.com>
AuthorDate: Thu Jun 27 09:26:23 2024 +0200

    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-...@xiaomi.com>
---
 arch/x86_64/include/hpet.h                |  6 +--
 arch/x86_64/src/intel64/Kconfig           | 12 +++++
 arch/x86_64/src/intel64/intel64_hpet.c    | 90 +++++++++++++++++++++++++++++++
 arch/x86_64/src/intel64/intel64_oneshot.c | 45 ++++++++++++++--
 4 files changed, 147 insertions(+), 6 deletions(-)

diff --git a/arch/x86_64/include/hpet.h b/arch/x86_64/include/hpet.h
index 40627c82ef..0d4dff70f4 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 3d28782b55..e8a17fde9e 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 c906fa3b86..9bf3ce35b7 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 6646c3934b..3abc96ef36 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;