2005-04-16 15:20:36 -07:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* arch / sh64 / kernel / time . c
*
* Copyright ( C ) 2000 , 2001 Paolo Alberelli
* Copyright ( C ) 2003 , 2004 Paul Mundt
* Copyright ( C ) 2003 Richard Curnow
*
* Original TMU / RTC code taken from sh version .
* Copyright ( C ) 1999 Tetsuya Okada & Niibe Yutaka
* Some code taken from i386 version .
* Copyright ( C ) 1991 , 1992 , 1995 Linus Torvalds
*/
# include <linux/config.h>
# include <linux/errno.h>
# include <linux/rwsem.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/param.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/time.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/profile.h>
# include <linux/smp.h>
2006-02-01 03:06:09 -08:00
# include <linux/module.h>
2006-03-28 01:56:10 -08:00
# include <linux/bcd.h>
2005-04-16 15:20:36 -07:00
# include <asm/registers.h> /* required by inline __asm__ stmt. */
# include <asm/processor.h>
# include <asm/uaccess.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/delay.h>
# include <linux/timex.h>
# include <linux/irq.h>
# include <asm/hardware.h>
# define TMU_TOCR_INIT 0x00
# define TMU0_TCR_INIT 0x0020
# define TMU_TSTR_INIT 1
# define TMU_TSTR_OFF 0
/* RCR1 Bits */
# define RCR1_CF 0x80 /* Carry Flag */
# define RCR1_CIE 0x10 /* Carry Interrupt Enable */
# define RCR1_AIE 0x08 /* Alarm Interrupt Enable */
# define RCR1_AF 0x01 /* Alarm Flag */
/* RCR2 Bits */
# define RCR2_PEF 0x80 /* PEriodic interrupt Flag */
# define RCR2_PESMASK 0x70 /* Periodic interrupt Set */
# define RCR2_RTCEN 0x08 /* ENable RTC */
# define RCR2_ADJ 0x04 /* ADJustment (30-second) */
# define RCR2_RESET 0x02 /* Reset bit */
# define RCR2_START 0x01 /* Start bit */
/* Clock, Power and Reset Controller */
# define CPRC_BLOCK_OFF 0x01010000
# define CPRC_BASE PHYS_PERIPHERAL_BLOCK + CPRC_BLOCK_OFF
# define FRQCR (cprc_base+0x0)
# define WTCSR (cprc_base+0x0018)
# define STBCR (cprc_base+0x0030)
/* Time Management Unit */
# define TMU_BLOCK_OFF 0x01020000
# define TMU_BASE PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF
# define TMU0_BASE tmu_base + 0x8 + (0xc * 0x0)
# define TMU1_BASE tmu_base + 0x8 + (0xc * 0x1)
# define TMU2_BASE tmu_base + 0x8 + (0xc * 0x2)
# define TMU_TOCR tmu_base+0x0 /* Byte access */
# define TMU_TSTR tmu_base+0x4 /* Byte access */
# define TMU0_TCOR TMU0_BASE+0x0 /* Long access */
# define TMU0_TCNT TMU0_BASE+0x4 /* Long access */
# define TMU0_TCR TMU0_BASE+0x8 /* Word access */
/* Real Time Clock */
# define RTC_BLOCK_OFF 0x01040000
# define RTC_BASE PHYS_PERIPHERAL_BLOCK + RTC_BLOCK_OFF
# define R64CNT rtc_base+0x00
# define RSECCNT rtc_base+0x04
# define RMINCNT rtc_base+0x08
# define RHRCNT rtc_base+0x0c
# define RWKCNT rtc_base+0x10
# define RDAYCNT rtc_base+0x14
# define RMONCNT rtc_base+0x18
# define RYRCNT rtc_base+0x1c /* 16bit */
# define RSECAR rtc_base+0x20
# define RMINAR rtc_base+0x24
# define RHRAR rtc_base+0x28
# define RWKAR rtc_base+0x2c
# define RDAYAR rtc_base+0x30
# define RMONAR rtc_base+0x34
# define RCR1 rtc_base+0x38
# define RCR2 rtc_base+0x3c
# define TICK_SIZE (tick_nsec / 1000)
extern unsigned long wall_jiffies ;
static unsigned long tmu_base , rtc_base ;
unsigned long cprc_base ;
/* Variables to allow interpolation of time of day to resolution better than a
* jiffy . */
/* This is effectively protected by xtime_lock */
static unsigned long ctc_last_interrupt ;
static unsigned long long usecs_per_jiffy = 1000000 / HZ ; /* Approximation */
# define CTC_JIFFY_SCALE_SHIFT 40
/* 2**CTC_JIFFY_SCALE_SHIFT / ctc_ticks_per_jiffy */
static unsigned long long scaled_recip_ctc_ticks_per_jiffy ;
/* Estimate number of microseconds that have elapsed since the last timer tick,
by scaling the delta that has occured in the CTC register .
WARNING WARNING WARNING : This algorithm relies on the CTC decrementing at
the CPU clock rate . If the CPU sleeps , the CTC stops counting . Bear this
in mind if enabling SLEEP_WORKS in process . c . In that case , this algorithm
probably needs to use TMU . TCNT0 instead . This will work even if the CPU is
sleeping , though will be coarser .
FIXME : What if usecs_per_tick is moving around too much , e . g . if an adjtime
is running or if the freq or tick arguments of adjtimex are modified after
we have calibrated the scaling factor ? This will result in either a jump at
the end of a tick period , or a wrap backwards at the start of the next one ,
if the application is reading the time of day often enough . I think we
ought to do better than this . For this reason , usecs_per_jiffy is left
separated out in the calculation below . This allows some future hook into
the adjtime - related stuff in kernel / timer . c to remove this hazard .
*/
static unsigned long usecs_since_tick ( void )
{
unsigned long long current_ctc ;
long ctc_ticks_since_interrupt ;
unsigned long long ull_ctc_ticks_since_interrupt ;
unsigned long result ;
unsigned long long mul1_out ;
unsigned long long mul1_out_high ;
unsigned long long mul2_out_low , mul2_out_high ;
/* Read CTC register */
asm ( " getcon cr62, %0 " : " =r " ( current_ctc ) ) ;
/* Note, the CTC counts down on each CPU clock, not up.
Note ( 2 ) , use long type to get correct wraparound arithmetic when
the counter crosses zero . */
ctc_ticks_since_interrupt = ( long ) ctc_last_interrupt - ( long ) current_ctc ;
ull_ctc_ticks_since_interrupt = ( unsigned long long ) ctc_ticks_since_interrupt ;
/* Inline assembly to do 32x32x32->64 multiplier */
asm volatile ( " mulu.l %1, %2, %0 " :
" =r " ( mul1_out ) :
" r " ( ull_ctc_ticks_since_interrupt ) , " r " ( usecs_per_jiffy ) ) ;
mul1_out_high = mul1_out > > 32 ;
asm volatile ( " mulu.l %1, %2, %0 " :
" =r " ( mul2_out_low ) :
" r " ( mul1_out ) , " r " ( scaled_recip_ctc_ticks_per_jiffy ) ) ;
# if 1
asm volatile ( " mulu.l %1, %2, %0 " :
" =r " ( mul2_out_high ) :
" r " ( mul1_out_high ) , " r " ( scaled_recip_ctc_ticks_per_jiffy ) ) ;
# endif
result = ( unsigned long ) ( ( ( mul2_out_high < < 32 ) + mul2_out_low ) > > CTC_JIFFY_SCALE_SHIFT ) ;
return result ;
}
void do_gettimeofday ( struct timeval * tv )
{
unsigned long flags ;
unsigned long seq ;
unsigned long usec , sec ;
do {
seq = read_seqbegin_irqsave ( & xtime_lock , flags ) ;
usec = usecs_since_tick ( ) ;
{
unsigned long lost = jiffies - wall_jiffies ;
if ( lost )
usec + = lost * ( 1000000 / HZ ) ;
}
sec = xtime . tv_sec ;
usec + = xtime . tv_nsec / 1000 ;
} while ( read_seqretry_irqrestore ( & xtime_lock , seq , flags ) ) ;
while ( usec > = 1000000 ) {
usec - = 1000000 ;
sec + + ;
}
tv - > tv_sec = sec ;
tv - > tv_usec = usec ;
}
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 - = 1000 * ( usecs_since_tick ( ) +
( jiffies - wall_jiffies ) * ( 1000000 / HZ ) ) ;
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-06 15:17:46 -07:00
ntp_clear ( ) ;
2005-04-16 15:20:36 -07:00
write_sequnlock_irq ( & xtime_lock ) ;
clock_was_set ( ) ;
return 0 ;
}
2005-10-29 07:32:07 +01:00
EXPORT_SYMBOL ( do_settimeofday ) ;
2005-04-16 15:20:36 -07:00
static int set_rtc_time ( unsigned long nowtime )
{
int retval = 0 ;
int real_seconds , real_minutes , cmos_minutes ;
ctrl_outb ( RCR2_RESET , RCR2 ) ; /* Reset pre-scaler & stop RTC */
cmos_minutes = ctrl_inb ( RMINCNT ) ;
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 ) ;
ctrl_outb ( real_seconds , RSECCNT ) ;
ctrl_outb ( real_minutes , RMINCNT ) ;
} else {
printk ( KERN_WARNING
" set_rtc_time: can't update from %d to %d \n " ,
cmos_minutes , real_minutes ) ;
retval = - 1 ;
}
ctrl_outb ( RCR2_RTCEN | RCR2_START , RCR2 ) ; /* Start RTC */
return retval ;
}
/* last time the RTC clock got updated */
static long last_rtc_update = 0 ;
/*
* timer_interrupt ( ) needs to keep up the real - time clock ,
* as well as call the " do_timer() " routine every clocktick
*/
2005-09-06 15:17:39 -07:00
static inline void do_timer_interrupt ( int irq , struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
unsigned long long current_ctc ;
asm ( " getcon cr62, %0 " : " =r " ( current_ctc ) ) ;
ctc_last_interrupt = ( unsigned long ) current_ctc ;
do_timer ( regs ) ;
# ifndef CONFIG_SMP
update_process_times ( user_mode ( regs ) ) ;
# endif
profile_tick ( CPU_PROFILING , regs ) ;
# ifdef CONFIG_HEARTBEAT
{
extern void heartbeat ( void ) ;
heartbeat ( ) ;
}
# endif
/*
* If we have an externally synchronized Linux clock , then update
* RTC clock accordingly every ~ 11 minutes . Set_rtc_mmss ( ) has to be
* called as close as possible to 500 ms before the new second starts .
*/
2005-09-06 15:17:46 -07:00
if ( ntp_synced ( ) & &
2005-04-16 15:20:36 -07:00
xtime . tv_sec > last_rtc_update + 660 & &
( xtime . tv_nsec / 1000 ) > = 500000 - ( ( unsigned ) TICK_SIZE ) / 2 & &
( xtime . tv_nsec / 1000 ) < = 500000 + ( ( unsigned ) TICK_SIZE ) / 2 ) {
if ( set_rtc_time ( xtime . tv_sec ) = = 0 )
last_rtc_update = xtime . tv_sec ;
else
last_rtc_update = xtime . tv_sec - 600 ; /* do it again in 60 s */
}
}
/*
* This is the same as the above , except we _also_ save the current
* Time Stamp Counter value at the time of the timer interrupt , so that
* we later on can estimate the time of day more exactly .
*/
static irqreturn_t timer_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
unsigned long timer_status ;
/* Clear UNF bit */
timer_status = ctrl_inw ( TMU0_TCR ) ;
timer_status & = ~ 0x100 ;
ctrl_outw ( timer_status , TMU0_TCR ) ;
/*
* Here we are in the timer irq handler . We just have irqs locally
* disabled but we don ' t know if the timer_bh is running on the other
* CPU . We need to avoid to SMP race with it . NOTE : we don ' t need
* the irq version of write_lock because as just said we have irq
* locally disabled . - arca
*/
write_lock ( & xtime_lock ) ;
2005-09-06 15:17:39 -07:00
do_timer_interrupt ( irq , regs ) ;
2005-04-16 15:20:36 -07:00
write_unlock ( & xtime_lock ) ;
return IRQ_HANDLED ;
}
static unsigned long get_rtc_time ( void )
{
unsigned int sec , min , hr , wk , day , mon , yr , yr100 ;
again :
do {
ctrl_outb ( 0 , RCR1 ) ; /* Clear CF-bit */
sec = ctrl_inb ( RSECCNT ) ;
min = ctrl_inb ( RMINCNT ) ;
hr = ctrl_inb ( RHRCNT ) ;
wk = ctrl_inb ( RWKCNT ) ;
day = ctrl_inb ( RDAYCNT ) ;
mon = ctrl_inb ( RMONCNT ) ;
yr = ctrl_inw ( RYRCNT ) ;
yr100 = ( yr > > 8 ) ;
yr & = 0xff ;
} while ( ( ctrl_inb ( RCR1 ) & RCR1_CF ) ! = 0 ) ;
BCD_TO_BIN ( yr100 ) ;
BCD_TO_BIN ( yr ) ;
BCD_TO_BIN ( mon ) ;
BCD_TO_BIN ( day ) ;
BCD_TO_BIN ( hr ) ;
BCD_TO_BIN ( min ) ;
BCD_TO_BIN ( sec ) ;
if ( yr > 99 | | mon < 1 | | mon > 12 | | day > 31 | | day < 1 | |
hr > 23 | | min > 59 | | sec > 59 ) {
printk ( KERN_ERR
" SH RTC: invalid value, resetting to 1 Jan 2000 \n " ) ;
ctrl_outb ( RCR2_RESET , RCR2 ) ; /* Reset & Stop */
ctrl_outb ( 0 , RSECCNT ) ;
ctrl_outb ( 0 , RMINCNT ) ;
ctrl_outb ( 0 , RHRCNT ) ;
ctrl_outb ( 6 , RWKCNT ) ;
ctrl_outb ( 1 , RDAYCNT ) ;
ctrl_outb ( 1 , RMONCNT ) ;
ctrl_outw ( 0x2000 , RYRCNT ) ;
ctrl_outb ( RCR2_RTCEN | RCR2_START , RCR2 ) ; /* Start */
goto again ;
}
return mktime ( yr100 * 100 + yr , mon , day , hr , min , sec ) ;
}
static __init unsigned int get_cpu_hz ( void )
{
unsigned int count ;
unsigned long __dummy ;
unsigned long ctc_val_init , ctc_val ;
/*
* * Regardless the toolchain , force the compiler to use the
* * arbitrary register r3 as a clock tick counter .
2006-01-08 01:02:15 -08:00
* * NOTE : r3 must be in accordance with sh64_rtc_interrupt ( )
2005-04-16 15:20:36 -07:00
*/
register unsigned long long __rtc_irq_flag __asm__ ( " r3 " ) ;
local_irq_enable ( ) ;
do { } while ( ctrl_inb ( R64CNT ) ! = 0 ) ;
ctrl_outb ( RCR1_CIE , RCR1 ) ; /* Enable carry interrupt */
/*
* r3 is arbitrary . CDC does not support " =z " .
*/
ctc_val_init = 0xffffffff ;
ctc_val = ctc_val_init ;
asm volatile ( " gettr tr0, %1 \n \t "
" putcon %0, " __CTC " \n \t "
" and %2, r63, %2 \n \t "
" pta $+4, tr0 \n \t "
" beq/l %2, r63, tr0 \n \t "
" ptabs %1, tr0 \n \t "
" getcon " __CTC " , %0 \n \t "
: " =r " ( ctc_val ) , " =r " ( __dummy ) , " =r " ( __rtc_irq_flag )
: " 0 " ( 0 ) ) ;
local_irq_disable ( ) ;
/*
* SH - 3 :
* CPU clock = 4 stages * loop
* tst rm , rm if id ex
* bt / s 1 b if id ex
* add # 1 , rd if id ex
* ( if ) pipe line stole
* tst rm , rm if id ex
* . . . .
*
*
* SH - 4 :
* CPU clock = 6 stages * loop
* I don ' t know why .
* . . . .
*
* SH - 5 :
* Use CTC register to count . This approach returns the right value
* even if the I - cache is disabled ( e . g . whilst debugging . )
*
*/
count = ctc_val_init - ctc_val ; /* CTC counts down */
# if defined (CONFIG_SH_SIMULATOR)
/*
* Let ' s pretend we are a 5 MHz SH - 5 to avoid a too
* little timer interval . Also to keep delay
* calibration within a reasonable time .
*/
return 5000000 ;
# else
/*
* This really is count by the number of clock cycles
* by the ratio between a complete R64CNT
* wrap - around ( 128 ) and CUI interrupt being raised ( 64 ) .
*/
return count * 2 ;
# endif
}
2006-01-08 01:02:15 -08:00
static irqreturn_t sh64_rtc_interrupt ( int irq , void * dev_id ,
struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
ctrl_outb ( 0 , RCR1 ) ; /* Disable Carry Interrupts */
regs - > regs [ 3 ] = 1 ; /* Using r3 */
return IRQ_HANDLED ;
}
static struct irqaction irq0 = { timer_interrupt , SA_INTERRUPT , CPU_MASK_NONE , " timer " , NULL , NULL } ;
2006-01-08 01:02:15 -08:00
static struct irqaction irq1 = { sh64_rtc_interrupt , SA_INTERRUPT , CPU_MASK_NONE , " rtc " , NULL , NULL } ;
2005-04-16 15:20:36 -07:00
void __init time_init ( void )
{
unsigned int cpu_clock , master_clock , bus_clock , module_clock ;
unsigned long interval ;
unsigned long frqcr , ifc , pfc ;
static int ifc_table [ ] = { 2 , 4 , 6 , 8 , 10 , 12 , 16 , 24 } ;
# define bfc_table ifc_table /* Same */
# define pfc_table ifc_table /* Same */
tmu_base = onchip_remap ( TMU_BASE , 1024 , " TMU " ) ;
if ( ! tmu_base ) {
panic ( " Unable to remap TMU \n " ) ;
}
rtc_base = onchip_remap ( RTC_BASE , 1024 , " RTC " ) ;
if ( ! rtc_base ) {
panic ( " Unable to remap RTC \n " ) ;
}
cprc_base = onchip_remap ( CPRC_BASE , 1024 , " CPRC " ) ;
if ( ! cprc_base ) {
panic ( " Unable to remap CPRC \n " ) ;
}
xtime . tv_sec = get_rtc_time ( ) ;
xtime . tv_nsec = 0 ;
setup_irq ( TIMER_IRQ , & irq0 ) ;
setup_irq ( RTC_IRQ , & irq1 ) ;
/* Check how fast it is.. */
cpu_clock = get_cpu_hz ( ) ;
/* Note careful order of operations to maintain reasonable precision and avoid overflow. */
scaled_recip_ctc_ticks_per_jiffy = ( ( 1ULL < < CTC_JIFFY_SCALE_SHIFT ) / ( unsigned long long ) ( cpu_clock / HZ ) ) ;
disable_irq ( RTC_IRQ ) ;
printk ( " CPU clock: %d.%02dMHz \n " ,
( cpu_clock / 1000000 ) , ( cpu_clock % 1000000 ) / 10000 ) ;
{
unsigned short bfc ;
frqcr = ctrl_inl ( FRQCR ) ;
ifc = ifc_table [ ( frqcr > > 6 ) & 0x0007 ] ;
bfc = bfc_table [ ( frqcr > > 3 ) & 0x0007 ] ;
pfc = pfc_table [ ( frqcr > > 12 ) & 0x0007 ] ;
master_clock = cpu_clock * ifc ;
bus_clock = master_clock / bfc ;
}
printk ( " Bus clock: %d.%02dMHz \n " ,
( bus_clock / 1000000 ) , ( bus_clock % 1000000 ) / 10000 ) ;
module_clock = master_clock / pfc ;
printk ( " Module clock: %d.%02dMHz \n " ,
( module_clock / 1000000 ) , ( module_clock % 1000000 ) / 10000 ) ;
interval = ( module_clock / ( HZ * 4 ) ) ;
printk ( " Interval = %ld \n " , interval ) ;
current_cpu_data . cpu_clock = cpu_clock ;
current_cpu_data . master_clock = master_clock ;
current_cpu_data . bus_clock = bus_clock ;
current_cpu_data . module_clock = module_clock ;
/* Start TMU0 */
ctrl_outb ( TMU_TSTR_OFF , TMU_TSTR ) ;
ctrl_outb ( TMU_TOCR_INIT , TMU_TOCR ) ;
ctrl_outw ( TMU0_TCR_INIT , TMU0_TCR ) ;
ctrl_outl ( interval , TMU0_TCOR ) ;
ctrl_outl ( interval , TMU0_TCNT ) ;
ctrl_outb ( TMU_TSTR_INIT , TMU_TSTR ) ;
}
void enter_deep_standby ( void )
{
/* Disable watchdog timer */
ctrl_outl ( 0xa5000000 , WTCSR ) ;
/* Configure deep standby on sleep */
ctrl_outl ( 0x03 , STBCR ) ;
# ifdef CONFIG_SH_ALPHANUMERIC
{
extern void mach_alphanum ( int position , unsigned char value ) ;
extern void mach_alphanum_brightness ( int setting ) ;
char halted [ ] = " Halted. " ;
int i ;
mach_alphanum_brightness ( 6 ) ; /* dimmest setting above off */
for ( i = 0 ; i < 8 ; i + + ) {
mach_alphanum ( i , halted [ i ] ) ;
}
asm __volatile__ ( " synco " ) ;
}
# endif
asm __volatile__ ( " sleep " ) ;
asm __volatile__ ( " synci " ) ;
asm __volatile__ ( " nop " ) ;
asm __volatile__ ( " nop " ) ;
asm __volatile__ ( " nop " ) ;
asm __volatile__ ( " nop " ) ;
panic ( " Unexpected wakeup! \n " ) ;
}
/*
* Scheduler clock - returns current time in nanosec units .
*/
unsigned long long sched_clock ( void )
{
return ( unsigned long long ) jiffies * ( 1000000000 / HZ ) ;
}