| /* | |
| * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. | |
| * | |
| * 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 of the | |
| * License, or any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, but | |
| * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| * 02110-1301, USA. | |
| * | |
| * You can also choose to distribute this program under the terms of | |
| * the Unmodified Binary Distribution Licence (as given in the file | |
| * COPYING.UBDL), provided that you have satisfied its requirements. | |
| */ | |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); | |
| FILE_SECBOOT ( PERMITTED ); | |
| #include <string.h> | |
| #include <ipxe/process.h> | |
| #include <ipxe/console.h> | |
| #include <ipxe/keys.h> | |
| #include <ipxe/nap.h> | |
| #include <ipxe/init.h> | |
| #include <ipxe/timer.h> | |
| /** Current timer */ | |
| static struct timer *timer; | |
| /** | |
| * Get current system time in ticks | |
| * | |
| * @ret ticks Current time, in ticks | |
| */ | |
| unsigned long currticks ( void ) { | |
| /* Guard against use during early initialisation */ | |
| if ( ! timer ) { | |
| DBGC ( &timer, "TIMER currticks() called before initialisation " | |
| "from %p\n", __builtin_return_address ( 0 ) ); | |
| return 0; | |
| } | |
| /* Use selected timer */ | |
| return timer->currticks(); | |
| } | |
| /** | |
| * Delay for a fixed number of microseconds | |
| * | |
| * @v usecs Number of microseconds for which to delay | |
| */ | |
| void udelay ( unsigned long usecs ) { | |
| /* Guard against use during early initialisation */ | |
| if ( ! timer ) { | |
| DBGC ( &timer, "TIMER udelay() called before initialisation " | |
| "from %p\n", __builtin_return_address ( 0 ) ); | |
| return; | |
| } | |
| /* Use selected timer */ | |
| timer->udelay ( usecs ); | |
| } | |
| /** | |
| * Delay for a fixed number of milliseconds | |
| * | |
| * @v msecs Number of milliseconds for which to delay | |
| */ | |
| void mdelay ( unsigned long msecs ) { | |
| /* Guard against use during early initialisation */ | |
| if ( ! timer ) { | |
| DBGC ( &timer, "TIMER mdelay() called before initialisation " | |
| "from %p\n", __builtin_return_address ( 0 ) ); | |
| return; | |
| } | |
| /* Delay for specified number of milliseconds */ | |
| while ( msecs-- ) | |
| udelay ( 1000 ); | |
| } | |
| /** | |
| * Sleep (possibly interruptibly) for a fixed number of seconds | |
| * | |
| * @v secs Number of seconds for which to delay | |
| * @v interrupted Interrupt checking method, or NULL | |
| * @ret secs Number of seconds remaining, if interrupted | |
| */ | |
| static unsigned int sleep_interruptible ( unsigned int secs, | |
| int ( * interrupted ) ( void ) ) { | |
| unsigned long start = currticks(); | |
| unsigned long now; | |
| for ( ; secs ; secs-- ) { | |
| while ( ( ( now = currticks() ) - start ) < TICKS_PER_SEC ) { | |
| step(); | |
| if ( interrupted && interrupted() ) | |
| return secs; | |
| cpu_nap(); | |
| } | |
| start = now; | |
| } | |
| return 0; | |
| } | |
| /** | |
| * Check if sleep has been interrupted by keypress | |
| * | |
| * @ret interrupted Sleep has been interrupted | |
| */ | |
| static int keypress_interrupted ( void ) { | |
| return ( iskey() && ( getchar() == CTRL_C ) ); | |
| } | |
| /** | |
| * Sleep (interruptibly) for a fixed number of seconds | |
| * | |
| * @v secs Number of seconds for which to delay | |
| * @ret secs Number of seconds remaining, if interrupted | |
| */ | |
| unsigned int sleep ( unsigned int secs ) { | |
| return sleep_interruptible ( secs, keypress_interrupted ); | |
| } | |
| /** | |
| * Sleep (uninterruptibly) for a fixed number of seconds | |
| * | |
| * @v secs Number of seconds for which to delay | |
| */ | |
| void sleep_fixed ( unsigned int secs ) { | |
| sleep_interruptible ( secs, NULL ); | |
| } | |
| /** | |
| * Find a working timer | |
| * | |
| */ | |
| static void timer_probe ( void ) { | |
| int rc; | |
| /* Use first working timer */ | |
| for_each_table_entry ( timer, TIMERS ) { | |
| if ( ( timer->probe == NULL ) || | |
| ( ( rc = timer->probe() ) == 0 ) ) { | |
| DBGC ( &timer, "TIMER using %s\n", timer->name ); | |
| return; | |
| } | |
| DBGC ( &timer, "TIMER could not initialise %s: %s\n", | |
| timer->name, strerror ( rc ) ); | |
| } | |
| /* This is a fatal error */ | |
| DBGC ( &timer, "TIMER found no working timers!\n" ); | |
| while ( 1 ) {} | |
| } | |
| /** Timer initialisation function */ | |
| struct init_fn timer_init_fn __init_fn ( INIT_EARLY ) = { | |
| .name = "timer", | |
| .initialise = timer_probe, | |
| }; | |
| /* Drag in timer configuration */ | |
| REQUIRING_SYMBOL ( timer_init_fn ); | |
| REQUIRE_OBJECT ( config_timer ); |