]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[time] Allow timer to be selected at runtime
authorMichael Brown <mcb30@ipxe.org>
Wed, 25 Jan 2017 20:59:15 +0000 (20:59 +0000)
committerMichael Brown <mcb30@ipxe.org>
Thu, 26 Jan 2017 08:17:37 +0000 (08:17 +0000)
Allow the active timer (providing udelay() and currticks()) to be
selected at runtime based on probing during the INIT_EARLY stage of
initialisation.

TICKS_PER_SEC is now a fixed compile-time constant for all builds, and
is independent of the underlying clock tick rate.  We choose the value
1024 to allow multiplications and divisions on seconds to be converted
to bit shifts.

TICKS_PER_MS is defined as 1, allowing multiplications and divisions
on milliseconds to be omitted entirely.  The 2% inaccuracy in this
definition is negligible when using the standard BIOS timer (running
at around 18.2Hz).

TIMER_RDTSC now checks for a constant TSC before claiming to be a
usable timer.  (This timer can be tested in KVM via the command-line
option "-cpu host,+invtsc".)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
24 files changed:
src/arch/arm/include/bits/timer.h [deleted file]
src/arch/x86/core/rdtsc_timer.c
src/arch/x86/include/bios.h
src/arch/x86/include/bits/errfile.h
src/arch/x86/include/bits/timer.h [deleted file]
src/arch/x86/include/ipxe/bios_timer.h [deleted file]
src/arch/x86/include/ipxe/cpuid.h
src/arch/x86/include/ipxe/rdtsc_timer.h [deleted file]
src/arch/x86/interface/pcbios/bios_timer.c
src/config/config_timer.c [new file with mode: 0644]
src/core/parseopt.c
src/core/timer.c
src/drivers/net/ath/ath5k/ath5k.c
src/drivers/net/forcedeth.c
src/hci/commands/time_cmd.c
src/include/ipxe/efi/efi_timer.h [deleted file]
src/include/ipxe/linux/linux_timer.h [deleted file]
src/include/ipxe/timer.h
src/include/unistd.h
src/interface/efi/efi_timer.c
src/interface/linux/linux_timer.c
src/net/80211/net80211.c
src/net/fcoe.c
src/net/stp.c

diff --git a/src/arch/arm/include/bits/timer.h b/src/arch/arm/include/bits/timer.h
deleted file mode 100644 (file)
index 64e7d31..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _BITS_TIMER_H
-#define _BITS_TIMER_H
-
-/** @file
- *
- * ARM-specific timer API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#endif /* _BITS_TIMER_H */
index e720a239cc9e5cb36a3941b7e79c7e64781596ce..ed5151503f0d17d1dc0dad854fea8fcb907001de 100644 (file)
@@ -29,16 +29,70 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  *
  */
 
-#include <assert.h>
+#include <string.h>
+#include <errno.h>
 #include <ipxe/timer.h>
+#include <ipxe/cpuid.h>
 #include <ipxe/pit8254.h>
 
+/** Number of microseconds to use for TSC calibration */
+#define TSC_CALIBRATE_US 1024
+
+/** TSC increment per microsecond */
+static unsigned long tsc_per_us;
+
+/** Minimum resolution for scaled TSC timer */
+#define TSC_SCALED_HZ 32
+
+/** TSC scale (expressed as a bit shift)
+ *
+ * We use this to avoid the need for 64-bit divsion on 32-bit systems.
+ */
+static unsigned int tsc_scale;
+
+/** Number of timer ticks per scaled TSC increment */
+static unsigned long ticks_per_scaled_tsc;
+
+/** Colour for debug messages */
+#define colour &tsc_per_us
+
+/**
+ * Get raw TSC value
+ *
+ * @ret tsc            Raw TSC value
+ */
+static inline __always_inline unsigned long rdtsc_raw ( void ) {
+       unsigned long raw;
+
+       __asm__ __volatile__ ( "rdtsc\n\t" : "=a" ( raw ) : : "edx" );
+       return raw;
+}
+
+/**
+ * Get TSC value, shifted to avoid rollover within a realistic timescale
+ *
+ * @ret tsc            Scaled TSC value
+ */
+static inline __always_inline unsigned long rdtsc_scaled ( void ) {
+       unsigned long scaled;
+
+       __asm__ __volatile__ ( "rdtsc\n\t"
+                              "shrdl %b1, %%edx, %%eax\n\t"
+                              : "=a" ( scaled ) : "c" ( tsc_scale ) : "edx" );
+       return scaled;
+}
+
 /**
- * Number of TSC ticks per microsecond
+ * Get current system time in ticks
  *
- * This is calibrated on the first use of the timer.
+ * @ret ticks          Current time, in ticks
  */
