diff options
| author | Michael Brown | 2008-10-12 20:56:52 +0200 |
|---|---|---|
| committer | Michael Brown | 2008-10-12 21:22:02 +0200 |
| commit | 16f1e35775c972ba8e02bc2d97d7a2eb333eae1b (patch) | |
| tree | f653099d4b230c9807986aa2b4cd89dce2cffe9b /src/arch | |
| parent | [process] Add DBG2() messages to help track down frozen processes (diff) | |
| download | ipxe-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.c | 87 | ||||
| -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.c | 57 | ||||
| -rw-r--r-- | src/arch/i386/drivers/timer_rdtsc.c | 69 | ||||
| -rw-r--r-- | src/arch/i386/include/bios.h | 1 | ||||
| -rw-r--r-- | src/arch/i386/include/bits/timer.h | 13 | ||||
| -rw-r--r-- | src/arch/i386/include/bits/timer2.h | 8 | ||||
| -rw-r--r-- | src/arch/i386/include/gpxe/bios_timer.h | 42 | ||||
| -rw-r--r-- | src/arch/i386/include/gpxe/rdtsc_timer.h | 37 | ||||
| -rw-r--r-- | src/arch/i386/include/gpxe/timer2.h | 12 | ||||
| -rw-r--r-- | src/arch/i386/interface/pcbios/bios_timer.c | 63 |
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 ); |
