2005-04-17 02:20:36 +04: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 .
*
* Machine dependent access functions for RTC registers .
*/
# ifndef __ASM_MC146818_TIME_H
# define __ASM_MC146818_TIME_H
# include <linux/bcd.h>
# include <linux/mc146818rtc.h>
# include <linux/time.h>
/*
* For check timing call set_rtc_mmss ( ) 500 ms ; used in timer interrupt .
*/
# define USEC_AFTER 500000
# define USEC_BEFORE 500000
/*
* In order to set the CMOS clock precisely , set_rtc_mmss has to be
* called 500 ms after the second nowtime has started , because when
* nowtime is written into the registers of the CMOS clock , it will
* jump to the next second precisely 500 ms later . Check the Motorola
* MC146818A or Dallas DS12887 data sheet for details .
*
* BUG : This routine does not handle hour overflow properly ; it just
* sets the minutes . Usually you ' ll only notice that after reboot !
*/
static inline int mc146818_set_rtc_mmss ( unsigned long nowtime )
{
int real_seconds , real_minutes , cmos_minutes ;
unsigned char save_control , save_freq_select ;
int retval = 0 ;
2005-11-02 19:01:15 +03:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2005-11-02 19:01:15 +03:00
spin_lock_irqsave ( & rtc_lock , flags ) ;
2005-04-17 02:20:36 +04:00
save_control = CMOS_READ ( RTC_CONTROL ) ; /* tell the clock it's being set */
CMOS_WRITE ( ( save_control | RTC_SET ) , RTC_CONTROL ) ;
save_freq_select = CMOS_READ ( RTC_FREQ_SELECT ) ; /* stop and reset prescaler */
CMOS_WRITE ( ( save_freq_select | RTC_DIV_RESET2 ) , RTC_FREQ_SELECT ) ;
cmos_minutes = CMOS_READ ( RTC_MINUTES ) ;
if ( ! ( save_control & RTC_DM_BINARY ) | | RTC_ALWAYS_BCD )
2008-10-19 07:28:44 +04:00
cmos_minutes = bcd2bin ( cmos_minutes ) ;
2005-04-17 02:20:36 +04:00
/*
* 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 ) {
if ( ! ( save_control & RTC_DM_BINARY ) | | RTC_ALWAYS_BCD ) {
2008-10-19 07:28:44 +04:00
real_seconds = bin2bcd ( real_seconds ) ;
real_minutes = bin2bcd ( real_minutes ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-12 02:46:15 +04:00
CMOS_WRITE ( real_seconds , RTC_SECONDS ) ;
CMOS_WRITE ( real_minutes , RTC_MINUTES ) ;
2005-04-17 02:20:36 +04:00
} else {
printk ( KERN_WARNING
" set_rtc_mmss: can't update from %d to %d \n " ,
cmos_minutes , real_minutes ) ;
retval = - 1 ;
}
/* The following flags have to be released exactly in this order,
* otherwise the DS12887 ( popular MC146818A clone with integrated
* battery and quartz ) will not reset the oscillator and will not
* update precisely 500 ms later . You won ' t find this mentioned in
* the Dallas Semiconductor data sheets , but who believes data
* sheets anyway . . . - - Markus Kuhn
*/
CMOS_WRITE ( save_control , RTC_CONTROL ) ;
CMOS_WRITE ( save_freq_select , RTC_FREQ_SELECT ) ;
2005-11-02 19:01:15 +03:00
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
static inline unsigned long mc146818_get_cmos_time ( void )
{
unsigned int year , mon , day , hour , min , sec ;
2005-11-02 19:01:15 +03:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2005-11-02 19:01:15 +03:00
spin_lock_irqsave ( & rtc_lock , flags ) ;
2006-03-28 13:56:05 +04:00
do {
2005-04-17 02:20:36 +04:00
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 ) ;
} while ( sec ! = CMOS_READ ( RTC_SECONDS ) ) ;
if ( ! ( CMOS_READ ( RTC_CONTROL ) & RTC_DM_BINARY ) | | RTC_ALWAYS_BCD ) {
2008-10-19 07:28:44 +04:00
sec = bcd2bin ( sec ) ;
min = bcd2bin ( min ) ;
hour = bcd2bin ( hour ) ;
day = bcd2bin ( day ) ;
mon = bcd2bin ( mon ) ;
year = bcd2bin ( year ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-02 19:01:15 +03:00
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
2005-04-17 02:20:36 +04:00
year = mc146818_decode_year ( year ) ;
return mktime ( year , mon , day , hour , min , sec ) ;
}
# endif /* __ASM_MC146818_TIME_H */