-static unsigned long rdtsc_ticks_per_usec;
+static unsigned long rdtsc_currticks ( void ) {
+       unsigned long scaled;
+
+       scaled = rdtsc_scaled();
+       return ( scaled * ticks_per_scaled_tsc );
+}
 
 /**
  * Delay for a fixed number of microseconds
@@ -48,47 +102,76 @@ static unsigned long rdtsc_ticks_per_usec;
 static void rdtsc_udelay ( unsigned long usecs ) {
        unsigned long start;
        unsigned long elapsed;
+       unsigned long threshold;
 
-       /* 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 8254 PIT and calibrate
-                * based on result.
-                */
-               pit8254_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 ) );
-       }
+       start = rdtsc_raw();
+       threshold = ( usecs * tsc_per_us );
+       do {
+               elapsed = ( rdtsc_raw() - start );
+       } while ( elapsed < threshold );
 }
 
 /**
- * Get number of ticks per second
+ * Probe RDTSC timer
  *
- * @ret ticks_per_sec  Number of ticks per second
+ * @ret rc             Return status code
  */
-static unsigned long rdtsc_ticks_per_sec ( void ) {
+static int rdtsc_probe ( void ) {
+       unsigned long before;
+       unsigned long after;
+       unsigned long elapsed;
+       uint32_t apm;
+       uint32_t discard_a;
+       uint32_t discard_b;
+       uint32_t discard_c;
+       int rc;
 
-       /* Calibrate timer, if not already done */
-       if ( ! rdtsc_ticks_per_usec )
-               udelay ( 1 );
+       /* Check that TSC is invariant */
+       if ( ( rc = cpuid_supported ( CPUID_APM ) ) != 0 ) {
+               DBGC ( colour, "RDTSC cannot determine APM features: %s\n",
+                      strerror ( rc ) );
+               return rc;
+       }
+       cpuid ( CPUID_APM, &discard_a, &discard_b, &discard_c, &apm );
+       if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) {
+               DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n",
+                      apm );
+               return -ENOTTY;
+       }
 
-       /* Sanity check */
-       assert ( rdtsc_ticks_per_usec != 0 );
+       /* Calibrate udelay() timer via 8254 PIT */
+       before = rdtsc_raw();
+       pit8254_udelay ( TSC_CALIBRATE_US );
+       after = rdtsc_raw();
+       elapsed = ( after - before );
+       tsc_per_us = ( elapsed / TSC_CALIBRATE_US );
+       if ( ! tsc_per_us ) {
+               DBGC ( colour, "RDTSC has zero TSC per microsecond\n" );
+               return -EIO;
+       }
+
+       /* Calibrate currticks() scaling factor */
+       tsc_scale = 31;
+       ticks_per_scaled_tsc = ( ( 1UL << tsc_scale ) /
+                                ( tsc_per_us * ( 1000000 / TICKS_PER_SEC ) ) );
+       while ( ticks_per_scaled_tsc > ( TICKS_PER_SEC / TSC_SCALED_HZ ) ) {
+               tsc_scale--;
+               ticks_per_scaled_tsc >>= 1;
+       }
+       DBGC ( colour, "RDTSC has %ld tsc per us, %ld ticks per 2^%d tsc\n",
+              tsc_per_us, ticks_per_scaled_tsc, tsc_scale );
+       if ( ! ticks_per_scaled_tsc ) {
+               DBGC ( colour, "RDTSC has zero ticks per TSC\n" );
+               return -EIO;
+       }
 
-       return ( rdtsc_ticks_per_usec * 1000 * 1000 );
+       return 0;
 }
 
