2019-05-19 15:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-06-01 17:46:23 +03:00
# include <linux/bcd.h>
# include <linux/delay.h>
# include <linux/export.h>
# include <linux/mc146818rtc.h>
# ifdef CONFIG_ACPI
# include <linux/acpi.h>
# endif
/*
* Returns true if a clock update is in progress
*/
static inline unsigned char mc146818_is_updating ( void )
{
unsigned char uip ;
unsigned long flags ;
spin_lock_irqsave ( & rtc_lock , flags ) ;
uip = ( CMOS_READ ( RTC_FREQ_SELECT ) & RTC_UIP ) ;
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
return uip ;
}
unsigned int mc146818_get_time ( struct rtc_time * time )
{
unsigned char ctrl ;
unsigned long flags ;
unsigned char century = 0 ;
# ifdef CONFIG_MACH_DECSTATION
unsigned int real_year ;
# endif
/*
* read RTC once any update in progress is done . The update
* can take just over 2 ms . We wait 20 ms . There is no need to
* to poll - wait ( up to 1 s - eeccch ) for the falling edge of RTC_UIP .
* If you need to know * exactly * when a second has started , enable
* periodic update complete interrupts , ( via ioctl ) and then
* immediately read / dev / rtc which will block until you get the IRQ .
* Once the read clears , read the RTC time ( again via ioctl ) . Easy .
*/
if ( mc146818_is_updating ( ) )
mdelay ( 20 ) ;
/*
* Only the values that we read from the RTC are set . We leave
* tm_wday , tm_yday and tm_isdst untouched . Even though the
* RTC has RTC_DAY_OF_WEEK , we ignore it , as it is only updated
* by the RTC when initially set to a non - zero value .
*/
spin_lock_irqsave ( & rtc_lock , flags ) ;
time - > tm_sec = CMOS_READ ( RTC_SECONDS ) ;
time - > tm_min = CMOS_READ ( RTC_MINUTES ) ;
time - > tm_hour = CMOS_READ ( RTC_HOURS ) ;
time - > tm_mday = CMOS_READ ( RTC_DAY_OF_MONTH ) ;
time - > tm_mon = CMOS_READ ( RTC_MONTH ) ;
time - > tm_year = CMOS_READ ( RTC_YEAR ) ;
# ifdef CONFIG_MACH_DECSTATION
real_year = CMOS_READ ( RTC_DEC_YEAR ) ;
# endif
# ifdef CONFIG_ACPI
if ( acpi_gbl_FADT . header . revision > = FADT2_REVISION_ID & &
acpi_gbl_FADT . century )
century = CMOS_READ ( acpi_gbl_FADT . century ) ;
# endif
ctrl = CMOS_READ ( RTC_CONTROL ) ;
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
if ( ! ( ctrl & RTC_DM_BINARY ) | | RTC_ALWAYS_BCD )
{
time - > tm_sec = bcd2bin ( time - > tm_sec ) ;
time - > tm_min = bcd2bin ( time - > tm_min ) ;
time - > tm_hour = bcd2bin ( time - > tm_hour ) ;
time - > tm_mday = bcd2bin ( time - > tm_mday ) ;
time - > tm_mon = bcd2bin ( time - > tm_mon ) ;
time - > tm_year = bcd2bin ( time - > tm_year ) ;
century = bcd2bin ( century ) ;
}
# ifdef CONFIG_MACH_DECSTATION
time - > tm_year + = real_year - 72 ;
# endif
2019-01-06 11:21:03 +03:00
if ( century > 20 )
2016-06-01 17:46:23 +03:00
time - > tm_year + = ( century - 19 ) * 100 ;
/*
* Account for differences between how the RTC uses the values
* and how they are defined in a struct rtc_time ;
*/
if ( time - > tm_year < = 69 )
time - > tm_year + = 100 ;
time - > tm_mon - - ;
return RTC_24H ;
}
EXPORT_SYMBOL_GPL ( mc146818_get_time ) ;
/* Set the current date and time in the real time clock. */
int mc146818_set_time ( struct rtc_time * time )
{
unsigned long flags ;
unsigned char mon , day , hrs , min , sec ;
unsigned char save_control , save_freq_select ;
unsigned int yrs ;
# ifdef CONFIG_MACH_DECSTATION
unsigned int real_yrs , leap_yr ;
# endif
unsigned char century = 0 ;
yrs = time - > tm_year ;
mon = time - > tm_mon + 1 ; /* tm_mon starts at zero */
day = time - > tm_mday ;
hrs = time - > tm_hour ;
min = time - > tm_min ;
sec = time - > tm_sec ;
if ( yrs > 255 ) /* They are unsigned */
return - EINVAL ;
spin_lock_irqsave ( & rtc_lock , flags ) ;
# ifdef CONFIG_MACH_DECSTATION
real_yrs = yrs ;
leap_yr = ( ( ! ( ( yrs + 1900 ) % 4 ) & & ( ( yrs + 1900 ) % 100 ) ) | |
! ( ( yrs + 1900 ) % 400 ) ) ;
yrs = 72 ;
/*
* We want to keep the year set to 73 until March
* for non - leap years , so that Feb , 29 th is handled
* correctly .
*/
if ( ! leap_yr & & mon < 3 ) {
real_yrs - - ;
yrs = 73 ;
}
# endif
# ifdef CONFIG_ACPI
if ( acpi_gbl_FADT . header . revision > = FADT2_REVISION_ID & &
acpi_gbl_FADT . century ) {
century = ( yrs + 1900 ) / 100 ;
yrs % = 100 ;
}
# endif
/* These limits and adjustments are independent of
* whether the chip is in binary mode or not .
*/
if ( yrs > 169 ) {
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
return - EINVAL ;
}
if ( yrs > = 100 )
yrs - = 100 ;
if ( ! ( CMOS_READ ( RTC_CONTROL ) & RTC_DM_BINARY )
| | RTC_ALWAYS_BCD ) {
sec = bin2bcd ( sec ) ;
min = bin2bcd ( min ) ;
hrs = bin2bcd ( hrs ) ;
day = bin2bcd ( day ) ;
mon = bin2bcd ( mon ) ;
yrs = bin2bcd ( yrs ) ;
century = bin2bcd ( century ) ;
}
save_control = CMOS_READ ( RTC_CONTROL ) ;
CMOS_WRITE ( ( save_control | RTC_SET ) , RTC_CONTROL ) ;
save_freq_select = CMOS_READ ( RTC_FREQ_SELECT ) ;
2020-01-04 07:31:10 +03:00
CMOS_WRITE ( ( save_freq_select | RTC_DIV_RESET2 ) , RTC_FREQ_SELECT ) ;
2016-06-01 17:46:23 +03:00
# ifdef CONFIG_MACH_DECSTATION
CMOS_WRITE ( real_yrs , RTC_DEC_YEAR ) ;
# endif
CMOS_WRITE ( yrs , RTC_YEAR ) ;
CMOS_WRITE ( mon , RTC_MONTH ) ;
CMOS_WRITE ( day , RTC_DAY_OF_MONTH ) ;
CMOS_WRITE ( hrs , RTC_HOURS ) ;
CMOS_WRITE ( min , RTC_MINUTES ) ;
CMOS_WRITE ( sec , RTC_SECONDS ) ;
# ifdef CONFIG_ACPI
if ( acpi_gbl_FADT . header . revision > = FADT2_REVISION_ID & &
acpi_gbl_FADT . century )
CMOS_WRITE ( century , acpi_gbl_FADT . century ) ;
# endif
CMOS_WRITE ( save_control , RTC_CONTROL ) ;
CMOS_WRITE ( save_freq_select , RTC_FREQ_SELECT ) ;
spin_unlock_irqrestore ( & rtc_lock , flags ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( mc146818_set_time ) ;