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 ) ;
}
/* Changed at early boot */
int timer_irq_inited = 0 ;
static int first_tick ;
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 ;
if ( ! timer_irq_inited ) {
/* This is to ensure that ticks don't pile up when
* the timer handler is suspended */
first_tick = 0 ;
return ;
}
if ( first_tick ) {
# ifdef CONFIG_UML_REAL_TIME_CLOCK
/* 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 ;
2005-04-16 15:20:36 -07:00
# else
ticks = 1 ;
# endif
}
else {
2006-01-18 17:42:42 -08:00
prev_nsecs = os_nsecs ( ) ;
2005-04-16 15:20:36 -07:00
first_tick = 1 ;
}
while ( ticks > 0 ) {
do_IRQ ( TIMER_IRQ , regs ) ;
ticks - - ;
}
}
2006-01-18 17:42:43 -08:00
void do_boot_timer_handler ( struct sigcontext * sc )
2005-04-16 15:20:36 -07:00
{
struct pt_regs regs ;
2006-01-18 17:42:43 -08:00
CHOOSE_MODE ( ( void ) ( UPT_SC ( & regs . regs ) = sc ) ,
2005-04-16 15:20:36 -07:00
( void ) ( regs . regs . skas . is_user = 0 ) ) ;
do_timer ( & regs ) ;
}
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 ;
}
2005-04-16 15:20:36 -07:00
irqreturn_t um_timer ( int irq , void * dev , struct pt_regs * regs )
{
2006-01-18 17:42:42 -08:00
unsigned long long nsecs ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
do_timer ( regs ) ;
2006-01-18 17:42:42 -08:00
2005-04-16 15:20:36 -07:00
write_seqlock_irqsave ( & xtime_lock , flags ) ;
2006-01-18 17:42:42 -08:00
nsecs = get_time ( ) + local_offset ;
xtime . tv_sec = nsecs / NSEC_PER_SEC ;
xtime . tv_nsec = nsecs - xtime . tv_sec * NSEC_PER_SEC ;
2005-04-16 15:20:36 -07:00
write_sequnlock_irqrestore ( & xtime_lock , flags ) ;
2006-01-18 17:42:42 -08:00
2005-04-16 15:20:36 -07:00
return ( IRQ_HANDLED ) ;
}
long um_time ( int __user * tloc )
{
2006-01-18 17:42:42 -08:00
long ret = get_time ( ) / NSEC_PER_SEC ;
2005-04-16 15:20:36 -07:00
2006-01-18 17:42:42 -08:00
if ( ( tloc ! = NULL ) & & put_user ( ret , tloc ) )
return - EFAULT ;
return ret ;
}
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
}
long um_stime ( int __user * tptr )
{
int value ;
if ( get_user ( value , tptr ) )
return - EFAULT ;
2006-01-18 17:42:42 -08:00
set_time ( ( unsigned long long ) value * NSEC_PER_SEC ) ;
return 0 ;
}
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 ( ) ;
update_process_times ( CHOOSE_MODE ( user_context ( UPT_SP ( regs ) ) ,
( regs ) - > skas . is_user ) ) ;
irq_exit ( ) ;
2005-04-16 15:20:36 -07:00
local_irq_enable ( ) ;
if ( current_thread - > cpu = = 0 )
timer_irq ( regs ) ;
}
int __init timer_init ( void )
{
int err ;
2005-06-25 14:55:24 -07:00
user_time_init ( ) ;
2005-04-16 15:20:36 -07:00
err = request_irq ( TIMER_IRQ , um_timer , SA_INTERRUPT , " timer " , NULL ) ;
if ( err ! = 0 )
printk ( KERN_ERR " timer_init : request_irq failed - "
" errno = %d \n " , - err ) ;
timer_irq_inited = 1 ;
return ( 0 ) ;
}
__initcall ( timer_init ) ;