Home | History | Annotate | Download | only in efi
      1 /*
      2  * Copyright (C) 2008 Michael Brown <mbrown (at) fensystems.co.uk>.
      3  *
      4  * This program is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU General Public License as
      6  * published by the Free Software Foundation; either version 2 of the
      7  * License, or any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program; if not, write to the Free Software
     16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     17  */
     18 
     19 FILE_LICENCE ( GPL2_OR_LATER );
     20 
     21 #include <limits.h>
     22 #include <assert.h>
     23 #include <unistd.h>
     24 #include <gpxe/timer.h>
     25 #include <gpxe/efi/efi.h>
     26 #include <gpxe/efi/Protocol/Cpu.h>
     27 
     28 /** @file
     29  *
     30  * gPXE timer API for EFI
     31  *
     32  */
     33 
     34 /** Scale factor to apply to CPU timer 0
     35  *
     36  * The timer is scaled down in order to ensure that reasonable values
     37  * for "number of ticks" don't exceed the size of an unsigned long.
     38  */
     39 #define EFI_TIMER0_SHIFT 12
     40 
     41 /** Calibration time */
     42 #define EFI_CALIBRATE_DELAY_MS 1
     43 
     44 /** CPU protocol */
     45 static EFI_CPU_ARCH_PROTOCOL *cpu_arch;
     46 EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch );
     47 
     48 /**
     49  * Delay for a fixed number of microseconds
     50  *
     51  * @v usecs		Number of microseconds for which to delay
     52  */
     53 static void efi_udelay ( unsigned long usecs ) {
     54 	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
     55 	EFI_STATUS efirc;
     56 
     57 	if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
     58 		DBG ( "EFI could not delay for %ldus: %s\n",
     59 		      usecs, efi_strerror ( efirc ) );
     60 		/* Probably screwed */
     61 	}
     62 }
     63 
     64 /**
     65  * Get current system time in ticks
     66  *
     67  * @ret ticks		Current time, in ticks
     68  */
     69 static unsigned long efi_currticks ( void ) {
     70 	UINT64 time;
     71 	EFI_STATUS efirc;
     72 
     73 	/* Read CPU timer 0 (TSC) */
     74 	if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time,
     75 						 NULL ) ) != 0 ) {
     76 		DBG ( "EFI could not read CPU timer: %s\n",
     77 		      efi_strerror ( efirc ) );
     78 		/* Probably screwed */
     79 		return -1UL;
     80 	}
     81 
     82 	return ( time >> EFI_TIMER0_SHIFT );
     83 }
     84 
     85 /**
     86  * Get number of ticks per second
     87  *
     88  * @ret ticks_per_sec	Number of ticks per second
     89  */
     90 static unsigned long efi_ticks_per_sec ( void ) {
     91 	static unsigned long ticks_per_sec = 0;
     92 
     93 	/* Calibrate timer, if necessary.  EFI does nominally provide
     94 	 * the timer speed via the (optional) TimerPeriod parameter to
     95 	 * the GetTimerValue() call, but it gets the speed slightly
     96 	 * wrong.  By up to three orders of magnitude.  Not helpful.
     97 	 */
     98 	if ( ! ticks_per_sec ) {
     99 		unsigned long start;
    100 		unsigned long elapsed;
    101 
    102 		DBG ( "Calibrating EFI timer with a %d ms delay\n",
    103 		      EFI_CALIBRATE_DELAY_MS );
    104 		start = currticks();
    105 		mdelay ( EFI_CALIBRATE_DELAY_MS );
    106 		elapsed = ( currticks() - start );
    107 		ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS ));
    108 		DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld "
    109 		      "ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS,
    110 		      ticks_per_sec );
    111 	}
    112 
    113 	return ticks_per_sec;
    114 }
    115 
    116 PROVIDE_TIMER ( efi, udelay, efi_udelay );
    117 PROVIDE_TIMER ( efi, currticks, efi_currticks );
    118 PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec );
    119