-PROVIDE_TIMER ( rdtsc, udelay, rdtsc_udelay );
-PROVIDE_TIMER_INLINE ( rdtsc, currticks );
-PROVIDE_TIMER ( rdtsc, ticks_per_sec, rdtsc_ticks_per_sec );
+/** RDTSC timer */
+struct timer rdtsc_timer __timer ( TIMER_PREFERRED ) = {
+       .name = "rdtsc",
+       .probe = rdtsc_probe,
+       .currticks = rdtsc_currticks,
+       .udelay = rdtsc_udelay,
+};
index a5a5d887c448c6c1d4df12531baec50582071770..14e7acbc79bf4161f5c19d33c0509207a5677619 100644 (file)
@@ -7,6 +7,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define BDA_EBDA 0x000e
 #define BDA_EQUIPMENT_WORD 0x0010
 #define BDA_FBMS 0x0013
+#define BDA_TICKS 0x006c
+#define BDA_MIDNIGHT 0x0070
 #define BDA_REBOOT 0x0072
 #define BDA_REBOOT_WARM 0x1234
 #define BDA_NUM_DRIVES 0x0075
index 105cdf5d29d83a6ac7e77ab962374bde9b31c16f..9d1fed7f623b59658f4f2382b3ef3dfa27a5cfbb 100644 (file)
@@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define ERRFILE_rtc_entropy    ( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 )
 #define ERRFILE_acpipwr                ( ERRFILE_ARCH | ERRFILE_CORE | 0x00100000 )
 #define ERRFILE_cpuid          ( ERRFILE_ARCH | ERRFILE_CORE | 0x00110000 )
+#define ERRFILE_rdtsc_timer    ( ERRFILE_ARCH | ERRFILE_CORE | 0x00120000 )
 
 #define ERRFILE_bootsector     ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
 #define ERRFILE_bzimage               ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )
