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>
# include <linux/timex.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/ioport.h>
# include <linux/profile.h>
# include <asm/oplib.h>
# include <asm/timer.h>
# include <asm/mostek.h>
# include <asm/system.h>
# include <asm/irq.h>
# include <asm/io.h>
# include <asm/idprom.h>
# include <asm/machines.h>
# include <asm/sun4paddr.h>
# include <asm/page.h>
# include <asm/pcic.h>
2006-06-29 14:36:52 -07:00
# include <asm/of_device.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 ) ;
enum sparc_clock_type sp_clock_typ ;
DEFINE_SPINLOCK ( mostek_lock ) ;
void __iomem * mstk48t02_regs = NULL ;
2005-12-06 05:55:44 -05:00
static struct mostek48t08 __iomem * mstk48t08_regs = NULL ;
2005-04-16 15:20:36 -07:00
static int set_rtc_mmss ( unsigned long ) ;
static int sbus_do_settimeofday ( struct timespec * tv ) ;
# ifdef CONFIG_SUN4
struct intersil * intersil_clock ;
# define intersil_cmd(intersil_reg, intsil_cmd) intersil_reg->int_cmd_reg = \
( intsil_cmd )
# define intersil_intr(intersil_reg, intsil_cmd) intersil_reg->int_intr_reg = \
( intsil_cmd )
# define intersil_start(intersil_reg) intersil_cmd(intersil_reg, \
( INTERSIL_START | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H | \
INTERSIL_INTR_ENABLE ) )
# define intersil_stop(intersil_reg) intersil_cmd(intersil_reg, \
( INTERSIL_STOP | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H | \
INTERSIL_INTR_ENABLE ) )
# define intersil_read_intr(intersil_reg, towhere) towhere = \
intersil_reg - > int_intr_reg
# endif
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 ;
__volatile__ unsigned int * master_l10_limit ;
/*
* 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 ) ;
# ifdef CONFIG_SUN4
if ( ( idprom - > id_machtype = = ( SM_SUN4 | SM_4_260 ) ) | |
( idprom - > id_machtype = = ( SM_SUN4 | SM_4_110 ) ) ) {
int temp ;
intersil_read_intr ( intersil_clock , temp ) ;
/* re-enable the irq */
enable_pil_irq ( 10 ) ;
}
# endif
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 ;
}
/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */
2007-05-19 12:03:24 -07:00
static void __devinit kick_start_clock ( void )
2005-04-16 15:20:36 -07:00
{
struct mostek48t02 * regs = ( struct mostek48t02 * ) mstk48t02_regs ;
unsigned char sec ;
int i , count ;
prom_printf ( " CLOCK: Clock was stopped. Kick start " ) ;
spin_lock_irq ( & mostek_lock ) ;
/* Turn on the kick start bit to start the oscillator. */
regs - > creg | = MSTK_CREG_WRITE ;
regs - > sec & = ~ MSTK_STOP ;
regs - > hour | = MSTK_KICK_START ;
regs - > creg & = ~ MSTK_CREG_WRITE ;
spin_unlock_irq ( & mostek_lock ) ;
/* Delay to allow the clock oscillator to start. */
sec = MSTK_REG_SEC ( regs ) ;
for ( i = 0 ; i < 3 ; i + + ) {
while ( sec = = MSTK_REG_SEC ( regs ) )
for ( count = 0 ; count < 100000 ; count + + )
/* nothing */ ;
prom_printf ( " . " ) ;
sec = regs - > sec ;
}
prom_printf ( " \n " ) ;
spin_lock_irq ( & mostek_lock ) ;
/* Turn off kick start and set a "valid" time and date. */
regs - > creg | = MSTK_CREG_WRITE ;
regs - > hour & = ~ MSTK_KICK_START ;
MSTK_SET_REG_SEC ( regs , 0 ) ;
MSTK_SET_REG_MIN ( regs , 0 ) ;
MSTK_SET_REG_HOUR ( regs , 0 ) ;
MSTK_SET_REG_DOW ( regs , 5 ) ;
MSTK_SET_REG_DOM ( regs , 1 ) ;
MSTK_SET_REG_MONTH ( regs , 8 ) ;
MSTK_SET_REG_YEAR ( regs , 1996 - MSTK_YEAR_ZERO ) ;
regs - > creg & = ~ MSTK_CREG_WRITE ;
spin_unlock_irq ( & mostek_lock ) ;
/* Ensure the kick start bit is off. If it isn't, turn it off. */
while ( regs - > hour & MSTK_KICK_START ) {
prom_printf ( " CLOCK: Kick start still on! \n " ) ;
spin_lock_irq ( & mostek_lock ) ;
regs - > creg | = MSTK_CREG_WRITE ;
regs - > hour & = ~ MSTK_KICK_START ;
regs - > creg & = ~ MSTK_CREG_WRITE ;
spin_unlock_irq ( & mostek_lock ) ;
}
prom_printf ( " CLOCK: Kick start procedure successful. \n " ) ;
}
/* Return nonzero if the clock chip battery is low. */
2007-10-27 00:17:01 -07:00
static inline int has_low_battery ( void )
2005-04-16 15:20:36 -07:00
{
struct mostek48t02 * regs = ( struct mostek48t02 * ) mstk48t02_regs ;
unsigned char data1 , data2 ;
spin_lock_irq ( & mostek_lock ) ;
data1 = regs - > eeprom [ 0 ] ; /* Read some data. */
regs - > eeprom [ 0 ] = ~ data1 ; /* Write back the complement. */
data2 = regs - > eeprom [ 0 ] ; /* Read back the complement. */
regs - > eeprom [ 0 ] = data1 ; /* Restore the original value. */
spin_unlock_irq ( & mostek_lock ) ;
return ( data1 = = data2 ) ; /* Was the write blocked? */
}
2007-05-19 12:03:24 -07:00
static void __devinit mostek_set_system_time ( void )
2006-07-27 22:08:01 -07:00
{
unsigned int year , mon , day , hour , min , sec ;
struct mostek48t02 * mregs ;
mregs = ( struct mostek48t02 * ) mstk48t02_regs ;
if ( ! mregs ) {
prom_printf ( " Something wrong, clock regs not mapped yet. \n " ) ;
prom_halt ( ) ;
}
spin_lock_irq ( & mostek_lock ) ;
mregs - > creg | = MSTK_CREG_READ ;
sec = MSTK_REG_SEC ( mregs ) ;
min = MSTK_REG_MIN ( mregs ) ;
hour = MSTK_REG_HOUR ( mregs ) ;
day = MSTK_REG_DOM ( mregs ) ;
mon = MSTK_REG_MONTH ( mregs ) ;
year = MSTK_CVT_YEAR ( MSTK_REG_YEAR ( mregs ) ) ;
xtime . tv_sec = mktime ( year , mon , day , hour , min , sec ) ;
xtime . tv_nsec = ( INITIAL_JIFFIES % HZ ) * ( NSEC_PER_SEC / HZ ) ;
set_normalized_timespec ( & wall_to_monotonic ,
- xtime . tv_sec , - xtime . tv_nsec ) ;
mregs - > creg & = ~ MSTK_CREG_READ ;
spin_unlock_irq ( & mostek_lock ) ;
}
2005-04-16 15:20:36 -07:00
/* Probe for the real time clock chip on Sun4 */
2007-10-27 00:17:01 -07:00
static inline void sun4_clock_probe ( void )
2005-04-16 15:20:36 -07:00
{
# ifdef CONFIG_SUN4
int temp ;
struct resource r ;
memset ( & r , 0 , sizeof ( r ) ) ;
if ( idprom - > id_machtype = = ( SM_SUN4 | SM_4_330 ) ) {
sp_clock_typ = MSTK48T02 ;
r . start = sun4_clock_physaddr ;
mstk48t02_regs = sbus_ioremap ( & r , 0 ,
sizeof ( struct mostek48t02 ) , NULL ) ;
mstk48t08_regs = NULL ; /* To catch weirdness */
intersil_clock = NULL ; /* just in case */
/* Kick start the clock if it is completely stopped. */
if ( mostek_read ( mstk48t02_regs + MOSTEK_SEC ) & MSTK_STOP )
kick_start_clock ( ) ;
} else if ( idprom - > id_machtype = = ( SM_SUN4 | SM_4_260 ) ) {
/* intersil setup code */
printk ( " Clock: INTERSIL at %8x " , sun4_clock_physaddr ) ;
sp_clock_typ = INTERSIL ;
r . start = sun4_clock_physaddr ;
intersil_clock = ( struct intersil * )
sbus_ioremap ( & r , 0 , sizeof ( * intersil_clock ) , " intersil " ) ;
mstk48t02_regs = 0 ; /* just be sure */
mstk48t08_regs = NULL ; /* ditto */
/* initialise the clock */
intersil_intr ( intersil_clock , INTERSIL_INT_100HZ ) ;
intersil_start ( intersil_clock ) ;
intersil_read_intr ( intersil_clock , temp ) ;
while ( ! ( temp & 0x80 ) )
intersil_read_intr ( intersil_clock , temp ) ;
intersil_read_intr ( intersil_clock , temp ) ;
while ( ! ( temp & 0x80 ) )
intersil_read_intr ( intersil_clock , temp ) ;
intersil_stop ( intersil_clock ) ;
}
# endif
}
2006-07-27 22:08:01 -07:00
# ifndef CONFIG_SUN4
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
2006-06-29 14:36:52 -07:00
if ( ! strcmp ( model , " mk48t02 " ) ) {
2005-04-16 15:20:36 -07:00
sp_clock_typ = MSTK48T02 ;
2006-06-29 14:36:52 -07:00
2005-04-16 15:20:36 -07:00
/* Map the clock register io area read-only */
2006-06-29 14:36:52 -07:00
mstk48t02_regs = of_ioremap ( & op - > resource [ 0 ] , 0 ,
sizeof ( struct mostek48t02 ) ,
" mk48t02 " ) ;
2005-04-16 15:20:36 -07:00
mstk48t08_regs = NULL ; /* To catch weirdness */
2006-06-29 14:36:52 -07:00
} else if ( ! strcmp ( model , " mk48t08 " ) ) {
2005-04-16 15:20:36 -07:00
sp_clock_typ = MSTK48T08 ;
2006-06-29 14:36:52 -07:00
mstk48t08_regs = of_ioremap ( & op - > resource [ 0 ] , 0 ,
sizeof ( struct mostek48t08 ) ,
" mk48t08 " ) ;
2005-04-16 15:20:36 -07:00
mstk48t02_regs = & mstk48t08_regs - > regs ;
2006-06-29 14:36:52 -07:00
} else
return - ENODEV ;
2005-04-16 15:20:36 -07:00
/* Report a low battery voltage condition. */
if ( has_low_battery ( ) )
printk ( KERN_CRIT " NVRAM: Low battery voltage! \n " ) ;
/* Kick start the clock if it is completely stopped. */
if ( mostek_read ( mstk48t02_regs + MOSTEK_SEC ) & MSTK_STOP )
kick_start_clock ( ) ;
2006-06-29 14:36:52 -07:00
2006-07-27 22:08:01 -07:00
mostek_set_system_time ( ) ;
2006-06-29 14:36:52 -07:00
return 0 ;
}
static struct of_device_id clock_match [ ] = {
{
. 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 = {
. name = " clock " ,
} ,
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 ) ;
# endif /* !CONFIG_SUN4 */
2005-04-16 15:20:36 -07:00
void __init sbus_time_init ( void )
{
BTFIXUPSET_CALL ( bus_do_settimeofday , sbus_do_settimeofday , BTFIXUPCALL_NORM ) ;
btfixup ( ) ;
if ( ARCH_SUN4 )
sun4_clock_probe ( ) ;
sparc_init_timers ( timer_interrupt ) ;
# ifdef CONFIG_SUN4
if ( idprom - > id_machtype = = ( SM_SUN4 | SM_4_330 ) ) {
2006-07-27 22:08:01 -07:00
mostek_set_system_time ( ) ;
2005-04-16 15:20:36 -07:00
} else if ( idprom - > id_machtype = = ( SM_SUN4 | SM_4_260 ) ) {
/* initialise the intersil on sun4 */
2006-07-27 22:08:01 -07:00
unsigned int year , mon , day , hour , min , sec ;
int temp ;
struct intersil * iregs ;
2005-04-16 15:20:36 -07:00
iregs = intersil_clock ;
if ( ! iregs ) {
prom_printf ( " Something wrong, clock regs not mapped yet. \n " ) ;
prom_halt ( ) ;
}
intersil_intr ( intersil_clock , INTERSIL_INT_100HZ ) ;
disable_pil_irq ( 10 ) ;
intersil_stop ( iregs ) ;
intersil_read_intr ( intersil_clock , temp ) ;
temp = iregs - > clk . int_csec ;
sec = iregs - > clk . int_sec ;
min = iregs - > clk . int_min ;
hour = iregs - > clk . int_hour ;
day = iregs - > clk . int_day ;
mon = iregs - > clk . int_month ;
year = MSTK_CVT_YEAR ( iregs - > clk . int_year ) ;
enable_pil_irq ( 10 ) ;
intersil_start ( iregs ) ;
xtime . tv_sec = mktime ( year , mon , day , hour , min , sec ) ;
xtime . tv_nsec = ( INITIAL_JIFFIES % HZ ) * ( NSEC_PER_SEC / HZ ) ;
set_normalized_timespec ( & wall_to_monotonic ,
- xtime . tv_sec , - xtime . tv_nsec ) ;
printk ( " %u/%u/%u %u:%u:%u \n " , day , mon , year , hour , min , sec ) ;
}
# endif
/* 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 ;
}
/*
* BUG : This routine does not handle hour overflow properly ; it just
* sets the minutes . Usually you won ' t notice until after reboot !
*/
static int set_rtc_mmss ( unsigned long nowtime )
{
int real_seconds , real_minutes , mostek_minutes ;
struct mostek48t02 * regs = ( struct mostek48t02 * ) mstk48t02_regs ;
unsigned long flags ;
# ifdef CONFIG_SUN4
struct intersil * iregs = intersil_clock ;
int temp ;
# endif
/* Not having a register set can lead to trouble. */
if ( ! regs ) {
# ifdef CONFIG_SUN4
if ( ! iregs )
return - 1 ;
else {
temp = iregs - > clk . int_csec ;
mostek_minutes = iregs - > clk . int_min ;
real_seconds = nowtime % 60 ;
real_minutes = nowtime / 60 ;
if ( ( ( abs ( real_minutes - mostek_minutes ) + 15 ) / 30 ) & 1 )
real_minutes + = 30 ; /* correct for half hour time zone */
real_minutes % = 60 ;
if ( abs ( real_minutes - mostek_minutes ) < 30 ) {
intersil_stop ( iregs ) ;
iregs - > clk . int_sec = real_seconds ;
iregs - > clk . int_min = real_minutes ;
intersil_start ( iregs ) ;
} else {
printk ( KERN_WARNING
" set_rtc_mmss: can't update from %d to %d \n " ,
mostek_minutes , real_minutes ) ;
return - 1 ;
}
return 0 ;
}
# endif
}
spin_lock_irqsave ( & mostek_lock , flags ) ;
/* Read the current RTC minutes. */
regs - > creg | = MSTK_CREG_READ ;
mostek_minutes = MSTK_REG_MIN ( regs ) ;
regs - > creg & = ~ MSTK_CREG_READ ;
/*
* 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 - mostek_minutes ) + 15 ) / 30 ) & 1 )
real_minutes + = 30 ; /* correct for half hour time zone */
real_minutes % = 60 ;
if ( abs ( real_minutes - mostek_minutes ) < 30 ) {
regs - > creg | = MSTK_CREG_WRITE ;
MSTK_SET_REG_SEC ( regs , real_seconds ) ;
MSTK_SET_REG_MIN ( regs , real_minutes ) ;
regs - > creg & = ~ MSTK_CREG_WRITE ;
spin_unlock_irqrestore ( & mostek_lock , flags ) ;
return 0 ;
} else {
spin_unlock_irqrestore ( & mostek_lock , flags ) ;
return - 1 ;
}
}