diff options
Diffstat (limited to 'src/arch')
| -rw-r--r-- | src/arch/i386/Makefile | 1 | ||||
| -rw-r--r-- | src/arch/i386/core/i386_timer.c | 213 | ||||
| -rw-r--r-- | src/arch/i386/core/nap.c | 12 | ||||
| -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/firmware/pcbios/bios.c | 55 | ||||
| -rw-r--r-- | src/arch/i386/firmware/pcbios/gateA20.c | 7 | ||||
| -rw-r--r-- | src/arch/i386/include/bits/errfile.h | 3 | ||||
| -rw-r--r-- | src/arch/i386/include/bits/timer2.h | 8 | ||||
| -rw-r--r-- | src/arch/i386/include/latch.h | 12 |
10 files changed, 206 insertions, 231 deletions
diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile index 5e7416cbb..da7976df7 100644 --- a/src/arch/i386/Makefile +++ b/src/arch/i386/Makefile @@ -7,6 +7,7 @@ ISOLINUX_BIN = /usr/lib/syslinux/isolinux.bin SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix SRCDIRS += arch/i386/firmware/pcbios SRCDIRS += arch/i386/image +SRCDIRS += arch/i386/drivers SRCDIRS += arch/i386/drivers/bus SRCDIRS += arch/i386/drivers/net SRCDIRS += arch/i386/drivers/disk diff --git a/src/arch/i386/core/i386_timer.c b/src/arch/i386/core/i386_timer.c index 8d3a629ad..8f90ae05a 100644 --- a/src/arch/i386/core/i386_timer.c +++ b/src/arch/i386/core/i386_timer.c @@ -1,18 +1,58 @@ -/* A couple of routines to implement a low-overhead timer for drivers */ - - /* +/* + * arch/i386/core/i386_timer.c + * + * Use the "System Timer 2" to implement the udelay callback in + * the BIOS timer driver. Also used to calibrate the clock rate + * in the RTDSC timer driver. + * * 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, or (at * your option) any later version. */ -#include "timer.h" -#include "latch.h" -#include <io.h> -#include <gpxe/init.h> +#include <stddef.h> +#include <bits/timer2.h> +#include <gpxe/timer.h> +#include <io.h> + +/* Timers tick over at this rate */ +#define TIMER2_TICK_RATE 1193180U + +/* Parallel Peripheral Controller Port B */ +#define PPC_PORTB 0x61 + +/* Meaning of the port bits */ +#define PPCB_T2OUT 0x20 /* Bit 5 */ +#define PPCB_SPKR 0x02 /* Bit 1 */ +#define PPCB_T2GATE 0x01 /* Bit 0 */ + +/* Ports for the 8254 timer chip */ +#define TIMER2_PORT 0x42 +#define TIMER_MODE_PORT 0x43 + +/* Meaning of the mode bits */ +#define TIMER0_SEL 0x00 +#define TIMER1_SEL 0x40 +#define TIMER2_SEL 0x80 +#define READBACK_SEL 0xC0 + +#define LATCH_COUNT 0x00 +#define LOBYTE_ACCESS 0x10 +#define HIBYTE_ACCESS 0x20 +#define WORD_ACCESS 0x30 + +#define MODE0 0x00 +#define MODE1 0x02 +#define MODE2 0x04 +#define MODE3 0x06 +#define MODE4 0x08 +#define MODE5 0x0A -void __load_timer2(unsigned int ticks) +#define BINARY_COUNT 0x00 +#define BCD_COUNT 0x01 + +static void load_timer2(unsigned int ticks) { /* * Now let's take care of PPC channel 2 @@ -35,162 +75,15 @@ 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); } -#if !defined(CONFIG_TSC_CURRTICKS) -static void setup_timers(void) -{ - return; -} - -void load_timer2(unsigned int ticks) -{ - return __load_timer2(ticks); -} - -int timer2_running(void) -{ - return __timer2_running(); -} - -void ndelay(unsigned int nsecs) -{ - waiton_timer2((nsecs * CLOCK_TICK_RATE)/1000000000); -} -void udelay(unsigned int usecs) -{ - waiton_timer2((usecs * TICKS_PER_MS)/1000); -} -#endif /* !defined(CONFIG_TSC_CURRTICKS) */ - -#if defined(CONFIG_TSC_CURRTICKS) - -#define rdtsc(low,high) \ - __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) - -#define rdtscll(val) \ - __asm__ __volatile__ ("rdtsc" : "=A" (val)) - - -/* Number of clock ticks to time with the rtc */ -#define LATCH 0xFF - -#define LATCHES_PER_SEC ((CLOCK_TICK_RATE + (LATCH/2))/LATCH) -#define TICKS_PER_LATCH ((LATCHES_PER_SEC + (TICKS_PER_SEC/2))/TICKS_PER_SEC) - -static void sleep_latch(void) +void i386_timer2_udelay(unsigned int usecs) { - __load_timer2(LATCH); - while(__timer2_running()); + load_timer2((usecs * TIMER2_TICK_RATE)/USECS_IN_SEC); + while (timer2_running()) + ; } -/* ------ Calibrate the TSC ------- - * Time how long it takes to excute a loop that runs in known time. - * And find the convertion needed to get to CLOCK_TICK_RATE - */ - - -static unsigned long long calibrate_tsc(void) -{ - unsigned long startlow, starthigh; - unsigned long endlow, endhigh; - - rdtsc(startlow,starthigh); - sleep_latch(); - rdtsc(endlow,endhigh); - - /* 64-bit subtract - gcc just messes up with long longs */ - __asm__("subl %2,%0\n\t" - "sbbl %3,%1" - :"=a" (endlow), "=d" (endhigh) - :"g" (startlow), "g" (starthigh), - "0" (endlow), "1" (endhigh)); - - /* Error: ECPUTOOFAST */ - if (endhigh) - goto bad_ctc; - - endlow *= TICKS_PER_LATCH; - return endlow; - - /* - * The CTC wasn't reliable: we got a hit on the very first read, - * or the CPU was so fast/slow that the quotient wouldn't fit in - * 32 bits.. - */ -bad_ctc: - printf("bad_ctc\n"); - return 0; -} - -static unsigned long clocks_per_tick; -static void setup_timers(void) -{ - if (!clocks_per_tick) { - clocks_per_tick = calibrate_tsc(); - /* Display the CPU Mhz to easily test if the calibration was bad */ - printf("CPU %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000); - } -} - -unsigned long currticks(void) -{ - unsigned long clocks_high, clocks_low; - unsigned long currticks; - /* Read the Time Stamp Counter */ - rdtsc(clocks_low, clocks_high); - - /* currticks = clocks / clocks_per_tick; */ - __asm__("divl %1" - :"=a" (currticks) - :"r" (clocks_per_tick), "0" (clocks_low), "d" (clocks_high)); - - - return currticks; -} - -static unsigned long long timer_timeout; -static int __timer_running(void) -{ - unsigned long long now; - rdtscll(now); - return now < timer_timeout; -} - -void udelay(unsigned int usecs) -{ - unsigned long long now; - rdtscll(now); - timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000)); - while(__timer_running()); -} -void ndelay(unsigned int nsecs) -{ - unsigned long long now; - rdtscll(now); - timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000)); - while(__timer_running()); -} - -void load_timer2(unsigned int timer2_ticks) -{ - unsigned long long now; - unsigned long clocks; - rdtscll(now); - clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE); - timer_timeout = now + clocks; -} - -int timer2_running(void) -{ - return __timer_running(); -} - -#endif /* RTC_CURRTICKS */ - -struct init_fn timer_init_fn __init_fn ( INIT_NORMAL ) = { - .initialise = setup_timers, -}; diff --git a/src/arch/i386/core/nap.c b/src/arch/i386/core/nap.c new file mode 100644 index 000000000..12bb5699c --- /dev/null +++ b/src/arch/i386/core/nap.c @@ -0,0 +1,12 @@ + +#include <realmode.h> +#include <bios.h> + +/************************************************************************** + * Save power by halting the CPU until the next interrupt + **************************************************************************/ +void cpu_nap ( void ) { + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "hlt\n\t" + "cli\n\t" ) : : ); +} 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, +}; + diff --git a/src/arch/i386/firmware/pcbios/bios.c b/src/arch/i386/firmware/pcbios/bios.c deleted file mode 100644 index bcbe98a88..000000000 --- a/src/arch/i386/firmware/pcbios/bios.c +++ /dev/null @@ -1,55 +0,0 @@ -/* Etherboot routines for PCBIOS firmware. - * - * Body of routines taken from old pcbios.S - */ - -#include <stdint.h> -#include <realmode.h> -#include <bios.h> - -#define CF ( 1 << 0 ) - -/************************************************************************** -CURRTICKS - Get Time -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. -**************************************************************************/ -#if defined(CONFIG_TSC_CURRTICKS) -#undef CONFIG_BIOS_CURRTICKS -#else -#define CONFIG_BIOS_CURRTICKS 1 -#endif -#if defined(CONFIG_BIOS_CURRTICKS) -unsigned long currticks ( void ) { - static uint32_t 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 ); -} -#endif /* CONFIG_BIOS_CURRTICKS */ - -/************************************************************************** -CPU_NAP - Save power by halting the CPU until the next interrupt -**************************************************************************/ -void cpu_nap ( void ) { - __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" - "hlt\n\t" - "cli\n\t" ) : : ); -} diff --git a/src/arch/i386/firmware/pcbios/gateA20.c b/src/arch/i386/firmware/pcbios/gateA20.c index 66b4da18a..2caac8941 100644 --- a/src/arch/i386/firmware/pcbios/gateA20.c +++ b/src/arch/i386/firmware/pcbios/gateA20.c @@ -1,8 +1,7 @@ #include <stdio.h> -#include "realmode.h" -#include "timer.h" -#include "latch.h" -#include "bios.h" +#include <realmode.h> +#include <bios.h> +#include <gpxe/timer.h> #define K_RDWR 0x60 /* keyboard data & cmds (read/write) */ #define K_STATUS 0x64 /* keyboard status */ diff --git a/src/arch/i386/include/bits/errfile.h b/src/arch/i386/include/bits/errfile.h index a6f878254..0f1402148 100644 --- a/src/arch/i386/include/bits/errfile.h +++ b/src/arch/i386/include/bits/errfile.h @@ -26,6 +26,9 @@ #define ERRFILE_undionly ( ERRFILE_ARCH | ERRFILE_NET | 0x00030000 ) #define ERRFILE_undirom ( ERRFILE_ARCH | ERRFILE_NET | 0x00040000 ) +#define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 ) +#define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 ) + /** @} */ #endif /* _BITS_ERRFILE_H */ diff --git a/src/arch/i386/include/bits/timer2.h b/src/arch/i386/include/bits/timer2.h new file mode 100644 index 000000000..83923b299 --- /dev/null +++ b/src/arch/i386/include/bits/timer2.h @@ -0,0 +1,8 @@ +#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/latch.h b/src/arch/i386/include/latch.h deleted file mode 100644 index 5000d5826..000000000 --- a/src/arch/i386/include/latch.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef LATCH_H -#define LATCH_H - -#define TICKS_PER_SEC 18 - -/* For different calibrators of the TSC move the declaration of - * sleep_latch and the definitions of it's length here... - */ - -extern unsigned long currticks ( void ); - -#endif /* LATCH_H */ |