diff --git a/src/arch/x86/include/bits/timer.h b/src/arch/x86/include/bits/timer.h
deleted file mode 100644 (file)
index b0ff5ee..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef _BITS_TIMER_H
-#define _BITS_TIMER_H
-
-/** @file
- *
- * x86-specific timer API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <ipxe/bios_timer.h>
-#include <ipxe/rdtsc_timer.h>
-
-#endif /* _BITS_TIMER_H */
diff --git a/src/arch/x86/include/ipxe/bios_timer.h b/src/arch/x86/include/ipxe/bios_timer.h
deleted file mode 100644 (file)
index 6b88a62..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef _IPXE_BIOS_TIMER_H
-#define _IPXE_BIOS_TIMER_H
-
-/** @file
- *
- * BIOS timer
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#ifdef TIMER_PCBIOS
-#define TIMER_PREFIX_pcbios
-#else
-#define TIMER_PREFIX_pcbios __pcbios_
-#endif
-
-#include <ipxe/pit8254.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 the 8254 Programmable Interval Timer.
-        */
-       pit8254_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 /* _IPXE_BIOS_TIMER_H */
index 2e2cc7c1ae713ae96a097e65fb75291783635e28..a9df9f0de579ab7314ba9525e2618cf083a54d55 100644 (file)
@@ -57,6 +57,12 @@ struct x86_features {
 /** Get CPU model */
 #define CPUID_MODEL 0x80000002UL
 
+/** Get APM information */
+#define CPUID_APM 0x80000007UL
+
+/** Invariant TSC */
+#define CPUID_APM_EDX_TSC_INVARIANT 0x00000100UL
+
 /**
  * Issue CPUID instruction
  *
diff --git a/src/arch/x86/include/ipxe/rdtsc_timer.h b/src/arch/x86/include/ipxe/rdtsc_timer.h
deleted file mode 100644 (file)
index 598f4bb..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef _IPXE_RDTSC_TIMER_H
-#define _IPXE_RDTSC_TIMER_H
-
-/** @file
- *
- * RDTSC timer
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#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 /* _IPXE_RDTSC_TIMER_H */
index 3299c9aaecba2a7f470057cda1947d3193371a26..49e1d22658e997e494bd57a93e819fd0f0297cd5 100644 (file)
@@ -32,6 +32,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/timer.h>
 #include <realmode.h>
 #include <bios.h>
+#include <ipxe/pit8254.h>
+
+/** Number of ticks per day
+ *
+ * This seems to be the normative value, as used by e.g. SeaBIOS to
+ * decide when to set the midnight rollover flag.
+ */
+#define BIOS_TICKS_PER_DAY 0x1800b0
+
+/** Number of ticks per BIOS tick */
+#define TICKS_PER_BIOS_TICK \
+       ( ( TICKS_PER_SEC * 60 * 60 * 24 ) / BIOS_TICKS_PER_DAY )
 
 /**
  * Get current system time in ticks
@@ -43,7 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  * of calling timeofday BIOS interrupt.
  */
 static unsigned long bios_currticks ( void ) {
-       static int days = 0;
+       static uint32_t offset;
        uint32_t ticks;
        uint8_t midnight;
 
@@ -53,18 +65,25 @@ static unsigned long bios_currticks ( void ) {
                               "nop\n\t"
                               "cli\n\t" );
 
-       get_real ( ticks, BDA_SEG, 0x006c );
-       get_real ( midnight, BDA_SEG, 0x0070 );
+       /* Read current BIOS time of day */
+       get_real ( ticks, BDA_SEG, BDA_TICKS );
+       get_real ( midnight, BDA_SEG, BDA_MIDNIGHT );
 
+       /* Handle midnight rollover */
        if ( midnight ) {
                midnight = 0;
-               put_real ( midnight, BDA_SEG, 0x0070 );
-               days += 0x1800b0;
+               put_real ( midnight, BDA_SEG, BDA_MIDNIGHT );
+               offset += BIOS_TICKS_PER_DAY;
        }
+       ticks += offset;
 
-       return ( days + ticks );
+       /* Convert to timer ticks */
+       return ( ticks * TICKS_PER_BIOS_TICK );
 }
 
-PROVIDE_TIMER_INLINE ( pcbios, udelay );
-PROVIDE_TIMER ( pcbios, currticks, bios_currticks );
-PROVIDE_TIMER_INLINE ( pcbios, ticks_per_sec );
+/** BIOS timer */
+struct timer bios_timer __timer ( TIMER_NORMAL ) = {
+       .name = "bios",
+       .currticks = bios_currticks,
+       .udelay = pit8254_udelay,
+};
diff --git a/src/config/config_timer.c b/src/config/config_timer.c
new file mode 100644 (file)
index 0000000..a462970
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <config/timer.h>
+
+/** @file
+ *
+ * Timer configuration options
+ *
+ */
+
+PROVIDE_REQUIRING_SYMBOL();
+
+/*
+ * Drag in timers
+ */
+#ifdef TIMER_PCBIOS
+REQUIRE_OBJECT ( bios_timer );
+#endif
+#ifdef TIMER_RDTSC
+REQUIRE_OBJECT ( rdtsc_timer );
+#endif
+#ifdef TIMER_EFI
+REQUIRE_OBJECT ( efi_timer );
+#endif
+#ifdef TIMER_LINUX
+REQUIRE_OBJECT ( linux_timer );
+#endif
index 66f60158cb4e090725f6c4ad0390bf974db65a09..3ddf94f3d3dbb55cf248c4d467fcde51594d7e83 100644 (file)
@@ -117,7 +117,7 @@ int parse_timeout ( char *text, unsigned long *value ) {
                return rc;
 
        /* Convert to a number of timer ticks */
-       *value = ( ( value_ms * TICKS_PER_SEC ) / 1000 );
+       *value = ( value_ms * TICKS_PER_MS );
 
        return 0;
 }
index ca945cfba3d418a16da470924a6d43cec7fb8e7d..ed7246059512d0968ab6472f4add62dd9cca78b1 100644 (file)
 
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
-#include <unistd.h>
+#include <string.h>
+#include <assert.h>
 #include <ipxe/process.h>
 #include <ipxe/console.h>
 #include <ipxe/keys.h>
 #include <ipxe/nap.h>
+#include <ipxe/init.h>
+#include <ipxe/timer.h>
+
+/** Current timer */
+static struct timer *timer;
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks          Current time, in ticks
+ */
+unsigned long currticks ( void ) {
+
+       assert ( timer != NULL );
+       return timer->currticks();
+}
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs            Number of microseconds for which to delay
+ */
+void udelay ( unsigned long usecs ) {
+
+       assert ( timer != NULL );
+       timer->udelay ( usecs );
+}
 
 /**
  * Delay for a fixed number of milliseconds
@@ -61,3 +89,35 @@ unsigned int sleep ( unsigned int secs ) {
 
        return 0;
 }
+
+/**
+ * Find a working timer
+ *
+ */
+static void timer_probe ( void ) {
+       int rc;
+
+       /* Use first working timer */
+       for_each_table_entry ( timer, TIMERS ) {
+               if ( ( timer->probe == NULL ) ||
+                    ( ( rc = timer->probe() ) == 0 ) ) {
+                       DBGC ( &timer, "TIMER using %s\n", timer->name );
+                       return;
+               }
+               DBGC ( &timer, "TIMER could not initialise %s: %s\n",
+                      timer->name, strerror ( rc ) );
+       }
+
+       /* This is a fatal error */
+       DBGC ( &timer, "TIMER found no working timers!\n" );
+       while ( 1 ) {}
+}
+
+/** Timer initialisation function */
+struct init_fn timer_init_fn __init_fn ( INIT_EARLY ) = {
+       .initialise = timer_probe,
+};
+
+/* Drag in timer configuration */
+REQUIRING_SYMBOL ( timer_init_fn );
+REQUIRE_OBJECT ( config_timer );
index a6a65a2e97a6ef8ad9d1ddd134f33b1b9cc58f1c..a500175a7c027aee37f9eb8c07a16d016e1ceea7 100644 (file)
@@ -1381,7 +1381,7 @@ ath5k_poll(struct net80211_device *dev)
        unsigned int counter = 1000;
 
        if (currticks() - sc->last_calib_ticks >
-           ATH5K_CALIB_INTERVAL * ticks_per_sec()) {
+           ATH5K_CALIB_INTERVAL * TICKS_PER_SEC) {
                ath5k_calibrate(sc);
                sc->last_calib_ticks = currticks();
        }
index 79938cbbb42de07fc3224f8446c17cef2fa1b2f6..7f044b192e3a44763746ca18093e3a0ecdac2115 100644 (file)
@@ -1176,7 +1176,7 @@ nv_mgmt_get_version ( struct forcedeth_private *priv )
                ioaddr + NvRegTransmitterControl );
        start = currticks();
 
