Introduce the new timer subsystem.

Timer subsystem initialization code in core/timer.c

	Split the BIOS and RTDSC timer drivers from i386_timer.c

	Split arch/i386/firmware/pcbios/bios.c into the RTSDC
	timer driver and arch/i386/core/nap.c

	Split the headers properly:
		include/unistd.h - delay functions to be used by the
					gPXE core and drivers.

		include/gpxe/timer.h - the fimer subsystem interface
					to be used by the timer drivers
					and currticks() to be used by
					the code gPXE subsystems.

		include/latch.h	- removed
		include/timer.h - scheduled for removal. Some driver
					are using currticks, which is
					only for core subsystems.

Signed-off-by: Alexey Zaytsev <alexey.zaytsev@gmail.com>
This commit is contained in:
Alexey Zaytsev
2007-12-01 07:07:01 +03:00
parent 844828cb15
commit 4006d229e5
13 changed files with 384 additions and 296 deletions

View File

@@ -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,
};

View File

@@ -0,0 +1,90 @@
#include <gpxe/init.h>
#include <gpxe/timer.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))
static unsigned long long calibrate_tsc(void)
{
uint32_t startlow, starthigh;
uint32_t endlow, endhigh;
rdtsc(startlow,starthigh);
i386_timer2_udelay(USECS_IN_MSEC/2);
rdtsc(endlow,endhigh);
/* 64-bit subtract - gcc just messes up with long longs */
/* XXX ORLY? Check it. */
__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 *= MSECS_IN_SEC*2;
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:
return 0;
}
static uint32_t clocks_per_second = 0;
static tick_t rtdsc_currticks(void)
{
uint32_t clocks_high, clocks_low;
uint32_t currticks;
/* Read the Time Stamp Counter */
rdtsc(clocks_low, clocks_high);
/* currticks = clocks / clocks_per_tick; */
__asm__("divl %1"
:"=a" (currticks)
:"r" (clocks_per_second/USECS_IN_SEC), "0" (clocks_low), "d" (clocks_high));
return currticks;
}
static int rtdsc_ts_init(void)
{
struct cpuinfo_x86 cpu_info;
get_cpuinfo(&cpu_info);
if (cpu_info.features & X86_FEATURE_TSC) {
clocks_per_second = calibrate_tsc();
if (clocks_per_second) {
DBG("RTDSC Ticksource installed. CPU running at %ld Mhz\n",
clocks_per_second/(1000*1000));
return 0;
}
}
printf("RTDSC timer not available on this machine.\n");
return 1;
}
struct timer rtdsc_ts __timer (01) = {
.init = rtdsc_ts_init,
.udelay = generic_currticks_udelay,
.currticks = rtdsc_currticks,
};