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 /** @file 22 * 23 * RDTSC timer 24 * 25 */ 26 27 #include <assert.h> 28 #include <gpxe/timer.h> 29 #include <gpxe/timer2.h> 30 31 /** 32 * Number of TSC ticks per microsecond 33 * 34 * This is calibrated on the first use of the timer. 35 */ 36 static unsigned long rdtsc_ticks_per_usec; 37 38 /** 39 * Delay for a fixed number of microseconds 40 * 41 * @v usecs Number of microseconds for which to delay 42 */ 43 static void rdtsc_udelay ( unsigned long usecs ) { 44 unsigned long start; 45 unsigned long elapsed; 46 47 /* Sanity guard, since we may divide by this */ 48 if ( ! usecs ) 49 usecs = 1; 50 51 start = currticks(); 52 if ( rdtsc_ticks_per_usec ) { 53 /* Already calibrated; busy-wait until done */ 54 do { 55 elapsed = ( currticks() - start ); 56 } while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) ); 57 } else { 58 /* Not yet calibrated; use timer2 and calibrate 59 * based on result. 60 */ 61 timer2_udelay ( usecs ); 62 elapsed = ( currticks() - start ); 63 rdtsc_ticks_per_usec = ( elapsed / usecs ); 64 DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs " 65 "(%ld MHz)\n", elapsed, usecs, 66 ( rdtsc_ticks_per_usec << TSC_SHIFT ) ); 67 } 68 } 69 70 /** 71 * Get number of ticks per second 72 * 73 * @ret ticks_per_sec Number of ticks per second 74 */ 75 static unsigned long rdtsc_ticks_per_sec ( void ) { 76 77 /* Calibrate timer, if not already done */ 78 if ( ! rdtsc_ticks_per_usec ) 79 udelay ( 1 ); 80 81 /* Sanity check */ 82 assert ( rdtsc_ticks_per_usec != 0 ); 83 84 return ( rdtsc_ticks_per_usec * 1000 * 1000 ); 85 } 86 87 PROVIDE_TIMER ( rdtsc, udelay, rdtsc_udelay ); 88 PROVIDE_TIMER_INLINE ( rdtsc, currticks ); 89 PROVIDE_TIMER ( rdtsc, ticks_per_sec, rdtsc_ticks_per_sec ); 90