-       while ( currticks() > start + 5 * ticks_per_sec() ) {
+       while ( currticks() > start + 5 * TICKS_PER_SEC ) {
                data_ready2 = readl ( ioaddr + NvRegTransmitterControl );
                if ( ( data_ready & NVREG_XMITCTL_DATA_READY ) !=
                     ( data_ready2 & NVREG_XMITCTL_DATA_READY ) ) {
index d1dd49cafe4d9aa84f91f56652c2ccaceed98987..08148bf3882372bcb704d790bc6903a7b6f041db 100644 (file)
@@ -68,7 +68,7 @@ static int time_exec ( int argc, char **argv ) {
        start = currticks();
        rc = execv ( argv[1], argv + 1 );
        elapsed = ( currticks() - start );
-       decisecs = ( 10 * elapsed / ticks_per_sec() );
+       decisecs = ( 10 * elapsed / TICKS_PER_SEC );
 
        printf ( "%s: %d.%ds\n", argv[0],
                 ( decisecs / 10 ), ( decisecs % 10 ) );
diff --git a/src/include/ipxe/efi/efi_timer.h b/src/include/ipxe/efi/efi_timer.h
deleted file mode 100644 (file)
index c498759..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef _IPXE_EFI_TIMER_H
-#define _IPXE_EFI_TIMER_H
-
-/** @file
- *
- * iPXE timer API for EFI
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#ifdef TIMER_EFI
-#define TIMER_PREFIX_efi
-#else
-#define TIMER_PREFIX_efi __efi_
-#endif
-
-/**
- * Number of ticks per second
- *
- * This is a policy decision.
- */
-#define EFI_TICKS_PER_SEC 20
-
-/**
- * Get number of ticks per second
- *
- * @ret ticks_per_sec  Number of ticks per second
- */
-static inline __attribute__ (( always_inline )) unsigned long
-TIMER_INLINE ( efi, ticks_per_sec ) ( void ) {
-
-       return EFI_TICKS_PER_SEC;
-}
-
-#endif /* _IPXE_EFI_TIMER_H */
diff --git a/src/include/ipxe/linux/linux_timer.h b/src/include/ipxe/linux/linux_timer.h
deleted file mode 100644 (file)
index 7f46e36..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef _IPXE_LINUX_TIMER_H
-#define _IPXE_LINUX_TIMER_H
-
-/** @file
- *
- * iPXE timer API for Linux
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#ifdef TIMER_LINUX
-#define TIMER_PREFIX_linux
-#else
-#define TIMER_PREFIX_linux __linux_
-#endif
-
-#endif /* _IPXE_LINUX_TIMER_H */
index 82fbb676442ea6d8b2797c0e4a3d9e8398ad28fb..e6b95172e27956bd821cba6cbdc1f1bb803a6909 100644 (file)
@@ -3,75 +3,77 @@
 
 /** @file
  *
- * iPXE timer API
+ * iPXE timers
  *
- * The timer API provides udelay() for fixed delays, and currticks()
- * for a monotonically increasing tick counter.
  */
 
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
-#include <ipxe/api.h>
-#include <config/timer.h>
+#include <ipxe/tables.h>
 
-/**
- * Calculate static inline timer API function name
- *
- * @v _prefix          Subsystem prefix
- * @v _api_func                API function
- * @ret _subsys_func   Subsystem API function
- */
-#define TIMER_INLINE( _subsys, _api_func ) \
-       SINGLE_API_INLINE ( TIMER_PREFIX_ ## _subsys, _api_func )
+/** Number of ticks per second */
+#define TICKS_PER_SEC 1024
 
-/**
- * Provide a timer API implementation
+/** Number of ticks per millisecond
  *
- * @v _prefix          Subsystem prefix
- * @v _api_func                API function
- * @v _func            Implementing function
+ * This is (obviously) not 100% consistent with the definition of
+ * TICKS_PER_SEC, but it allows for multiplications and divisions to
+ * be elided.  In any case, timer ticks are not expected to be a
+ * precision timing source; for example, the standard BIOS timer is
+ * based on an 18.2Hz clock.
  */
-#define PROVIDE_TIMER( _subsys, _api_func, _func ) \
-       PROVIDE_SINGLE_API ( TIMER_PREFIX_ ## _subsys, _api_func, _func )
+#define TICKS_PER_MS 1
 
-/**
- * Provide a static inline timer API implementation
- *
- * @v _prefix          Subsystem prefix
- * @v _api_func                API function
- */
-#define PROVIDE_TIMER_INLINE( _subsys, _api_func ) \
-       PROVIDE_SINGLE_API_INLINE ( TIMER_PREFIX_ ## _subsys, _api_func )
+/** A timer */
+struct timer {
+       /** Name */
+       const char *name;
+       /**
+        * Probe timer
+        *
+        * @ret rc              Return status code
+        */
+       int ( * probe ) ( void );
+       /**
+        * Get current system time in ticks
+        *
+        * @ret ticks           Current time, in ticks
+        */
+       unsigned long ( * currticks ) ( void );
+       /**
+        * Delay for a fixed number of microseconds
+        *
+        * @v usecs             Number of microseconds for which to delay
+        */
+       void ( * udelay ) ( unsigned long usecs );
+};
 
-/* Include all architecture-independent I/O API headers */
-#include <ipxe/efi/efi_timer.h>
-#include <ipxe/linux/linux_timer.h>
+/** Timer table */
+#define TIMERS __table ( struct timer, "timers" )
 
-/* Include all architecture-dependent I/O API headers */
-#include <bits/timer.h>
+/** Declare a timer */
+#define __timer( order ) __table_entry ( TIMERS, order )
 
-/**
- * Delay for a fixed number of microseconds
+/** @defgroup timer_order Timer detection order
  *
- * @v usecs            Number of microseconds for which to delay
+ * @{
  */
-void udelay ( unsigned long usecs );
 
-/**
- * Get current system time in ticks
- *
- * @ret ticks          Current time, in ticks
- */
-unsigned long currticks ( void );
+#define TIMER_PREFERRED        01      /**< Preferred timer */
+#define TIMER_NORMAL   02      /**< Normal timer */
 
-/**
- * Get number of ticks per second
+/** @} */
+
+/*
+ * sleep() prototype is defined by POSIX.1.  usleep() prototype is
+ * defined by 4.3BSD.  udelay() and mdelay() prototypes are chosen to
+ * be reasonably sensible.
  *
- * @ret ticks_per_sec  Number of ticks per second
  */
-unsigned long ticks_per_sec ( void );
 
-/** Number of ticks per second */
-#define TICKS_PER_SEC ( ticks_per_sec() )
+extern void udelay ( unsigned long usecs );
+extern void mdelay ( unsigned long msecs );
+extern unsigned long currticks ( void );
+extern unsigned int sleep ( unsigned int seconds );
 
 #endif /* _IPXE_TIMER_H */
index d09e1ae30a9e45b1e31c8fc85dbd5bf198a0e447..6c31c060128d3165c180b3ba9638a60edfe2a060 100644 (file)
@@ -23,19 +23,9 @@ extern int execv ( const char *command, char * const argv[] );
                rc;                                                     \
        } )
 
-/* Pick up udelay() */
+/* Pick up udelay() and sleep() */
 #include <ipxe/timer.h>
 
-/*
- * sleep() prototype is defined by POSIX.1.  usleep() prototype is
- * defined by 4.3BSD.  udelay() and mdelay() prototypes are chosen to
- * be reasonably sensible.
- *
- */
-
-extern unsigned int sleep ( unsigned int seconds );
-extern void mdelay ( unsigned long msecs );
-
 static inline __always_inline void usleep ( unsigned long usecs ) {
        udelay ( usecs );
 }
index 1fd9971e9ed8f09762783dc308f689e64bc32944..0ffe2a1a005bfdecb9a18166e36c92f6de1cdbb8 100644 (file)
@@ -36,6 +36,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  *
  */
 
+/**
+ * Number of jiffies per second
+ *
+ * This is a policy decision.
+ */
+#define EFI_JIFFIES_PER_SEC 32
+
 /** Current tick count */
 static unsigned long efi_jiffies;
 
@@ -95,7 +102,7 @@ static unsigned long efi_currticks ( void ) {
        if ( efi_shutdown_in_progress )
                efi_jiffies++;
 
-       return efi_jiffies;
+       return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) );
 }
 
 /**
@@ -133,7 +140,7 @@ static void efi_tick_startup ( void ) {
 
        /* Start timer tick */
        if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerPeriodic,
-                                     ( 10000000 / EFI_TICKS_PER_SEC ) ) ) !=0){
+                                     ( 10000000 / EFI_JIFFIES_PER_SEC ) ))!=0){
                rc = -EEFI ( efirc );
                DBGC ( colour, "EFI could not start timer tick: %s\n",
                       strerror ( rc ) );
@@ -141,7 +148,7 @@ static void efi_tick_startup ( void ) {
                return;
        }
        DBGC ( colour, "EFI timer started at %d ticks per second\n",
-              EFI_TICKS_PER_SEC );
+              EFI_JIFFIES_PER_SEC );
 }
 
 /**
@@ -180,6 +187,9 @@ struct startup_fn efi_tick_startup_fn __startup_fn ( STARTUP_EARLY ) = {
        .shutdown = efi_tick_shutdown,
 };
 
-PROVIDE_TIMER ( efi, udelay, efi_udelay );
-PROVIDE_TIMER ( efi, currticks, efi_currticks );
-PROVIDE_TIMER_INLINE ( efi, ticks_per_sec );
+/** EFI timer */
+struct timer efi_timer __timer ( TIMER_NORMAL ) = {
+       .name = "efi",
+       .currticks = efi_currticks,
+       .udelay = efi_udelay,
+};
index 7a994517bd247ba37ae606b89cc9da9ad1ed2481..9c5e96f2b4a00319119d4ce4fcebe3d630307fe9 100644 (file)
@@ -39,16 +39,6 @@ static void linux_udelay(unsigned long usecs)
        linux_usleep(usecs);
 }
 
