bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 1/2 gnumach] Add HPET timer for small accurate delays


From: Damien Zammit
Subject: [PATCH 1/2 gnumach] Add HPET timer for small accurate delays
Date: Fri, 02 Feb 2024 06:39:57 +0000

Enables a 32 bit periodic HPET timer without generating interrupts.
The purpose of this is to provide a convenient udelay/mdelay in mach
since the pit one-shot mode is unreliable.

TESTED: This works in qemu correctly
TESTED: This works on an AMD board with ACPI v2.0 correctly
---
 i386/i386/apic.c              | 87 +++++++++++++++++++++++++++++++++++
 i386/i386/apic.h              |  4 ++
 i386/i386at/acpi_parse_apic.c | 35 ++++++++++----
 i386/i386at/acpi_parse_apic.h | 23 +++++++++
 i386/i386at/model_dep.c       |  5 ++
 5 files changed, 146 insertions(+), 8 deletions(-)

diff --git a/i386/i386/apic.c b/i386/i386/apic.c
index 0cf7c37c..e3d53ce3 100644
--- a/i386/i386/apic.c
+++ b/i386/i386/apic.c
@@ -26,6 +26,9 @@
 #include <kern/printf.h>
 #include <kern/kalloc.h>
 
+uint32_t hpet_period_nsec;
+
+extern uint32_t *hpet_addr;
 
 /*
  * This dummy structure is needed so that CPU_NUMBER can be called
@@ -347,3 +350,87 @@ lapic_eoi(void)
 {
     lapic->eoi.r = 0;
 }
+
+#define HPET32(x) *((volatile uint32_t *)((uint8_t *)hpet_addr + x))
+#define HPET_CAP_PERIOD                        0x04
+#define HPET_CFG                       0x10
+# define HPET_CFG_ENABLE               (1 << 0)
+# define HPET_LEGACY_ROUTE             (1 << 1)
+#define HPET_COUNTER                   0xf0
+#define HPET_T0_CFG                    0x100
+# define HPET_T0_32BIT_MODE            (1 << 8)
+# define HPET_T0_VAL_SET               (1 << 6)
+# define HPET_T0_TYPE_PERIODIC         (1 << 3)
+# define HPET_T0_INT_ENABLE            (1 << 2)
+#define HPET_T0_COMPARATOR             0x108
+
+#define FSEC_PER_NSEC                  1000000
+#define NSEC_PER_USEC                  1000
+
+void
+hpet_init(void)
+{
+    uint32_t period;
+    uint32_t val;
+
+    assert(hpet_addr != 0);
+
+    /* Find out how often the HPET ticks in nanoseconds */
+    period = HPET32(HPET_CAP_PERIOD);
+    hpet_period_nsec = period / FSEC_PER_NSEC;
+    printf("HPET ticks every %d nanoseconds\n", hpet_period_nsec);
+
+    /* Disable HPET and legacy interrupt routing mode */
+    val = HPET32(HPET_CFG);
+    val = val & ~(HPET_LEGACY_ROUTE | HPET_CFG_ENABLE);
+    HPET32(HPET_CFG) = val;
+
+    /* Clear the counter */
+    HPET32(HPET_COUNTER) = 0;
+
+    /* Set up 32 bit periodic timer with no interrupts */
+    val = HPET32(HPET_T0_CFG);
+    val = (val & ~HPET_T0_INT_ENABLE) | HPET_T0_32BIT_MODE | 
HPET_T0_TYPE_PERIODIC | HPET_T0_VAL_SET;
+    HPET32(HPET_T0_CFG) = val;
+
+    /* Set comparator to max */
+    HPET32(HPET_T0_COMPARATOR) = 0xffffffff;
+
+    /* Enable the HPET */
+    HPET32(HPET_CFG) |= HPET_CFG_ENABLE;
+
+    printf("HPET enabled\n");
+}
+
+void
+hpet_udelay(uint32_t us)
+{
+    uint32_t start, finish, now;
+
+    us *= NSEC_PER_USEC / hpet_period_nsec;
+
+    start = HPET32(HPET_COUNTER);
+
+    finish = start + us;
+
+    while (1) {
+        now = HPET32(HPET_COUNTER);
+
+        if (finish > start) {
+            /* ticks did not wrap initially */
+            if (now >= finish)
+                break;
+        } else {
+            /* ticks wrapped initially */
+            if ((now < start) && (now >= finish))
+                break;
+        }
+    }
+}
+
+void
+hpet_mdelay(uint32_t ms)
+{
+    hpet_udelay(ms * 1000);
+}
+
diff --git a/i386/i386/apic.h b/i386/i386/apic.h
index e410e9c6..4322e41b 100644
--- a/i386/i386/apic.h
+++ b/i386/i386/apic.h
@@ -250,6 +250,10 @@ void calibrate_lapic_timer(void);
 void ioapic_toggle(int pin, int mask);
 void ioapic_configure(void);
 
+void hpet_init(void);
+void hpet_udelay(uint32_t us);
+void hpet_mdelay(uint32_t ms);
+
 extern int timer_pin;
 extern void intnull(int unit);
 extern volatile ApicLocalUnit* lapic;
diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c
index dcd5da89..1cfc1791 100644
--- a/i386/i386at/acpi_parse_apic.c
+++ b/i386/i386at/acpi_parse_apic.c
@@ -34,6 +34,7 @@
 
 static struct acpi_apic *apic_madt = NULL;
 unsigned lapic_addr;
