summaryrefslogtreecommitdiffstats
path: root/src/arch/i386/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/i386/drivers')
-rw-r--r--src/arch/i386/drivers/timer_bios.c57
-rw-r--r--src/arch/i386/drivers/timer_rdtsc.c69
2 files changed, 126 insertions, 0 deletions
diff --git a/src/arch/i386/drivers/timer_bios.c b/src/arch/i386/drivers/timer_bios.c
new file mode 100644
index 000000000..f9caf8d9a
--- /dev/null
+++ b/src/arch/i386/drivers/timer_bios.c
@@ -0,0 +1,57 @@
+/*
+ * Etherboot routines for PCBIOS firmware.
+ *
+ * Body of routines taken from old pcbios.S
+ */
+
+#include <gpxe/init.h>
+#include <gpxe/timer.h>
+#include <stdio.h>
+#include <realmode.h>
+#include <bios.h>
+#include <bits/timer2.h>
+
+/* A bit faster actually, but we don't care. */
+#define TIMER2_TICKS_PER_SEC 18
+
+/*
+ * Use direct memory access to BIOS variables, longword 0040:006C (ticks
+ * today) and byte 0040:0070 (midnight crossover flag) instead of calling
+ * timeofday BIOS interrupt.
+ */
+
+static tick_t bios_currticks ( void ) {
+ static int days = 0;
+ uint32_t ticks;
+ uint8_t midnight;
+
+ /* Re-enable interrupts so that the timer interrupt can occur */
+ __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cli\n\t" ) : : );
+
+ get_real ( ticks, BDA_SEG, 0x006c );
+ get_real ( midnight, BDA_SEG, 0x0070 );
+
+ if ( midnight ) {
+ midnight = 0;
+ put_real ( midnight, BDA_SEG, 0x0070 );
+ days += 0x1800b0;
+ }
+
+ return ( (days + ticks) * (USECS_IN_SEC / TIMER2_TICKS_PER_SEC) );
+}
+
+static int bios_ts_init(void)
+{
+ DBG("BIOS timer installed\n");
+ return 0;
+}
+
+struct timer bios_ts __timer ( 02 ) = {
+ .init = bios_ts_init,
+ .udelay = i386_timer2_udelay,
+ .currticks = bios_currticks,
+};
+
diff --git a/src/arch/i386/drivers/timer_rdtsc.c b/src/arch/i386/drivers/timer_rdtsc.c
new file mode 100644
index 000000000..09b7df2f0
--- /dev/null
+++ b/src/arch/i386/drivers/timer_rdtsc.c
@@ -0,0 +1,69 @@
+
+#include <gpxe/init.h>
+#include <gpxe/timer.h>
+#include <errno.h>
+#include <stdio.h>
+#include <bits/cpu.h>
+#include <bits/timer2.h>
+#include <io.h>
+
+
+#define rdtsc(low,high) \
+ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
+
+#define rdtscll(val) \
+ __asm__ __volatile__ ("rdtsc" : "=A" (val))
+
+
+/* Measure how many clocks we get in one microsecond */
+static inline uint64_t calibrate_tsc(void)
+{
+
+ uint64_t rdtsc_start;
+ uint64_t rdtsc_end;
+
+ rdtscll(rdtsc_start);
+ i386_timer2_udelay(USECS_IN_MSEC);
+ rdtscll(rdtsc_end);
+
+ return (rdtsc_end - rdtsc_start) / USECS_IN_MSEC;
+}
+
+static uint32_t clocks_per_usec = 0;
+
+/* We measure time in microseconds. */
+static tick_t rdtsc_currticks(void)
+{
+ uint64_t clocks;
+
+ /* Read the Time Stamp Counter */
+ rdtscll(clocks);
+
+ return clocks / clocks_per_usec;
+}
+
+static int rdtsc_ts_init(void)
+{
+
+ struct cpuinfo_x86 cpu_info;
+
+ get_cpuinfo(&cpu_info);
+ if (cpu_info.features & X86_FEATURE_TSC) {
+ clocks_per_usec= calibrate_tsc();
+ if (clocks_per_usec) {
+ DBG("RDTSC ticksource installed. CPU running at %ld Mhz\n",
+ clocks_per_usec);
+ return 0;
+ }
+ }
+
+ DBG("RDTSC ticksource not available on this machine.\n");
+ return -ENODEV;
+}
+
+struct timer rdtsc_ts __timer (01) = {
+ .init = rdtsc_ts_init,
+ .udelay = generic_currticks_udelay,
+ .currticks = rdtsc_currticks,
+};
+