-/**
- * Get number of ticks per second
- *
- * @ret ticks_per_sec  Number of ticks per second
- */
-static unsigned long linux_ticks_per_sec(void)
-{
-       return 1000;
-}
-
 /**
  * Get current system time in ticks
  *
@@ -67,21 +57,25 @@ static unsigned long linux_currticks(void)
 {
        static struct timeval start;
        static int initialized = 0;
+       struct timeval now;
+       unsigned long ticks;
 
        if (! initialized) {
                linux_gettimeofday(&start, NULL);
                initialized = 1;
        }
 
-       struct timeval now;
        linux_gettimeofday(&now, NULL);
 
-       unsigned long ticks = (now.tv_sec - start.tv_sec) * linux_ticks_per_sec();
-       ticks += now.tv_usec / (long)(1000000 / linux_ticks_per_sec());
+       ticks = ( ( now.tv_sec - start.tv_sec ) * TICKS_PER_SEC );
+       ticks += ( now.tv_usec / ( 1000000 / TICKS_PER_SEC ) );
 
        return ticks;
 }
 
-PROVIDE_TIMER(linux, udelay, linux_udelay);
-PROVIDE_TIMER(linux, currticks, linux_currticks);
-PROVIDE_TIMER(linux, ticks_per_sec, linux_ticks_per_sec);
+/** Linux timer */
+struct timer linux_timer __timer ( TIMER_NORMAL ) = {
+       .name = "linux",
+       .currticks = linux_currticks,
+       .udelay = linux_udelay,
+};
index d4970ad5c571b8fa731bafe24e1927e3d3eb801a..62912741f901dbb4cffb86aeb0f00e0e4a4585df 100644 (file)
@@ -1321,7 +1321,7 @@ struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev,
        ctx->ticks_start = currticks();
        ctx->ticks_beacon = 0;
        ctx->ticks_channel = currticks();
