summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2017-05-16 01:57:40 +0200
committerRichard Braun <rbraun@sceen.net>2017-05-16 01:57:40 +0200
commitfca151d3e8b551483cc3f5d43394a1d90b79ee34 (patch)
tree5d589f612de3d85f88bf9b1b61ea6b11141682ed
parent27781cb2cbed5752861483623ac00e740a71678b (diff)
x86/{cpu,pit}: implement cpu_delay using the timestamp counter
-rw-r--r--arch/x86/machine/boot.c2
-rw-r--r--arch/x86/machine/cpu.c47
-rw-r--r--arch/x86/machine/cpu.h19
-rw-r--r--arch/x86/machine/pit.c8
-rw-r--r--arch/x86/machine/pit.h11
5 files changed, 77 insertions, 10 deletions
diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c
index b111a3c0..09ed36eb 100644
--- a/arch/x86/machine/boot.c
+++ b/arch/x86/machine/boot.c
@@ -461,6 +461,7 @@ boot_main(void)
syscnt_setup();
percpu_bootstrap();
trap_setup();
+ pit_setup_free_running();
cpu_setup();
thread_bootstrap();
cga_setup();
@@ -476,7 +477,6 @@ boot_main(void)
boot_save_data();
biosmem_free_usable();
pic_setup();
- pit_setup();
cpu_mp_probe();
kernel_main();
diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c
index ff860ba0..d62b68f9 100644
--- a/arch/x86/machine/cpu.c
+++ b/arch/x86/machine/cpu.c
@@ -34,10 +34,16 @@
#include <machine/cpu.h>
#include <machine/io.h>
#include <machine/lapic.h>
+#include <machine/pit.h>
#include <machine/pmap.h>
#include <machine/trap.h>
#include <vm/vm_page.h>
+/*
+ * Delay used for frequency measurement, in microseconds.
+ */
+#define CPU_FREQ_CAL_DELAY 1000000
+
#define CPU_TYPE_MASK 0x00003000
#define CPU_TYPE_SHIFT 12
#define CPU_FAMILY_MASK 0x00000f00
@@ -141,6 +147,11 @@ struct cpu cpu_desc __percpu;
unsigned int cpu_nr_active __read_mostly;
/*
+ * Processor frequency, assumed fixed and equal on all processors.
+ */
+static uint64_t cpu_freq __read_mostly;
+
+/*
* Interrupt descriptor table.
*/
static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8) __read_mostly;
@@ -154,6 +165,24 @@ static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8) __read_mostly;
static unsigned long cpu_double_fault_handler;
static char cpu_double_fault_stack[STACK_SIZE] __aligned(DATA_ALIGN);
+void
+cpu_delay(unsigned long usecs)
+{
+ int64_t total, prev, count, diff;
+
+ assert(usecs != 0);
+
+ total = DIV_CEIL((int64_t)usecs * cpu_freq, 1000000);
+ prev = cpu_get_tsc();
+
+ do {
+ count = cpu_get_tsc();
+ diff = count - prev;
+ prev = count;
+ total -= diff;
+ } while (total > 0);
+}
+
void * __init
cpu_get_boot_stack(void)
{
@@ -508,6 +537,18 @@ cpu_init(struct cpu *cpu)
cpu->state = CPU_STATE_ON;
}
+static void __init
+cpu_measure_freq(void)
+{
+ uint64_t start, end;
+
+ start = cpu_get_tsc();
+ pit_delay(CPU_FREQ_CAL_DELAY);
+ end = cpu_get_tsc();
+
+ cpu_freq = (end - start) / (1000000 / CPU_FREQ_CAL_DELAY);
+}
+
void __init
cpu_setup(void)
{
@@ -518,6 +559,8 @@ cpu_setup(void)
cpu->double_fault_stack = cpu_double_fault_stack; /* XXX */
cpu_init(cpu);
cpu_nr_active = 1;
+
+ cpu_measure_freq();
}
static void __init
@@ -566,6 +609,10 @@ cpu_info(const struct cpu *cpu)
printf("cpu%u: address widths: physical: %hu, virtual: %hu\n",
cpu->id, cpu->phys_addr_width, cpu->virt_addr_width);
}
+
+ printf("cpu%u: frequency: %llu.%02llu MHz\n", cpu->id,
+ (unsigned long long)cpu_freq / 1000000,
+ (unsigned long long)cpu_freq % 1000000);
}
void __init
diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h
index 3bf1ade3..2360ea7d 100644
--- a/arch/x86/machine/cpu.h
+++ b/arch/x86/machine/cpu.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2014 Richard Braun.
+ * Copyright (c) 2010-2017 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -479,6 +479,15 @@ cpu_set_msr(uint32_t msr, uint32_t high, uint32_t low)
asm volatile("wrmsr" : : "c" (msr), "a" (low), "d" (high));
}
+static __always_inline uint64_t
+cpu_get_tsc(void)
+{
+ uint32_t high, low;
+
+ asm volatile("rdtsc" : "=a" (low), "=d" (high));
+ return ((uint64_t)high << 32) | low;
+}
+
/*
* Flush non-global TLB entries.
*
@@ -528,13 +537,9 @@ cpu_tlb_flush_va(unsigned long va)
}
/*
- * XXX For now, directly use the PIT.
+ * Busy-wait for a given amount of time, in microseconds.
*/
-static inline void
-cpu_delay(unsigned long usecs)
-{
- pit_delay(usecs);
-}
+void cpu_delay(unsigned long usecs);
/*
* Return the address of the boot stack allocated for the current processor.
diff --git a/arch/x86/machine/pit.c b/arch/x86/machine/pit.c
index d3fece51..bf53ee11 100644
--- a/arch/x86/machine/pit.c
+++ b/arch/x86/machine/pit.c
@@ -45,7 +45,7 @@
#define PIT_MAX_COUNT 0xffff
void __init
-pit_setup(void)
+pit_setup_free_running(void)
{
io_write_byte(PIT_PORT_MODE, PIT_MODE_RATE_GEN | PIT_MODE_RW_LSB
| PIT_MODE_RW_MSB);
@@ -53,6 +53,12 @@ pit_setup(void)
io_write_byte(PIT_PORT_COUNTER0, PIT_MAX_COUNT >> 8);
}
+void __init
+pit_setup(void)
+{
+ /* TODO Implement */
+}
+
static unsigned int
pit_read(void)
{
diff --git a/arch/x86/machine/pit.h b/arch/x86/machine/pit.h
index 3b4da60b..7bf7a7b7 100644
--- a/arch/x86/machine/pit.h
+++ b/arch/x86/machine/pit.h
@@ -19,7 +19,16 @@
#define _X86_PIT_H
/*
- * Set up the i8253 Programmable Interval Timer.
+ * Initialize the PIT as a free running counter.
+ *
+ * This is used during early initialization to measure the frequency of
+ * other clocks. The PIT is used despite its lack of precision because
+ * it's the only architectural timer with a known frequency.
+ */
+void pit_setup_free_running(void);
+
+/*
+ * Initialize the pit module.
*/
void pit_setup(void);