2005-07-27 22:44:40 +04:00
/* $Id: time.c,v 1.18 2005/03/04 08:16:17 starvik Exp $
2005-04-17 02:20:36 +04:00
*
* linux / arch / cris / kernel / time . c
*
* Copyright ( C ) 1991 , 1992 , 1995 Linus Torvalds
* Copyright ( C ) 1999 , 2000 , 2001 Axis Communications AB
*
* 1994 - 07 - 02 Alan Modra
* fixed set_rtc_mmss , fixed time . year for > = 2000 , new mktime
* 1995 - 03 - 26 Markus Kuhn
* fixed 500 ms bug at call to set_rtc_mmss , fixed DS12887
* precision CMOS clock update
* 1996 - 05 - 03 Ingo Molnar
* fixed time warps in do_ [ slow | fast ] _gettimeoffset ( )
* 1997 - 09 - 10 Updated NTP code according to technical memorandum Jan ' 96
* " A Kernel Model for Precision Timekeeping " by Dave Mills
*
* Linux / CRIS specific code :
*
* Authors : Bjorn Wesen
* Johan Adolfsson
*
*/
# include <asm/rtc.h>
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/param.h>
# include <linux/jiffies.h>
# include <linux/bcd.h>
# include <linux/timex.h>
# include <linux/init.h>
2005-07-27 22:44:40 +04:00
# include <linux/profile.h>
2005-10-31 02:03:48 +03:00
# include <linux/sched.h> /* just for sched_clock() - funny that */
2005-04-17 02:20:36 +04:00
int have_rtc ; /* used to remember if we have an RTC or not */ ;
# define TICK_SIZE tick
extern unsigned long loops_per_jiffy ; /* init/main.c */
unsigned long loops_per_usec ;
extern unsigned long do_slow_gettimeoffset ( void ) ;
static unsigned long ( * do_gettimeoffset ) ( void ) = do_slow_gettimeoffset ;
/*
* This version of gettimeofday has near microsecond resolution .
*
* Note : Division is quite slow on CRIS and do_gettimeofday is called
* rather often . Maybe we should do some kind of approximation here
* ( a naive approximation would be to divide by 1024 ) .
*/
void do_gettimeofday ( struct timeval * tv )
{
unsigned long flags ;
signed long usec , sec ;
local_irq_save ( flags ) ;
usec = do_gettimeoffset ( ) ;
/*
* If time_adjust is negative then NTP is slowing the clock
* so make sure not to go into next possible interval .
* Better to lose some accuracy than have time go backwards . .
*/
if ( unlikely ( time_adjust < 0 ) & & usec > tickadj )
usec = tickadj ;
sec = xtime . tv_sec ;
usec + = xtime . tv_nsec / 1000 ;
local_irq_restore ( flags ) ;
while ( usec > = 1000000 ) {
usec - = 1000000 ;
sec + + ;
}
tv - > tv_sec = sec ;
tv - > tv_usec = usec ;
}
EXPORT_SYMBOL ( do_gettimeofday ) ;
int do_settimeofday ( struct timespec * tv )
{
time_t wtm_sec , sec = tv - > tv_sec ;
long wtm_nsec , nsec = tv - > tv_nsec ;
if ( ( unsigned long ) tv - > tv_nsec > = NSEC_PER_SEC )
return - EINVAL ;
write_seqlock_irq ( & xtime_lock ) ;
/*
* This is revolting . We need to set " xtime " correctly . However , the
* value in this location is the value at the most recent update of
* wall time . Discover what correction gettimeofday ( ) would have
* made , and then undo it !
*/
nsec - = do_gettimeoffset ( ) * NSEC_PER_USEC ;
wtm_sec = wall_to_monotonic . tv_sec + ( xtime . tv_sec - sec ) ;
wtm_nsec = wall_to_monotonic . tv_nsec + ( xtime . tv_nsec - nsec ) ;
set_normalized_timespec ( & xtime , sec , nsec ) ;
set_normalized_timespec ( & wall_to_monotonic , wtm_sec , wtm_nsec ) ;
2005-09-07 02:17:46 +04:00
ntp_clear ( ) ;
2005-04-17 02:20:36 +04:00
write_sequnlock_irq ( & xtime_lock ) ;
clock_was_set ( ) ;
return 0 ;
}
EXPORT_SYMBOL ( do_settimeofday ) ;
/*
* BUG : This routine does not handle hour overflow properly ; it just
* sets the minutes . Usually you ' ll only notice that after reboot !
*/
int set_rtc_mmss ( unsigned long nowtime )
{
int retval = 0 ;
int real_seconds , real_minutes , cmos_minutes ;
printk ( KERN_DEBUG " set_rtc_mmss(%lu) \n " , nowtime ) ;
if ( ! have_rtc )
return 0 ;
cmos_minutes = CMOS_READ ( RTC_MINUTES ) ;
BCD_TO_BIN ( cmos_minutes ) ;
/*
* since we ' re only adjusting minutes and seconds ,
* don ' t interfere with hour overflow . This avoids
* messing with unknown time zones but requires your
* RTC not to be off by more than 15 minutes
*/
real_seconds = nowtime % 60 ;
real_minutes = nowtime / 60 ;
if ( ( ( abs ( real_minutes - cmos_minutes ) + 15 ) / 30 ) & 1 )
real_minutes + = 30 ; /* correct for half hour time zone */
real_minutes % = 60 ;
if ( abs ( real_minutes - cmos_minutes ) < 30 ) {
BIN_TO_BCD ( real_seconds ) ;
BIN_TO_BCD ( real_minutes ) ;
CMOS_WRITE ( real_seconds , RTC_SECONDS ) ;
CMOS_WRITE ( real_minutes , RTC_MINUTES ) ;
} else {
printk ( KERN_WARNING
" set_rtc_mmss: can't update from %d to %d \n " ,
cmos_minutes , real_minutes ) ;
retval = - 1 ;
}
return retval ;
}
/* grab the time from the RTC chip */
unsigned long
get_cmos_time ( void )
{
unsigned int year , mon , day , hour , min , sec ;
sec = CMOS_READ ( RTC_SECONDS ) ;
min = CMOS_READ ( RTC_MINUTES ) ;
hour = CMOS_READ ( RTC_HOURS ) ;
day = CMOS_READ ( RTC_DAY_OF_MONTH ) ;
mon = CMOS_READ ( RTC_MONTH ) ;
year = CMOS_READ ( RTC_YEAR ) ;
printk ( KERN_DEBUG
" rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x \n " ,
sec , min , hour , day , mon , year ) ;
BCD_TO_BIN ( sec ) ;
BCD_TO_BIN ( min ) ;
BCD_TO_BIN ( hour ) ;
BCD_TO_BIN ( day ) ;
BCD_TO_BIN ( mon ) ;
BCD_TO_BIN ( year ) ;
if ( ( year + = 1900 ) < 1970 )
year + = 100 ;
return mktime ( year , mon , day , hour , min , sec ) ;
}
/* update xtime from the CMOS settings. used when /dev/rtc gets a SET_TIME.
* TODO : this doesn ' t reset the fancy NTP phase stuff as do_settimeofday does .
*/
void
update_xtime_from_cmos ( void )
{
if ( have_rtc ) {
xtime . tv_sec = get_cmos_time ( ) ;
xtime . tv_nsec = 0 ;
}
}
2005-07-27 22:44:40 +04:00
extern void cris_profile_sample ( struct pt_regs * regs ) ;
void
cris_do_profile ( struct pt_regs * regs )
{
# if CONFIG_SYSTEM_PROFILER
cris_profile_sample ( regs ) ;
# endif
# if CONFIG_PROFILING
profile_tick ( CPU_PROFILING , regs ) ;
# endif
}
2005-04-17 02:20:36 +04:00
static int
__init init_udelay ( void )
{
loops_per_usec = ( loops_per_jiffy * HZ ) / 1000000 ;
return 0 ;
}
__initcall ( init_udelay ) ;