summaryrefslogtreecommitdiff
path: root/i386
diff options
context:
space:
mode:
authorDamien Zammit <damien@zamaudio.com>2024-02-07 05:02:15 +0000
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2024-02-08 00:27:18 +0100
commitcaa07fdb903c6d533ce6dad1a66907d862e162ad (patch)
tree923709e5ea823d461f3a53433f0cdd21c9cabf9f /i386
parent69477710c88420603f32e84511d9adc2923d0503 (diff)
Add HPET timer for small accurate delays
TESTED: This works in qemu correctly TESTED: This works on an AMD board with ACPI v2.0 correctly Message-ID: <20240207050158.1640853-3-damien@zamaudio.com>
Diffstat (limited to 'i386')
-rw-r--r--i386/i386/apic.c89
-rw-r--r--i386/i386/apic.h5
-rw-r--r--i386/i386at/acpi_parse_apic.c35
-rw-r--r--i386/i386at/acpi_parse_apic.h23
-rw-r--r--i386/i386at/model_dep.c6
5 files changed, 150 insertions, 8 deletions
diff --git a/i386/i386/apic.c b/i386/i386/apic.c
index feb49c85..0b5be50f 100644
--- a/i386/i386/apic.c
+++ b/i386/i386/apic.c
@@ -26,6 +26,10 @@
#include <kern/printf.h>
#include <kern/kalloc.h>
+/*
+ * Period of HPET timer in nanoseconds
+ */
+uint32_t hpet_period_nsec;
/*
* This dummy structure is needed so that CPU_NUMBER can be called
@@ -362,3 +366,88 @@ 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
+
+/* This function sets up the HPET timer to be in
+ * 32 bit periodic mode and not generating any interrupts.
+ * The timer counts upwards and when it reaches 0xffffffff it
+ * wraps to zero. The timer ticks at a constant rate in nanoseconds which
+ * is stored in hpet_period_nsec variable.
+ */
+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, now;
+ uint32_t max_delay_us = 0xffffffff / NSEC_PER_USEC;
+
+ if (us > max_delay_us) {
+ printf("HPET ERROR: Delay too long, %d usec, truncating to %d usec\n",
+ us, max_delay_us);
+ us = max_delay_us;
+ }
+
+ /* Convert us to HPET ticks */
+ us = (us * NSEC_PER_USEC) / hpet_period_nsec;
+
+ start = HPET32(HPET_COUNTER);
+ do {
+ now = HPET32(HPET_COUNTER);
+ } while (now - start < us);
+}
+
+void
+hpet_mdelay(uint32_t ms)
+{
+ hpet_udelay(ms * 1000);
+}
+
diff --git a/i386/i386/apic.h b/i386/i386/apic.h
index 29387d9d..9eef0d8b 100644
--- a/i386/i386/apic.h
+++ b/i386/i386/apic.h
@@ -252,10 +252,15 @@ 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;
extern int cpu_id_lut[];
+extern uint32_t *hpet_addr;
#endif
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 173b99f5..789271fd 100644
--- a/i386/i386at/model_dep.c
+++ b/i386/i386at/model_dep.c
@@ -221,6 +221,7 @@ void machine_init(void)
*/
/* FIXME: this may not allocate from below 1MB, if within first 16MB */
apboot_addr = vm_page_to_pa(vm_page_grab_contig(PAGE_SIZE, VM_PAGE_SEL_DMA));
+ apboot_addr = 0x3000;
assert (apboot_addr < 0x100000);
/*
@@ -229,6 +230,11 @@ void machine_init(void)
*/
gdt_descr_tmp.linear_base += apboot_addr;
apboot_jmp_offset += apboot_addr;
+
+ /*
+ * Initialize the HPET
+ */
+ hpet_init();
#endif
}