-       ctx->hop_time = ticks_per_sec() / ( active ? 2 : 6 );
+       ctx->hop_time = TICKS_PER_SEC / ( active ? 2 : 6 );
 
        /*
         * Channels on 2.4GHz overlap, and the most commonly used
@@ -1363,8 +1363,8 @@ struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev,
 int net80211_probe_step ( struct net80211_probe_ctx *ctx )
 {
        struct net80211_device *dev = ctx->dev;
-       u32 start_timeout = NET80211_PROBE_TIMEOUT * ticks_per_sec();
-       u32 gather_timeout = ticks_per_sec();
+       u32 start_timeout = NET80211_PROBE_TIMEOUT * TICKS_PER_SEC;
+       u32 gather_timeout = TICKS_PER_SEC;
        u32 now = currticks();
        struct io_buffer *iob;
        int signal;
@@ -2606,7 +2606,7 @@ static void net80211_rx_frag ( struct net80211_device *dev,
                /* start a frag cache entry */
                int i, newest = -1;
                u32 curr_ticks = currticks(), newest_ticks = 0;
-               u32 timeout = ticks_per_sec() * NET80211_FRAG_TIMEOUT;
+               u32 timeout = TICKS_PER_SEC * NET80211_FRAG_TIMEOUT;
 
                for ( i = 0; i < NET80211_NR_CONCURRENT_FRAGS; i++ ) {
                        if ( dev->frags[i].in_use == 0 )
index c3258f15e211776cb67feee374dd4fa0e89d9c31..f910eeeade644cdbe3107bcda3e35a1df5cf6040 100644 (file)
@@ -1094,7 +1094,7 @@ static void fcoe_expired ( struct retry_timer *timer, int over __unused ) {
 
                /* Send keepalive */
                start_timer_fixed ( &fcoe->timer,
-                             ( ( fcoe->keepalive * TICKS_PER_SEC ) / 1000 ) );
+                                   ( fcoe->keepalive * TICKS_PER_MS ) );
                fcoe_fip_tx_keepalive ( fcoe );
 
                /* Abandon FCF if we have not seen its advertisements */
index defdaed9ed351b2d1fe76f7d450ea76202ae5313..3d78400af5798789016b570941040be791d02f28 100644 (file)
@@ -110,7 +110,7 @@ static int stp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
                       "forwarding\n",
                       netdev->name, eth_ntoa ( stp->sender.mac ),
                       ntohs ( stp->port ), stp->flags );
-               hello = ( ( ntohs ( stp->hello ) * TICKS_PER_SEC ) / 256 );
+               hello = ( ntohs ( stp->hello ) * ( TICKS_PER_SEC / 256 ) );
                netdev_link_block ( netdev, ( hello * 2 ) );
                rc = -ENETUNREACH;
                goto done;