+uint32_t *hpet_addr;
 
 /*
  * acpi_print_info: shows by screen the ACPI's rsdp and rsdt virtual address
@@ -292,28 +293,37 @@ acpi_get_xsdt(phys_addr_t rsdp_phys, int* acpi_xsdt_n)
  * and the number of entries of RSDT table.
  *
  * Returns a reference to APIC/MADT table if success, NULL if failure.
+ * Also sets hpet_addr to base address of HPET.
  */
 static struct acpi_apic*
 acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n)
 {
     struct acpi_dhdr *descr_header;
+    struct acpi_apic *madt = NULL;
     int check_signature;
+    uint64_t map_addr;
 
     /* Search APIC entries in rsdt table. */
     for (int i = 0; i < acpi_rsdt_n; i++) {
         descr_header = (struct acpi_dhdr*) 
kmem_map_aligned_table(rsdt->entry[i], sizeof(struct acpi_dhdr),
                                                                   
VM_PROT_READ);
 
-        /* Check if the entry contains an APIC. */
+        /* Check if the entry is a MADT */
         check_signature = acpi_check_signature(descr_header->signature, 
ACPI_APIC_SIG, 4*sizeof(uint8_t));
+        if (check_signature == ACPI_SUCCESS)
+            madt = (struct acpi_apic*) descr_header;
 
+        /* Check if the entry is a HPET */
+        check_signature = acpi_check_signature(descr_header->signature, 
ACPI_HPET_SIG, 4*sizeof(uint8_t));
         if (check_signature == ACPI_SUCCESS) {
-            /* If yes, return the APIC. */
-            return (struct acpi_apic*) descr_header;
+            map_addr = ((struct acpi_hpet *)descr_header)->address.addr64;
+            assert (map_addr != 0);
+            hpet_addr = (uint32_t *)kmem_map_aligned_table(map_addr, 1024, 
VM_PROT_READ | VM_PROT_WRITE);
+            printf("HPET at physical address 0x%llx\n", map_addr);
         }
     }
 
-    return NULL;
+    return madt;
 }
 
 /*
@@ -323,28 +333,37 @@ acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n)
  * and the number of entries of XSDT table.
  *
  * Returns a reference to APIC/MADT table if success, NULL if failure.
+ * Also sets hpet_addr to base address of HPET.
  */
 static struct acpi_apic*
 acpi_get_apic2(struct acpi_xsdt *xsdt, int acpi_xsdt_n)
 {
     struct acpi_dhdr *descr_header;
+    struct acpi_apic *madt = NULL;
     int check_signature;
+    uint64_t map_addr;
 
     /* Search APIC entries in rsdt table. */
     for (int i = 0; i < acpi_xsdt_n; i++) {
         descr_header = (struct acpi_dhdr*) 
kmem_map_aligned_table(xsdt->entry[i], sizeof(struct acpi_dhdr),
                                                                   
VM_PROT_READ);
 
-        /* Check if the entry contains an APIC. */
+        /* Check if the entry is an APIC. */
         check_signature = acpi_check_signature(descr_header->signature, 
ACPI_APIC_SIG, 4*sizeof(uint8_t));
+        if (check_signature == ACPI_SUCCESS)
+            madt = (struct acpi_apic *)descr_header;
 
+        /* Check if the entry is a HPET. */
+        check_signature = acpi_check_signature(descr_header->signature, 
ACPI_HPET_SIG, 4*sizeof(uint8_t));
         if (check_signature == ACPI_SUCCESS) {
-            /* If yes, return the APIC. */
-            return (struct acpi_apic*) descr_header;
+            map_addr = ((struct acpi_hpet *)descr_header)->address.addr64;
+            assert (map_addr != 0);
+            hpet_addr = (uint32_t *)kmem_map_aligned_table(map_addr, 1024, 
VM_PROT_READ | VM_PROT_WRITE);
+            printf("HPET at physical address 0x%llx\n", map_addr);
         }
     }
 
-    return NULL;
+    return madt;
 }
 
 /*
diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h
index df36bb31..85e01170 100644
--- a/i386/i386at/acpi_parse_apic.h
+++ b/i386/i386at/acpi_parse_apic.h
@@ -91,6 +91,14 @@ struct acpi_xsdt {
     uint64_t                   entry[0];
 } __attribute__((__packed__));
 
+struct acpi_address {
+    uint8_t    is_io;
+    uint8_t    reg_width;
+    uint8_t    reg_offset;
+    uint8_t    reserved;
+    uint64_t   addr64;
+} __attribute__((__packed__));
+
 /* APIC table signature. */
 #define ACPI_APIC_SIG "APIC"
 
@@ -170,6 +178,21 @@ struct acpi_apic_irq_override {
     uint16_t    flags;
 } __attribute__((__packed__));
 
+
+#define ACPI_HPET_SIG "HPET"
+
+/*
+ * HPET High Precision Event Timer structure
+ */
+struct acpi_hpet {
+    struct acpi_dhdr header;
+    uint32_t   id;
+    struct acpi_address        address;
+    uint8_t    sequence;
+    uint16_t   minimum_tick;
+    uint8_t    flags;
+} __attribute__((__packed__));
+
 int acpi_apic_init(void);
 void acpi_print_info(phys_addr_t rsdp, void *rsdt, int acpi_rsdt_n);
 
diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
index e0995c96..676d8751 100644
--- a/i386/i386at/model_dep.c
+++ b/i386/i386at/model_dep.c
@@ -224,6 +224,11 @@ void machine_init(void)
         * Patch the realmode gdt with the correct offset
         */
        gdt_descr_tmp.linear_base += apboot_addr;
+
+       /*
+        * Initialize the HPET
+        */
+       hpet_init();
 #endif
 }
 
-- 
2.43.0





reply via email to

[Prev in Thread] Current Thread [Next in Thread]