2007-10-27 00:17:01 -07:00
/* linux/arch/sparc/kernel/time.c
2005-04-16 15:20:36 -07:00
*
2007-10-27 00:17:01 -07:00
* Copyright ( C ) 1995 David S . Miller ( davem @ davemloft . net )
2005-04-16 15:20:36 -07:00
* Copyright ( C ) 1996 Thomas K . Dyas ( tdyas @ eden . rutgers . edu )
*
* Chris Davis ( cdavis @ cois . on . ca ) 03 / 27 / 1998
* Added support for the intersil on the sun4 / 4200
*
* Gleb Raiko ( rajko @ mech . math . msu . su ) 08 / 18 / 1998
* Support for MicroSPARC - IIep , PCI CPU .
*
* This file handles the Sparc specific time handling details .
*
* 1997 - 09 - 10 Updated NTP code according to technical memorandum Jan ' 96
* " A Kernel Model for Precision Timekeeping " by Dave Mills
*/
# include <linux/errno.h>
# include <linux/module.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>
2008-09-03 15:52:38 -07:00
# include <linux/rtc.h>
# include <linux/rtc/m48t59.h>
2005-04-16 15:20:36 -07:00
# include <linux/timex.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/ioport.h>
# include <linux/profile.h>
2008-08-27 04:05:35 -07:00
# include <linux/of.h>
2008-08-07 15:33:36 -07:00
# include <linux/of_device.h>
2008-09-03 15:52:38 -07:00
# include <linux/platform_device.h>
2005-04-16 15:20:36 -07:00
# include <asm/oplib.h>
# include <asm/timer.h>
# include <asm/system.h>
# include <asm/irq.h>
# include <asm/io.h>
# include <asm/idprom.h>
# include <asm/machines.h>
# include <asm/page.h>
# include <asm/pcic.h>
2006-10-08 14:30:44 +01:00
# include <asm/irq_regs.h>
2005-04-16 15:20:36 -07:00
2007-07-21 19:18:57 -07:00
# include "irq.h"
2005-04-16 15:20:36 -07:00
DEFINE_SPINLOCK ( rtc_lock ) ;
2009-01-08 16:58:05 -08:00
EXPORT_SYMBOL ( rtc_lock ) ;
2005-04-16 15:20:36 -07:00
static int set_rtc_mmss ( unsigned long ) ;
static int sbus_do_settimeofday ( struct timespec * tv ) ;
unsigned long profile_pc ( struct pt_regs * regs )
{
extern char __copy_user_begin [ ] , __copy_user_end [ ] ;
extern char __atomic_begin [ ] , __atomic_end [ ] ;
extern char __bzero_begin [ ] , __bzero_end [ ] ;
unsigned long pc = regs - > pc ;
if ( in_lock_functions ( pc ) | |
( pc > = ( unsigned long ) __copy_user_begin & &
pc < ( unsigned long ) __copy_user_end ) | |
( pc > = ( unsigned long ) __atomic_begin & &
pc < ( unsigned long ) __atomic_end ) | |
( pc > = ( unsigned long ) __bzero_begin & &
2006-12-17 16:18:47 -08:00
pc < ( unsigned long ) __bzero_end ) )
2005-04-16 15:20:36 -07:00
pc = regs - > u_regs [ UREG_RETPC ] ;
return pc ;
}
2006-10-17 19:21:48 -07:00
EXPORT_SYMBOL ( profile_pc ) ;
2005-04-16 15:20:36 -07:00
__volatile__ unsigned int * master_l10_counter ;
/*
* timer_interrupt ( ) needs to keep up the real - time clock ,
* as well as call the " do_timer() " routine every clocktick
*/
# define TICK_SIZE (tick_nsec / 1000)
2007-10-31 05:08:48 -04:00
static irqreturn_t timer_interrupt ( int dummy , void * dev_id )
2005-04-16 15:20:36 -07:00
{
/* last time the cmos clock got updated */
static long last_rtc_update ;
# ifndef CONFIG_SMP
2006-10-08 14:30:44 +01:00
profile_tick ( CPU_PROFILING ) ;
2005-04-16 15:20:36 -07:00
# endif
/* Protect counter clear so that do_gettimeoffset works */
write_seqlock ( & xtime_lock ) ;
2008-08-31 20:59:37 -07:00
2005-04-16 15:20:36 -07:00
clear_clock_irq ( ) ;
2006-09-29 02:00:32 -07:00
do_timer ( 1 ) ;
2005-04-16 15:20:36 -07:00
/* Determine when to update the Mostek clock. */
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_mmss ( xtime . tv_sec ) = = 0 )
last_rtc_update = xtime . tv_sec ;
else
last_rtc_update = xtime . tv_sec - 600 ; /* do it again in 60 s */
}
write_sequnlock ( & xtime_lock ) ;
2008-02-13 21:33:16 +01:00
# ifndef CONFIG_SMP
update_process_times ( user_mode ( get_irq_regs ( ) ) ) ;
# endif
2005-04-16 15:20:36 -07:00
return IRQ_HANDLED ;
}
2008-09-03 15:52:38 -07:00
static unsigned char mostek_read_byte ( struct device * dev , u32 ofs )
2005-04-16 15:20:36 -07:00
{
2008-09-03 15:52:38 -07:00
struct platform_device * pdev = to_platform_device ( dev ) ;
struct m48t59_plat_data * pdata = pdev - > dev . platform_data ;
2008-10-29 15:35:24 -07:00
return readb ( pdata - > ioaddr + ofs ) ;
2005-04-16 15:20:36 -07:00
}
2008-09-03 15:52:38 -07:00
static void mostek_write_byte ( struct device * dev , u32 ofs , u8 val )
2005-04-16 15:20:36 -07:00
{
2008-09-03 15:52:38 -07:00
struct platform_device * pdev = to_platform_device ( dev ) ;
struct m48t59_plat_data * pdata = pdev - > dev . platform_data ;
2008-10-29 15:35:24 -07:00
writeb ( val , pdata - > ioaddr + ofs ) ;
2005-04-16 15:20:36 -07:00
}
2008-09-03 15:52:38 -07:00
static struct m48t59_plat_data m48t59_data = {
. read_byte = mostek_read_byte ,
. write_byte = mostek_write_byte ,
} ;
/* resource is set at runtime */
static struct platform_device m48t59_rtc = {
. name = " rtc-m48t59 " ,
. id = 0 ,
. num_resources = 1 ,
. dev = {
. platform_data = & m48t59_data ,
} ,
} ;
2006-07-27 22:08:01 -07:00
2006-06-29 14:36:52 -07:00
static int __devinit clock_probe ( struct of_device * op , const struct of_device_id * match )
2005-04-16 15:20:36 -07:00
{
2006-06-29 14:36:52 -07:00
struct device_node * dp = op - > node ;
2007-03-29 00:47:23 -07:00
const char * model = of_get_property ( dp , " model " , NULL ) ;
2005-04-16 15:20:36 -07:00
2006-06-29 14:36:52 -07:00
if ( ! model )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
2008-09-03 15:52:38 -07:00
m48t59_rtc . resource = & op - > resource [ 0 ] ;
2006-06-29 14:36:52 -07:00
if ( ! strcmp ( model , " mk48t02 " ) ) {
2005-04-16 15:20:36 -07:00
/* Map the clock register io area read-only */
2008-09-03 15:52:38 -07:00
m48t59_data . ioaddr = of_ioremap ( & op - > resource [ 0 ] , 0 ,
2048 , " rtc-m48t59 " ) ;
m48t59_data . type = M48T59RTC_TYPE_M48T02 ;
2006-06-29 14:36:52 -07:00
} else if ( ! strcmp ( model , " mk48t08 " ) ) {
2008-09-03 15:52:38 -07:00
m48t59_data . ioaddr = of_ioremap ( & op - > resource [ 0 ] , 0 ,
8192 , " rtc-m48t59 " ) ;
m48t59_data . type = M48T59RTC_TYPE_M48T08 ;
2006-06-29 14:36:52 -07:00
} else
return - ENODEV ;
2005-04-16 15:20:36 -07:00
2008-09-03 15:52:38 -07:00
if ( platform_device_register ( & m48t59_rtc ) < 0 )
printk ( KERN_ERR " Registering RTC device failed \n " ) ;
2006-07-27 22:08:01 -07:00
2006-06-29 14:36:52 -07:00
return 0 ;
}
2008-08-31 01:23:17 -07:00
static struct of_device_id __initdata clock_match [ ] = {
2006-06-29 14:36:52 -07:00
{
. name = " eeprom " ,
} ,
{ } ,
} ;
static struct of_platform_driver clock_driver = {
. match_table = clock_match ,
. probe = clock_probe ,
2007-10-10 23:27:34 -07:00
. driver = {
2008-09-03 15:52:38 -07:00
. name = " rtc " ,
2007-10-10 23:27:34 -07:00
} ,
2006-06-29 14:36:52 -07:00
} ;
/* Probe for the mostek real time clock chip. */
2006-07-27 22:08:01 -07:00
static int __init clock_init ( void )
2006-06-29 14:36:52 -07:00
{
2007-04-30 17:43:56 +10:00
return of_register_driver ( & clock_driver , & of_platform_bus_type ) ;
2005-04-16 15:20:36 -07:00
}
2006-07-27 22:08:01 -07:00
/* Must be after subsys_initcall() so that busses are probed. Must
* be before device_initcall ( ) because things like the RTC driver
* need to see the clock registers .
*/
fs_initcall ( clock_init ) ;
2008-06-05 11:40:58 -07:00
static void __init sbus_time_init ( void )
2005-04-16 15:20:36 -07:00
{
BTFIXUPSET_CALL ( bus_do_settimeofday , sbus_do_settimeofday , BTFIXUPCALL_NORM ) ;
btfixup ( ) ;
sparc_init_timers ( timer_interrupt ) ;
/* Now that OBP ticker has been silenced, it is safe to enable IRQ. */
local_irq_enable ( ) ;
}
void __init time_init ( void )
{
# ifdef CONFIG_PCI
extern void pci_time_init ( void ) ;
if ( pcic_present ( ) ) {
pci_time_init ( ) ;
return ;
}
# endif
sbus_time_init ( ) ;
}
2005-10-03 17:37:02 -07:00
static inline unsigned long do_gettimeoffset ( void )
2005-04-16 15:20:36 -07:00
{
2008-01-09 05:09:06 -08:00
unsigned long val = * master_l10_counter ;
unsigned long usec = ( val > > 10 ) & 0x1fffff ;
/* Limit hit? */
if ( val & 0x80000000 )
usec + = 1000000 / HZ ;
return usec ;
2005-04-16 15:20:36 -07:00
}
/* Ok, my cute asm atomicity trick doesn't work anymore.
* There are just too many variables that need to be protected
2006-09-30 23:28:31 -07:00
* now ( both members of xtime , et al . )
2005-04-16 15:20:36 -07:00
*/
void do_gettimeofday ( struct timeval * tv )
{
unsigned long flags ;
unsigned long seq ;
unsigned long usec , sec ;
unsigned long max_ntp_tick = tick_usec - tickadj ;
do {
seq = read_seqbegin_irqsave ( & xtime_lock , 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 . .
*/
2006-09-30 23:28:31 -07:00
if ( unlikely ( time_adjust < 0 ) )
2005-04-16 15:20:36 -07:00
usec = min ( usec , max_ntp_tick ) ;
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 ;
}
EXPORT_SYMBOL ( do_gettimeofday ) ;
int do_settimeofday ( struct timespec * tv )
{
int ret ;
write_seqlock_irq ( & xtime_lock ) ;
ret = bus_do_settimeofday ( tv ) ;
write_sequnlock_irq ( & xtime_lock ) ;
clock_was_set ( ) ;
return ret ;
}
EXPORT_SYMBOL ( do_settimeofday ) ;
static int sbus_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 ;
/*
* 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 !
*/
2006-09-30 23:28:31 -07:00
nsec - = 1000 * do_gettimeoffset ( ) ;
2005-04-16 15:20:36 -07:00
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
return 0 ;
}
2008-09-03 15:52:38 -07:00
static int set_rtc_mmss ( unsigned long secs )
2005-04-16 15:20:36 -07:00
{
2008-09-03 15:52:38 -07:00
struct rtc_device * rtc = rtc_class_open ( " rtc0 " ) ;
2008-09-10 13:36:13 -07:00
int err = - 1 ;
2005-04-16 15:20:36 -07:00
2008-09-10 13:36:13 -07:00
if ( rtc ) {
err = rtc_set_mmss ( rtc , secs ) ;
rtc_class_close ( rtc ) ;
}
2005-04-16 15:20:36 -07:00
2008-09-10 13:36:13 -07:00
return err ;
2005-04-16 15:20:36 -07:00
}