[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
- [PATCH 1/2 gnumach] Add HPET timer for small accurate delays,
Damien Zammit <=