2006-01-18 17:42:42 -08:00
/*
2005-04-16 15:20:36 -07:00
* Copyright ( C ) 2000 Jeff Dike ( jdike @ karaya . com )
* Licensed under the GPL
*/
# include "linux/kernel.h"
# include "linux/module.h"
# include "linux/unistd.h"
# include "linux/stddef.h"
# include "linux/spinlock.h"
# include "linux/time.h"
# include "linux/sched.h"
# include "linux/interrupt.h"
# include "linux/init.h"
# include "linux/delay.h"
2006-01-18 17:42:42 -08:00
# include "linux/hrtimer.h"
2005-04-16 15:20:36 -07:00
# include "asm/irq.h"
# include "asm/param.h"
# include "asm/current.h"
# include "kern_util.h"
# include "user_util.h"
# include "mode.h"
# include "os.h"
int hz ( void )
{
return ( HZ ) ;
}
/*
* Scheduler clock - returns current time in nanosec units .
*/
unsigned long long sched_clock ( void )
{
return ( unsigned long long ) jiffies_64 * ( 1000000000 / HZ ) ;
}
2006-01-18 17:42:42 -08:00
static unsigned long long prev_nsecs ;
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_UML_REAL_TIME_CLOCK
static long long delta ; /* Deviation per interval */
# endif
void timer_irq ( union uml_pt_regs * regs )
{
unsigned long long ticks = 0 ;
# ifdef CONFIG_UML_REAL_TIME_CLOCK
2006-07-10 04:45:08 -07:00
if ( prev_nsecs ) {
2005-04-16 15:20:36 -07:00
/* We've had 1 tick */
2006-01-18 17:42:42 -08:00
unsigned long long nsecs = os_nsecs ( ) ;
2005-04-16 15:20:36 -07:00
2006-01-18 17:42:42 -08:00
delta + = nsecs - prev_nsecs ;
prev_nsecs = nsecs ;
2005-04-16 15:20:36 -07:00
/* Protect against the host clock being set backwards */
if ( delta < 0 )
delta = 0 ;
2006-01-18 17:42:42 -08:00
ticks + = ( delta * HZ ) / BILLION ;
delta - = ( ticks * BILLION ) / HZ ;
2006-07-10 04:45:08 -07:00
}
else prev_nsecs = os_nsecs ( ) ;
2005-04-16 15:20:36 -07:00
# else
2006-07-10 04:45:08 -07:00
ticks = 1 ;
2005-04-16 15:20:36 -07:00
# endif
while ( ticks > 0 ) {
do_IRQ ( TIMER_IRQ , regs ) ;
ticks - - ;
}
}
2006-01-18 17:42:42 -08:00
static DEFINE_SPINLOCK ( timer_spinlock ) ;
static unsigned long long local_offset = 0 ;
static inline unsigned long long get_time ( void )
{
unsigned long long nsecs ;
unsigned long flags ;
spin_lock_irqsave ( & timer_spinlock , flags ) ;
nsecs = os_nsecs ( ) ;
nsecs + = local_offset ;
spin_unlock_irqrestore ( & timer_spinlock , flags ) ;
return nsecs ;
}
2006-10-08 22:49:34 +01:00
irqreturn_t um_timer ( int irq , void * dev )
2005-04-16 15:20:36 -07:00
{
2006-01-18 17:42:42 -08:00
unsigned long long nsecs ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
2006-06-30 01:55:56 -07:00
write_seqlock_irqsave ( & xtime_lock , flags ) ;
2006-09-29 02:00:32 -07:00
do_timer ( 1 ) ;
2006-01-18 17:42:42 -08:00
2006-09-27 01:50:42 -07:00
nsecs = get_time ( ) ;
2006-01-18 17:42:42 -08:00
xtime . tv_sec = nsecs / NSEC_PER_SEC ;
xtime . tv_nsec = nsecs - xtime . tv_sec * NSEC_PER_SEC ;
2006-06-30 01:55:56 -07:00
2005-04-16 15:20:36 -07:00
write_sequnlock_irqrestore ( & xtime_lock , flags ) ;
2006-01-18 17:42:42 -08:00
2006-06-30 01:55:56 -07:00
return IRQ_HANDLED ;
2005-04-16 15:20:36 -07:00
}
2006-07-10 04:45:05 -07:00
static void register_timer ( void )
{
int err ;
err = request_irq ( TIMER_IRQ , um_timer , IRQF_DISABLED , " timer " , NULL ) ;
if ( err ! = 0 )
2006-09-25 23:33:05 -07:00
printk ( KERN_ERR " register_timer : request_irq failed - "
2006-07-10 04:45:05 -07:00
" errno = %d \n " , - err ) ;
2006-09-25 23:33:05 -07:00
err = set_interval ( 1 ) ;
if ( err ! = 0 )
printk ( KERN_ERR " register_timer : set_interval failed - "
" errno = %d \n " , - err ) ;
2006-07-10 04:45:05 -07:00
}
extern void ( * late_time_init ) ( void ) ;
void time_init ( void )
{
long long nsecs ;
nsecs = os_nsecs ( ) ;
set_normalized_timespec ( & wall_to_monotonic , - nsecs / BILLION ,
- nsecs % BILLION ) ;
late_time_init = register_timer ;
}
2006-01-18 17:42:42 -08:00
void do_gettimeofday ( struct timeval * tv )
{
unsigned long long nsecs = get_time ( ) ;
tv - > tv_sec = nsecs / NSEC_PER_SEC ;
/* Careful about calculations here - this was originally done as
* ( nsecs - tv - > tv_sec * NSEC_PER_SEC ) / NSEC_PER_USEC
* which gave bogus ( > 1000000 ) values . Dunno why , suspect gcc
* ( 4.0 .0 ) miscompiled it , or there ' s a subtle 64 / 32 - bit conversion
* problem that I missed .
*/
nsecs - = tv - > tv_sec * NSEC_PER_SEC ;
tv - > tv_usec = ( unsigned long ) nsecs / NSEC_PER_USEC ;
}
static inline void set_time ( unsigned long long nsecs )
{
unsigned long long now ;
unsigned long flags ;
spin_lock_irqsave ( & timer_spinlock , flags ) ;
now = os_nsecs ( ) ;
local_offset = nsecs - now ;
spin_unlock_irqrestore ( & timer_spinlock , flags ) ;
clock_was_set ( ) ;
2005-04-16 15:20:36 -07:00
}
2006-01-18 17:42:42 -08:00
int do_settimeofday ( struct timespec * tv )
{
set_time ( ( unsigned long long ) tv - > tv_sec * NSEC_PER_SEC + tv - > tv_nsec ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
void timer_handler ( int sig , union uml_pt_regs * regs )
{
local_irq_disable ( ) ;
2005-07-28 21:16:09 -07:00
irq_enter ( ) ;
2006-01-18 17:42:51 -08:00
update_process_times ( CHOOSE_MODE (
( UPT_SC ( regs ) & & user_context ( UPT_SP ( regs ) ) ) ,
( regs ) - > skas . is_user ) ) ;
2005-07-28 21:16:09 -07:00
irq_exit ( ) ;
2005-04-16 15:20:36 -07:00
local_irq_enable ( ) ;
if ( current_thread - > cpu = = 0 )
timer_irq ( regs ) ;
}