2005-04-16 15:20:36 -07:00
/*
2008-01-30 13:30:26 +01:00
* RTC related functions
2005-04-16 15:20:36 -07:00
*/
2008-01-30 13:30:27 +01:00
# include <linux/acpi.h>
2008-01-30 13:30:26 +01:00
# include <linux/bcd.h>
2005-04-16 15:20:36 -07:00
# include <linux/mc146818rtc.h>
2008-06-12 15:21:54 -07:00
# include <linux/platform_device.h>
# include <linux/pnp.h>
2005-04-16 15:20:36 -07:00
2008-01-30 13:30:26 +01:00
# include <asm/time.h>
2008-01-30 13:32:39 +01:00
# include <asm/vsyscall.h>
2005-04-16 15:20:36 -07:00
2008-01-30 13:30:27 +01:00
# ifdef CONFIG_X86_32
/*
* This is a special lock that is owned by the CPU and holds the index
* register we are working with . It is required for NMI access to the
* CMOS / RTC registers . See include / asm - i386 / mc146818rtc . h for details .
*/
volatile unsigned long cmos_lock = 0 ;
EXPORT_SYMBOL ( cmos_lock ) ;
# endif
2008-02-09 16:16:58 +01:00
/* For two digit years assume time is always after that */
# define CMOS_YEARS_OFFS 2000
2008-01-30 13:30:27 +01:00
DEFINE_SPINLOCK ( rtc_lock ) ;
EXPORT_SYMBOL ( rtc_lock ) ;
2005-04-16 15:20:36 -07:00
/*
* 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 !
*/
2008-01-30 13:30:26 +01:00
int mach_set_rtc_mmss ( unsigned long nowtime )
2005-04-16 15:20:36 -07:00
{
int retval = 0 ;
int real_seconds , real_minutes , cmos_minutes ;
unsigned char save_control , save_freq_select ;
2008-01-30 13:30:27 +01:00
/* tell the clock it's being set */
save_control = CMOS_READ ( RTC_CONTROL ) ;
2005-04-16 15:20:36 -07:00
CMOS_WRITE ( ( save_control | RTC_SET ) , RTC_CONTROL ) ;
2008-01-30 13:30:27 +01:00
/* stop and reset prescaler */
save_freq_select = CMOS_READ ( RTC_FREQ_SELECT ) ;
2005-04-16 15:20:36 -07:00
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 )
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 ;
2008-01-30 13:30:27 +01:00
/* correct for half hour time zone */
2005-04-16 15:20:36 -07:00
if ( ( ( abs ( real_minutes - cmos_minutes ) + 15 ) / 30 ) & 1 )
2008-01-30 13:30:27 +01:00
real_minutes + = 30 ;
2005-04-16 15:20:36 -07:00
real_minutes % = 60 ;
if ( abs ( real_minutes - cmos_minutes ) < 30 ) {
if ( ! ( save_control & RTC_DM_BINARY ) | | RTC_ALWAYS_BCD ) {
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 ;
}
/* 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 ) ;
return retval ;
}
2008-01-30 13:30:26 +01:00
unsigned long mach_get_cmos_time ( void )
2005-04-16 15:20:36 -07:00
{
2008-02-09 16:16:59 +01:00
unsigned int status , year , mon , day , hour , min , sec , century = 0 ;
2008-01-30 13:30:27 +01:00
/*
* If UIP is clear , then we have > = 244 microseconds before
* RTC registers will be updated . Spec sheet says that this
* is the reliable way to read RTC - registers . If UIP is set
* then the register access might be invalid .
*/
while ( ( CMOS_READ ( RTC_FREQ_SELECT ) & RTC_UIP ) )
cpu_relax ( ) ;
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 ) ;
2008-02-09 16:17:01 +01:00
# ifdef CONFIG_ACPI
2008-01-30 13:30:27 +01:00
if ( acpi_gbl_FADT . header . revision > = FADT2_REVISION_ID & &
acpi_gbl_FADT . century )
century = CMOS_READ ( acpi_gbl_FADT . century ) ;
# endif
2008-02-09 16:16:59 +01:00
status = CMOS_READ ( RTC_CONTROL ) ;
2008-02-09 16:17:01 +01:00
WARN_ON_ONCE ( RTC_ALWAYS_BCD & & ( status & RTC_DM_BINARY ) ) ;
2008-02-09 16:16:59 +01:00
if ( RTC_ALWAYS_BCD | | ! ( status & RTC_DM_BINARY ) ) {
2006-03-28 01:56:09 -08:00
BCD_TO_BIN ( sec ) ;
BCD_TO_BIN ( min ) ;
BCD_TO_BIN ( hour ) ;
BCD_TO_BIN ( day ) ;
BCD_TO_BIN ( mon ) ;
BCD_TO_BIN ( year ) ;
}
2008-01-30 13:30:27 +01:00
if ( century ) {
BCD_TO_BIN ( century ) ;
year + = century * 100 ;
printk ( KERN_INFO " Extended CMOS year: %d \n " , century * 100 ) ;
2008-02-09 16:16:58 +01:00
} else
2008-01-30 13:30:27 +01:00
year + = CMOS_YEARS_OFFS ;
2005-04-16 15:20:36 -07:00
return mktime ( year , mon , day , hour , min , sec ) ;
}
2008-01-30 13:30:26 +01:00
/* Routines for accessing the CMOS RAM/RTC. */
unsigned char rtc_cmos_read ( unsigned char addr )
{
unsigned char val ;
lock_cmos_prefix ( addr ) ;
2008-02-17 16:56:39 -05:00
outb ( addr , RTC_PORT ( 0 ) ) ;
val = inb ( RTC_PORT ( 1 ) ) ;
2008-01-30 13:30:26 +01:00
lock_cmos_suffix ( addr ) ;
return val ;
}
EXPORT_SYMBOL ( rtc_cmos_read ) ;
void rtc_cmos_write ( unsigned char val , unsigned char addr )
{
lock_cmos_prefix ( addr ) ;
2008-02-17 16:56:39 -05:00
outb ( addr , RTC_PORT ( 0 ) ) ;
outb ( val , RTC_PORT ( 1 ) ) ;
2008-01-30 13:30:26 +01:00
lock_cmos_suffix ( addr ) ;
}
EXPORT_SYMBOL ( rtc_cmos_write ) ;
static int set_rtc_mmss ( unsigned long nowtime )
{
int retval ;
unsigned long flags ;
spin_lock_irqsave ( & rtc_lock , flags ) ;
retval = set_wallclock ( nowtime ) ;
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
return retval ;
}
/* not static: needed by APM */
unsigned long read_persistent_clock ( void )
{
2008-01-30 13:30:27 +01:00
unsigned long retval , flags ;
2008-01-30 13:30:26 +01:00
spin_lock_irqsave ( & rtc_lock , flags ) ;
retval = get_wallclock ( ) ;
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
return retval ;
}
int update_persistent_clock ( struct timespec now )
{
return set_rtc_mmss ( now . tv_sec ) ;
}
2008-01-30 13:32:39 +01:00
2008-01-30 13:32:40 +01:00
unsigned long long native_read_tsc ( void )
2008-01-30 13:32:39 +01:00
{
2008-01-30 13:32:40 +01:00
return __native_read_tsc ( ) ;
2008-01-30 13:32:39 +01:00
}
2008-01-30 13:32:40 +01:00
EXPORT_SYMBOL ( native_read_tsc ) ;
2008-06-12 15:21:54 -07:00
static struct resource rtc_resources [ ] = {
[ 0 ] = {
. start = RTC_PORT ( 0 ) ,
. end = RTC_PORT ( 1 ) ,
. flags = IORESOURCE_IO ,
} ,
[ 1 ] = {
. start = RTC_IRQ ,
. end = RTC_IRQ ,
. flags = IORESOURCE_IRQ ,
}
} ;
static struct platform_device rtc_device = {
. name = " rtc_cmos " ,
. id = - 1 ,
. resource = rtc_resources ,
. num_resources = ARRAY_SIZE ( rtc_resources ) ,
} ;
static __init int add_rtc_cmos ( void )
{
# ifdef CONFIG_PNP
if ( ! pnp_platform_devices )
platform_device_register ( & rtc_device ) ;
# else
platform_device_register ( & rtc_device ) ;
# endif /* CONFIG_PNP */
return 0 ;
}
device_initcall ( add_rtc_cmos ) ;