efi_timer.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 #include <limits.h>
00022 #include <assert.h>
00023 #include <unistd.h>
00024 #include <gpxe/timer.h>
00025 #include <gpxe/efi/efi.h>
00026 #include <gpxe/efi/Protocol/Cpu.h>
00027 
00028 /** @file
00029  *
00030  * gPXE timer API for EFI
00031  *
00032  */
00033 
00034 /** Scale factor to apply to CPU timer 0
00035  *
00036  * The timer is scaled down in order to ensure that reasonable values
00037  * for "number of ticks" don't exceed the size of an unsigned long.
00038  */
00039 #define EFI_TIMER0_SHIFT 12
00040 
00041 /** Calibration time */
00042 #define EFI_CALIBRATE_DELAY_MS 1
00043 
00044 /** CPU protocol */
00045 static EFI_CPU_ARCH_PROTOCOL *cpu_arch;
00046 EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch );
00047 
00048 /**
00049  * Delay for a fixed number of microseconds
00050  *
00051  * @v usecs             Number of microseconds for which to delay
00052  */
00053 static void efi_udelay ( unsigned long usecs ) {
00054         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00055         EFI_STATUS efirc;
00056 
00057         if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
00058                 DBG ( "EFI could not delay for %ldus: %s\n",
00059                       usecs, efi_strerror ( efirc ) );
00060                 /* Probably screwed */
00061         }
00062 }
00063 
00064 /**
00065  * Get current system time in ticks
00066  *
00067  * @ret ticks           Current time, in ticks
00068  */
00069 static unsigned long efi_currticks ( void ) {
00070         UINT64 time;
00071         EFI_STATUS efirc;
00072 
00073         /* Read CPU timer 0 (TSC) */
00074         if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time,
00075                                                  NULL ) ) != 0 ) {
00076                 DBG ( "EFI could not read CPU timer: %s\n",
00077                       efi_strerror ( efirc ) );
00078                 /* Probably screwed */
00079                 return -1UL;
00080         }
00081 
00082         return ( time >> EFI_TIMER0_SHIFT );
00083 }
00084 
00085 /**
00086  * Get number of ticks per second
00087  *
00088  * @ret ticks_per_sec   Number of ticks per second
00089  */
00090 static unsigned long efi_ticks_per_sec ( void ) {
00091         static unsigned long ticks_per_sec = 0;
00092 
00093         /* Calibrate timer, if necessary.  EFI does nominally provide
00094          * the timer speed via the (optional) TimerPeriod parameter to
00095          * the GetTimerValue() call, but it gets the speed slightly
00096          * wrong.  By up to three orders of magnitude.  Not helpful.
00097          */
00098         if ( ! ticks_per_sec ) {
00099                 unsigned long start;
00100                 unsigned long elapsed;
00101 
00102                 DBG ( "Calibrating EFI timer with a %d ms delay\n",
00103                       EFI_CALIBRATE_DELAY_MS );
00104                 start = currticks();
00105                 mdelay ( EFI_CALIBRATE_DELAY_MS );
00106                 elapsed = ( currticks() - start );
00107                 ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS ));
00108                 DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld "
00109                       "ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS,
00110                       ticks_per_sec );
00111         }
00112 
00113         return ticks_per_sec;
00114 }
00115 
00116 PROVIDE_TIMER ( efi, udelay, efi_udelay );
00117 PROVIDE_TIMER ( efi, currticks, efi_currticks );
00118 PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec );

Generated on Tue Apr 6 20:01:09 2010 for gPXE by  doxygen 1.5.7.1