summaryrefslogtreecommitdiff
path: root/sys/arch/amd64
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r--sys/arch/amd64/lapic.c71
1 files changed, 70 insertions, 1 deletions
diff --git a/sys/arch/amd64/lapic.c b/sys/arch/amd64/lapic.c
index 6a5ed9a..c020f19 100644
--- a/sys/arch/amd64/lapic.c
+++ b/sys/arch/amd64/lapic.c
@@ -31,11 +31,12 @@
#include <machine/lapicvar.h>
#include <machine/cpuid.h>
#include <machine/msr.h>
-#include <sys/types.h>
#include <sys/cdefs.h>
+#include <sys/timer.h>
#include <sys/syslog.h>
#include <sys/panic.h>
#include <sys/mmio.h>
+#include <dev/timer/hpet.h>
/*
* Only calls KINFO if we are the BSP.
@@ -52,7 +53,9 @@ __MODULE_NAME("lapic");
__KERNEL_META("$Vega$: lapic.c, Ian Marco Moffett, "
"Local APIC driver");
+
static void *lapic_base = NULL;
+static struct timer lapic_timer = { 0 };
/*
* Returns true if LAPIC is supported.
@@ -99,6 +102,28 @@ lapic_writel(uint32_t reg, uint32_t val)
}
/*
+ * Calibrates the Local APIC timer - Timer abstraction
+ */
+static size_t
+lapic_timer_calibrate(void)
+{
+ size_t freq_hz;
+
+ lapic_timer_init(&freq_hz);
+ return freq_hz;
+}
+
+/*
+ * Stops the Local APIC timer - Timer abstraction
+ */
+static void
+lapic_timer_stop(void)
+{
+ lapic_writel(LAPIC_INIT_CNT, 0);
+ lapic_writel(LAPIC_LVT_TMR, LAPIC_LVT_MASK);
+}
+
+/*
* Set bits within a LAPIC register
* without overwriting the whole thing.
*
@@ -131,6 +156,42 @@ lapic_reg_clear(uint32_t reg, uint32_t value)
}
void
+lapic_timer_init(size_t *freq_out)
+{
+ uint32_t ticks_per_10ms;
+ size_t freq_hz;
+ const uint32_t MHZ_DIV = 1000000;
+ const uint32_t GHZ_DIV = 1000000000;
+ const uint32_t MAX_SAMPLES = 0xFFFFFFFF;
+
+ lapic_writel(LAPIC_DCR, 3); /* Use divider 16 */
+ lapic_writel(LAPIC_INIT_CNT, MAX_SAMPLES); /* Set the initial count */
+
+ hpet_msleep(10); /* Wait 10ms (10000 usec) */
+ lapic_writel(LAPIC_LVT_TMR, LAPIC_LVT_MASK); /* Stop the timer w/ LVT mask bit */
+
+ /* Sanity check */
+ if (freq_out == NULL)
+ panic("lapic_timer_init() freq_out NULL\n");
+
+ /* Calculate frequency in Hz */
+ ticks_per_10ms = MAX_SAMPLES - lapic_readl(LAPIC_CUR_CNT);
+ freq_hz = ticks_per_10ms * 10000; /* 10000 since we waited 10ms */
+
+ /* Check if in MHz or GHz range */
+ if (freq_hz >= GHZ_DIV) {
+ BSP_KINFO("BSP Local APIC Timer frequency: %d GHz\n", freq_hz / GHZ_DIV);
+ } else if (freq_hz >= MHZ_DIV) {
+ BSP_KINFO("BSP Local APIC Timer frequency: %d MHz\n", freq_hz / MHZ_DIV);
+ } else {
+ BSP_KINFO("BSP Local APIC Timer frequency: %d Hz\n", freq_hz);
+ }
+
+ /* Divide by 10000 to get ticks */
+ *freq_out = freq_hz;
+}
+
+void
lapic_set_base(void *mmio_base)
{
if (lapic_base == NULL)
@@ -164,4 +225,12 @@ lapic_init(void)
BSP_KINFO("Enabled Local APIC for BSP\n");
lapic_writel(LAPIC_LDR, LAPIC_STARTUP_LID);
+
+ /* Setup the timer descriptor */
+ lapic_timer.name = "LAPIC_INTEGRATED_TIMER";
+ lapic_timer.calibrate = lapic_timer_calibrate;
+ lapic_timer.stop = lapic_timer_stop;
+
+ /* Register the timer for scheduler usage */
+ register_timer(TIMER_SCHED, &lapic_timer);
}