diff --git a/src/arch/arm/include/bits/timer.h b/src/arch/arm/include/bits/timer.h deleted file mode 100644 index 64e7d31d..00000000 --- a/src/arch/arm/include/bits/timer.h +++ /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 */ diff --git a/src/arch/x86/core/rdtsc_timer.c b/src/arch/x86/core/rdtsc_timer.c index e720a239..ed515150 100644 --- a/src/arch/x86/core/rdtsc_timer.c +++ b/src/arch/x86/core/rdtsc_timer.c @@ -29,16 +29,70 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -#include +#include +#include #include +#include #include -/** - * Number of TSC ticks per microsecond +/** 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) * - * This is calibrated on the first use of the timer. + * We use this to avoid the need for 64-bit divsion on 32-bit systems. */ -static unsigned long rdtsc_ticks_per_usec; +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; +} + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +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; + } - return ( rdtsc_ticks_per_usec * 1000 * 1000 ); + /* 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 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, +}; diff --git a/src/arch/x86/include/bios.h b/src/arch/x86/include/bios.h index a5a5d887..14e7acbc 100644 --- a/src/arch/x86/include/bios.h +++ b/src/arch/x86/include/bios.h @@ -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 diff --git a/src/arch/x86/include/bits/errfile.h b/src/arch/x86/include/bits/errfile.h index 105cdf5d..9d1fed7f 100644 --- a/src/arch/x86/include/bits/errfile.h +++ b/src/arch/x86/include/bits/errfile.h @@ -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 index b0ff5ee1..00000000 --- a/src/arch/x86/include/bits/timer.h +++ /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 -#include - -#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 index 6b88a623..00000000 --- a/src/arch/x86/include/ipxe/bios_timer.h +++ /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 - -/** - * 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 */ diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index 2e2cc7c1..a9df9f0d 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -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 index 598f4bb0..00000000 --- a/src/arch/x86/include/ipxe/rdtsc_timer.h +++ /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 */ diff --git a/src/arch/x86/interface/pcbios/bios_timer.c b/src/arch/x86/interface/pcbios/bios_timer.c index 3299c9aa..49e1d226 100644 --- a/src/arch/x86/interface/pcbios/bios_timer.c +++ b/src/arch/x86/interface/pcbios/bios_timer.c @@ -32,6 +32,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include + +/** 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 index 00000000..a462970c --- /dev/null +++ b/src/config/config_timer.c @@ -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 + +/** @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 diff --git a/src/core/parseopt.c b/src/core/parseopt.c index 66f60158..3ddf94f3 100644 --- a/src/core/parseopt.c +++ b/src/core/parseopt.c @@ -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; } diff --git a/src/core/timer.c b/src/core/timer.c index ca945cfb..ed724605 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -23,11 +23,39 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include +#include +#include #include #include #include #include +#include +#include + +/** 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 ); diff --git a/src/drivers/net/ath/ath5k/ath5k.c b/src/drivers/net/ath/ath5k/ath5k.c index a6a65a2e..a500175a 100644 --- a/src/drivers/net/ath/ath5k/ath5k.c +++ b/src/drivers/net/ath/ath5k/ath5k.c @@ -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(); } diff --git a/src/drivers/net/forcedeth.c b/src/drivers/net/forcedeth.c index 79938cbb..7f044b19 100644 --- a/src/drivers/net/forcedeth.c +++ b/src/drivers/net/forcedeth.c @@ -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 ) ) { diff --git a/src/hci/commands/time_cmd.c b/src/hci/commands/time_cmd.c index d1dd49ca..08148bf3 100644 --- a/src/hci/commands/time_cmd.c +++ b/src/hci/commands/time_cmd.c @@ -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 index c4987598..00000000 --- a/src/include/ipxe/efi/efi_timer.h +++ /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 index 7f46e36b..00000000 --- a/src/include/ipxe/linux/linux_timer.h +++ /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 */ diff --git a/src/include/ipxe/timer.h b/src/include/ipxe/timer.h index 82fbb676..e6b95172 100644 --- a/src/include/ipxe/timer.h +++ b/src/include/ipxe/timer.h @@ -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 -#include - -/** - * 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 ) - -/** - * Provide a timer API implementation - * - * @v _prefix Subsystem prefix - * @v _api_func API function - * @v _func Implementing function - */ -#define PROVIDE_TIMER( _subsys, _api_func, _func ) \ - PROVIDE_SINGLE_API ( TIMER_PREFIX_ ## _subsys, _api_func, _func ) - -/** - * 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 ) - -/* Include all architecture-independent I/O API headers */ -#include -#include - -/* Include all architecture-dependent I/O API headers */ -#include - -/** - * Delay for a fixed number of microseconds - * - * @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 ); - -/** - * Get number of ticks per second - * - * @ret ticks_per_sec Number of ticks per second - */ -unsigned long ticks_per_sec ( void ); +#include /** Number of ticks per second */ -#define TICKS_PER_SEC ( ticks_per_sec() ) +#define TICKS_PER_SEC 1024 + +/** Number of ticks per millisecond + * + * 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 TICKS_PER_MS 1 + +/** 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 ); +}; + +/** Timer table */ +#define TIMERS __table ( struct timer, "timers" ) + +/** Declare a timer */ +#define __timer( order ) __table_entry ( TIMERS, order ) + +/** @defgroup timer_order Timer detection order + * + * @{ + */ + +#define TIMER_PREFERRED 01 /**< Preferred timer */ +#define TIMER_NORMAL 02 /**< Normal timer */ + +/** @} */ + +/* + * 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 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 */ diff --git a/src/include/unistd.h b/src/include/unistd.h index d09e1ae3..6c31c060 100644 --- a/src/include/unistd.h +++ b/src/include/unistd.h @@ -23,19 +23,9 @@ extern int execv ( const char *command, char * const argv[] ); rc; \ } ) -/* Pick up udelay() */ +/* Pick up udelay() and sleep() */ #include -/* - * 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 ); } diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index 1fd9971e..0ffe2a1a 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -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, +}; diff --git a/src/interface/linux/linux_timer.c b/src/interface/linux/linux_timer.c index 7a994517..9c5e96f2 100644 --- a/src/interface/linux/linux_timer.c +++ b/src/interface/linux/linux_timer.c @@ -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, +}; diff --git a/src/net/80211/net80211.c b/src/net/80211/net80211.c index d4970ad5..62912741 100644 --- a/src/net/80211/net80211.c +++ b/src/net/80211/net80211.c @@ -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 ) diff --git a/src/net/fcoe.c b/src/net/fcoe.c index c3258f15..f910eeea 100644 --- a/src/net/fcoe.c +++ b/src/net/fcoe.c @@ -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 */ diff --git a/src/net/stp.c b/src/net/stp.c index defdaed9..3d78400a 100644 --- a/src/net/stp.c +++ b/src/net/stp.c @@ -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;