summaryrefslogtreecommitdiffstats
path: root/src/arch
diff options
context:
space:
mode:
authorMichael Brown2008-10-12 20:56:52 +0200
committerMichael Brown2008-10-12 21:22:02 +0200
commit16f1e35775c972ba8e02bc2d97d7a2eb333eae1b (patch)
treef653099d4b230c9807986aa2b4cd89dce2cffe9b /src/arch
parent[process] Add DBG2() messages to help track down frozen processes (diff)
downloadipxe-16f1e35775c972ba8e02bc2d97d7a2eb333eae1b.tar.gz
ipxe-16f1e35775c972ba8e02bc2d97d7a2eb333eae1b.tar.xz
ipxe-16f1e35775c972ba8e02bc2d97d7a2eb333eae1b.zip
[timer] Formalise the timer API
We now have two implementations for the timer API: one using the time-of-day counter at 40:70 and one using RDTSC. Both make use of timer2_udelay().
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/i386/core/rdtsc_timer.c87
-rw-r--r--src/arch/i386/core/timer2.c (renamed from src/arch/i386/core/i386_timer.c)22
-rw-r--r--src/arch/i386/drivers/timer_bios.c57
-rw-r--r--src/arch/i386/drivers/timer_rdtsc.c69
-rw-r--r--src/arch/i386/include/bios.h1
-rw-r--r--src/arch/i386/include/bits/timer.h13
-rw-r--r--src/arch/i386/include/bits/timer2.h8
-rw-r--r--src/arch/i386/include/gpxe/bios_timer.h42
-rw-r--r--src/arch/i386/include/gpxe/rdtsc_timer.h37
-rw-r--r--src/arch/i386/include/gpxe/timer2.h12
-rw-r--r--src/arch/i386/interface/pcbios/bios_timer.c63
11 files changed, 263 insertions, 148 deletions
diff --git a/src/arch/i386/core/rdtsc_timer.c b/src/arch/i386/core/rdtsc_timer.c
new file mode 100644
index 000000000..443c8adaf
--- /dev/null
+++ b/src/arch/i386/core/rdtsc_timer.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/** @file
+ *
+ * RDTSC timer
+ *
+ */
+
+#include <assert.h>
+#include <gpxe/timer.h>
+#include <gpxe/timer2.h>
+
+/**
+ * Number of TSC ticks per microsecond
+ *
+ * This is calibrated on the first use of the timer.
+ */
+static unsigned long rdtsc_ticks_per_usec;
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs Number of microseconds for which to delay
+ */
+static void rdtsc_udelay ( unsigned long usecs ) {
+ unsigned long start;
+ unsigned long elapsed;
+
+ /* Sanity guard, since we may divide by this */
+ if ( ! usecs )
+ usecs = 1;
+
+ start = currticks();
+ if ( rdtsc_ticks_per_usec ) {
+ /* Already calibrated; busy-wait until done */
+ do {
+ elapsed = ( currticks() - start );
+ } while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) );
+ } else {
+ /* Not yet calibrated; use timer2 and calibrate
+ * based on result.
+ */
+ timer2_udelay ( usecs );
+ elapsed = ( currticks() - start );
+ rdtsc_ticks_per_usec = ( elapsed / usecs );
+ DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs "
+ "(%ld MHz)\n", elapsed, usecs,
+ ( rdtsc_ticks_per_usec << TSC_SHIFT ) );
+ }
+}
+
+/**
+ * Get number of ticks per second
+ *
+ * @ret ticks_per_sec Number of ticks per second
+ */
+static unsigned long rdtsc_ticks_per_sec ( void ) {
+
+ /* Calibrate timer, if not already done */
+ if ( ! rdtsc_ticks_per_usec )
+ udelay ( 1 );
+
+ /* Sanity check */
+ assert ( rdtsc_ticks_per_usec != 0 );
+
+ return ( rdtsc_ticks_per_usec * 1000 * 1000 );
+}
+
+PROVIDE_TIMER ( rdtsc, udelay, rdtsc_udelay );
+PROVIDE_TIMER_INLINE ( rdtsc, currticks );
+PROVIDE_TIMER ( rdtsc, ticks_per_sec, rdtsc_ticks_per_sec );
diff --git a/src/arch/i386/core/i386_timer.c b/src/arch/i386/core/timer2.c
index 3325bb0b6..bb589ecc2 100644
--- a/src/arch/i386/core/i386_timer.c
+++ b/src/arch/i386/core/timer2.c
@@ -12,12 +12,11 @@
*/
#include <stddef.h>
-#include <bits/timer2.h>
-#include <gpxe/timer.h>
+#include <gpxe/timer2.h>
#include <gpxe/io.h>
/* Timers tick over at this rate */
-#define TIMER2_TICK_RATE 1193180U
+#define TIMER2_TICKS_PER_SEC 1193180U
/* Parallel Peripheral Controller Port B */
#define PPC_PORTB 0x61
@@ -52,8 +51,7 @@
#define BINARY_COUNT 0x00
#define BCD_COUNT 0x01
-static void load_timer2(unsigned int ticks)
-{
+static void load_timer2 ( unsigned int ticks ) {
/*
* Now let's take care of PPC channel 2
*
@@ -75,15 +73,13 @@ static void load_timer2(unsigned int ticks)
outb(ticks >> 8, TIMER2_PORT);
}
-static int timer2_running(void)
-{
+static int timer2_running ( void ) {
return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0);
}
-void i386_timer2_udelay(unsigned int usecs)
-{
- load_timer2((usecs * TIMER2_TICK_RATE)/USECS_IN_SEC);
- while (timer2_running())
- ;
+void timer2_udelay ( unsigned long usecs ) {
+ load_timer2 ( ( usecs * TIMER2_TICKS_PER_SEC ) / ( 1000 * 1000 ) );
+ while (timer2_running()) {
+ /* Do nothing */
+ }
}
-
diff --git a/src/arch/i386/drivers/timer_bios.c b/src/arch/i386/drivers/timer_bios.c
deleted file mode 100644
index f9caf8d9a..000000000
--- a/src/arch/i386/drivers/timer_bios.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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
deleted file mode 100644
index f4ede558b..000000000
--- a/src/arch/i386/drivers/timer_rdtsc.c
+++ /dev/null
@@ -1,69 +0,0 @@
-
-#include <gpxe/init.h>
-#include <gpxe/timer.h>
-#include <errno.h>
-#include <stdio.h>
-#include <bits/cpu.h>
-#include <bits/timer2.h>
-#include <gpxe/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,
-};
-
diff --git a/src/arch/i386/include/bios.h b/src/arch/i386/include/bios.h
index 630a898b3..5f9d6ab09 100644
--- a/src/arch/i386/include/bios.h
+++ b/src/arch/i386/include/bios.h
@@ -5,7 +5,6 @@
#define BDA_FBMS 0x0013
#define BDA_NUM_DRIVES 0x0075
-extern unsigned long currticks ( void );
extern void cpu_nap ( void );
#endif /* BIOS_H */
diff --git a/src/arch/i386/include/bits/timer.h b/src/arch/i386/include/bits/timer.h
new file mode 100644
index 000000000..99666d840
--- /dev/null
+++ b/src/arch/i386/include/bits/timer.h
@@ -0,0 +1,13 @@
+#ifndef _BITS_TIMER_H
+#define _BITS_TIMER_H
+
+/** @file
+ *
+ * i386-specific timer API implementations
+ *
+ */
+
+#include <gpxe/bios_timer.h>
+#include <gpxe/rdtsc_timer.h>
+
+#endif /* _BITS_TIMER_H */
diff --git a/src/arch/i386/include/bits/timer2.h b/src/arch/i386/include/bits/timer2.h
deleted file mode 100644
index 83923b299..000000000
--- a/src/arch/i386/include/bits/timer2.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef BITS_TIMER2_H
-#define BITS_TIMER2_H
-
-#include <stddef.h>
-
-void i386_timer2_udelay(unsigned int usecs);
-
-#endif
diff --git a/src/arch/i386/include/gpxe/bios_timer.h b/src/arch/i386/include/gpxe/bios_timer.h
new file mode 100644
index 000000000..7e3caa3c6
--- /dev/null
+++ b/src/arch/i386/include/gpxe/bios_timer.h
@@ -0,0 +1,42 @@
+#ifndef _GPXE_BIOS_TIMER_H
+#define _GPXE_BIOS_TIMER_H
+
+/** @file
+ *
+ * BIOS timer
+ *
+ */
+
+#ifdef TIMER_PCBIOS
+#define TIMER_PREFIX_pcbios
+#else
+#define TIMER_PREFIX_pcbios __pcbios_
+#endif
+
+#include <gpxe/timer2.h>
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs Number of microseconds for which to delay
+ */
+static inline __always_inline void
+TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) {
+ /* BIOS timer is not high-resolution enough for udelay(), so
+ * we use timer2
+ */
+ timer2_udelay ( usecs );
+}
+
+/**
+ * Get number of ticks per second
+ *
+ * @ret ticks_per_sec Number of ticks per second
+ */
+static inline __always_inline unsigned long
+TIMER_INLINE ( pcbios, ticks_per_sec ) ( void ) {
+ /* BIOS timer ticks over at 18.2 ticks per second */
+ return 18;
+}
+
+#endif /* _GPXE_BIOS_TIMER_H */
diff --git a/src/arch/i386/include/gpxe/rdtsc_timer.h b/src/arch/i386/include/gpxe/rdtsc_timer.h
new file mode 100644
index 000000000..0e03d707e
--- /dev/null
+++ b/src/arch/i386/include/gpxe/rdtsc_timer.h
@@ -0,0 +1,37 @@
+#ifndef _GPXE_RDTSC_TIMER_H
+#define _GPXE_RDTSC_TIMER_H
+
+/** @file
+ *
+ * RDTSC timer
+ *
+ */
+
+#ifdef TIMER_RDTSC
+#define TIMER_PREFIX_rdtsc
+#else
+#define TIMER_PREFIX_rdtsc __rdtsc_
+#endif
+
+/**
+ * RDTSC values can easily overflow an unsigned long. We discard the
+ * low-order bits in order to obtain sensibly-scaled values.
+ */
+#define TSC_SHIFT 8
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks Current time, in ticks
+ */
+static inline __always_inline unsigned long
+TIMER_INLINE ( rdtsc, currticks ) ( void ) {
+ unsigned long ticks;
+
+ __asm__ __volatile__ ( "rdtsc\n\t"
+ "shrdl %1, %%edx, %%eax\n\t"
+ : "=a" ( ticks ) : "i" ( TSC_SHIFT ) : "edx" );
+ return ticks;
+}
+
+#endif /* _GPXE_RDTSC_TIMER_H */
diff --git a/src/arch/i386/include/gpxe/timer2.h b/src/arch/i386/include/gpxe/timer2.h
new file mode 100644
index 000000000..59705fa26
--- /dev/null
+++ b/src/arch/i386/include/gpxe/timer2.h
@@ -0,0 +1,12 @@
+#ifndef _GPXE_TIMER2_H
+#define _GPXE_TIMER2_H
+
+/** @file
+ *
+ * Timer chip control
+ *
+ */
+
+extern void timer2_udelay ( unsigned long usecs );
+
+#endif /* _GPXE_TIMER2_H */
diff --git a/src/arch/i386/interface/pcbios/bios_timer.c b/src/arch/i386/interface/pcbios/bios_timer.c
new file mode 100644
index 000000000..0b475ea37
--- /dev/null
+++ b/src/arch/i386/interface/pcbios/bios_timer.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/** @file
+ *
+ * BIOS timer
+ *
+ */
+
+#include <gpxe/timer.h>
+#include <realmode.h>
+#include <bios.h>
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks Current time, in ticks
+ *
+ * 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 unsigned long 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 );
+}
+
+PROVIDE_TIMER_INLINE ( pcbios, udelay );
+PROVIDE_TIMER ( pcbios, currticks, bios_currticks );
+PROVIDE_TIMER_INLINE ( pcbios, ticks_per_sec );