2008-03-05 01:28:25 +03:00
/*
* Seiko Instruments S - 35390 A RTC Driver
*
* Copyright ( c ) 2007 Byron Bradley
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/module.h>
# include <linux/rtc.h>
# include <linux/i2c.h>
# include <linux/bitrev.h>
# include <linux/bcd.h>
# include <linux/slab.h>
2016-07-02 18:28:09 +03:00
# include <linux/delay.h>
2008-03-05 01:28:25 +03:00
# define S35390A_CMD_STATUS1 0
# define S35390A_CMD_STATUS2 1
# define S35390A_CMD_TIME1 2
2012-10-05 04:14:37 +04:00
# define S35390A_CMD_TIME2 3
# define S35390A_CMD_INT2_REG1 5
2008-03-05 01:28:25 +03:00
# define S35390A_BYTE_YEAR 0
# define S35390A_BYTE_MONTH 1
# define S35390A_BYTE_DAY 2
# define S35390A_BYTE_WDAY 3
# define S35390A_BYTE_HOURS 4
# define S35390A_BYTE_MINS 5
# define S35390A_BYTE_SECS 6
2012-10-05 04:14:37 +04:00
# define S35390A_ALRM_BYTE_WDAY 0
# define S35390A_ALRM_BYTE_HOURS 1
# define S35390A_ALRM_BYTE_MINS 2
2016-07-02 18:28:10 +03:00
/* flags for STATUS1 */
2008-03-05 01:28:25 +03:00
# define S35390A_FLAG_POC 0x01
# define S35390A_FLAG_BLD 0x02
2016-07-02 18:28:10 +03:00
# define S35390A_FLAG_INT2 0x04
2008-03-05 01:28:25 +03:00
# define S35390A_FLAG_24H 0x40
# define S35390A_FLAG_RESET 0x80
2016-07-02 18:28:10 +03:00
/* flag for STATUS2 */
2008-03-05 01:28:25 +03:00
# define S35390A_FLAG_TEST 0x01
2012-10-05 04:14:37 +04:00
# define S35390A_INT2_MODE_MASK 0xF0
# define S35390A_INT2_MODE_NOINTR 0x00
# define S35390A_INT2_MODE_FREQ 0x10
# define S35390A_INT2_MODE_ALARM 0x40
# define S35390A_INT2_MODE_PMIN_EDG 0x20
2008-04-30 01:11:40 +04:00
static const struct i2c_device_id s35390a_id [ ] = {
{ " s35390a " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , s35390a_id ) ;
2008-03-05 01:28:25 +03:00
struct s35390a {
struct i2c_client * client [ 8 ] ;
struct rtc_device * rtc ;
int twentyfourhour ;
} ;
static int s35390a_set_reg ( struct s35390a * s35390a , int reg , char * buf , int len )
{
struct i2c_client * client = s35390a - > client [ reg ] ;
struct i2c_msg msg [ ] = {
2012-10-05 04:14:21 +04:00
{
. addr = client - > addr ,
. len = len ,
. buf = buf
} ,
2008-03-05 01:28:25 +03:00
} ;
if ( ( i2c_transfer ( client - > adapter , msg , 1 ) ) ! = 1 )
return - EIO ;
return 0 ;
}
static int s35390a_get_reg ( struct s35390a * s35390a , int reg , char * buf , int len )
{
struct i2c_client * client = s35390a - > client [ reg ] ;
struct i2c_msg msg [ ] = {
2012-10-05 04:14:21 +04:00
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = len ,
. buf = buf
} ,
2008-03-05 01:28:25 +03:00
} ;
if ( ( i2c_transfer ( client - > adapter , msg , 1 ) ) ! = 1 )
return - EIO ;
return 0 ;
}
2016-07-02 18:28:09 +03:00
/*
* Returns < 0 on error , 0 if rtc is setup fine and 1 if the chip was reset .
* To keep the information if an irq is pending , pass the value read from
* STATUS1 to the caller .
*/
static int s35390a_reset ( struct s35390a * s35390a , char * status1 )
2008-03-05 01:28:25 +03:00
{
2016-07-02 18:28:09 +03:00
char buf ;
int ret ;
unsigned initcount = 0 ;
2008-03-05 01:28:25 +03:00
2016-07-02 18:28:09 +03:00
ret = s35390a_get_reg ( s35390a , S35390A_CMD_STATUS1 , status1 , 1 ) ;
if ( ret < 0 )
return ret ;
2008-03-05 01:28:25 +03:00
2016-07-02 18:28:09 +03:00
if ( * status1 & S35390A_FLAG_POC )
/*
* Do not communicate for 0.5 seconds since the power - on
* detection circuit is in operation .
*/
msleep ( 500 ) ;
else if ( ! ( * status1 & S35390A_FLAG_BLD ) )
/*
* If both POC and BLD are unset everything is fine .
*/
2008-03-05 01:28:25 +03:00
return 0 ;
2016-07-02 18:28:09 +03:00
/*
* At least one of POC and BLD are set , so reinitialise chip . Keeping
* this information in the hardware to know later that the time isn ' t
* valid is unfortunately not possible because POC and BLD are cleared
* on read . So the reset is best done now .
*
* The 24 H bit is kept over reset , so set it already here .
*/
initialize :
* status1 = S35390A_FLAG_24H ;
buf = S35390A_FLAG_RESET | S35390A_FLAG_24H ;
ret = s35390a_set_reg ( s35390a , S35390A_CMD_STATUS1 , & buf , 1 ) ;
if ( ret < 0 )
return ret ;
ret = s35390a_get_reg ( s35390a , S35390A_CMD_STATUS1 , & buf , 1 ) ;
if ( ret < 0 )
return ret ;
if ( buf & ( S35390A_FLAG_POC | S35390A_FLAG_BLD ) ) {
/* Try up to five times to reset the chip */
if ( initcount < 5 ) {
+ + initcount ;
goto initialize ;
} else
return - EIO ;
}
return 1 ;
2008-03-05 01:28:25 +03:00
}
static int s35390a_disable_test_mode ( struct s35390a * s35390a )
{
char buf [ 1 ] ;
if ( s35390a_get_reg ( s35390a , S35390A_CMD_STATUS2 , buf , sizeof ( buf ) ) < 0 )
return - EIO ;
if ( ! ( buf [ 0 ] & S35390A_FLAG_TEST ) )
return 0 ;
buf [ 0 ] & = ~ S35390A_FLAG_TEST ;
return s35390a_set_reg ( s35390a , S35390A_CMD_STATUS2 , buf , sizeof ( buf ) ) ;
}
static char s35390a_hr2reg ( struct s35390a * s35390a , int hour )
{
if ( s35390a - > twentyfourhour )
2008-10-19 07:28:41 +04:00
return bin2bcd ( hour ) ;
2008-03-05 01:28:25 +03:00
if ( hour < 12 )
2008-10-19 07:28:41 +04:00
return bin2bcd ( hour ) ;
2008-03-05 01:28:25 +03:00
2008-10-19 07:28:41 +04:00
return 0x40 | bin2bcd ( hour - 12 ) ;
2008-03-05 01:28:25 +03:00
}
static int s35390a_reg2hr ( struct s35390a * s35390a , char reg )
{
unsigned hour ;
if ( s35390a - > twentyfourhour )
2008-10-19 07:28:41 +04:00
return bcd2bin ( reg & 0x3f ) ;
2008-03-05 01:28:25 +03:00
2008-10-19 07:28:41 +04:00
hour = bcd2bin ( reg & 0x3f ) ;
2008-03-05 01:28:25 +03:00
if ( reg & 0x40 )
hour + = 12 ;
return hour ;
}
static int s35390a_set_datetime ( struct i2c_client * client , struct rtc_time * tm )
{
struct s35390a * s35390a = i2c_get_clientdata ( client ) ;
int i , err ;
char buf [ 7 ] ;
dev_dbg ( & client - > dev , " %s: tm is secs=%d, mins=%d, hours=%d mday=%d, "
" mon=%d, year=%d, wday=%d \n " , __func__ , tm - > tm_sec ,
tm - > tm_min , tm - > tm_hour , tm - > tm_mday , tm - > tm_mon , tm - > tm_year ,
tm - > tm_wday ) ;
2008-10-19 07:28:41 +04:00
buf [ S35390A_BYTE_YEAR ] = bin2bcd ( tm - > tm_year - 100 ) ;
buf [ S35390A_BYTE_MONTH ] = bin2bcd ( tm - > tm_mon + 1 ) ;
buf [ S35390A_BYTE_DAY ] = bin2bcd ( tm - > tm_mday ) ;
buf [ S35390A_BYTE_WDAY ] = bin2bcd ( tm - > tm_wday ) ;
2008-03-05 01:28:25 +03:00
buf [ S35390A_BYTE_HOURS ] = s35390a_hr2reg ( s35390a , tm - > tm_hour ) ;
2008-10-19 07:28:41 +04:00
buf [ S35390A_BYTE_MINS ] = bin2bcd ( tm - > tm_min ) ;
buf [ S35390A_BYTE_SECS ] = bin2bcd ( tm - > tm_sec ) ;
2008-03-05 01:28:25 +03:00
/* This chip expects the bits of each byte to be in reverse order */
for ( i = 0 ; i < 7 ; + + i )
buf [ i ] = bitrev8 ( buf [ i ] ) ;
err = s35390a_set_reg ( s35390a , S35390A_CMD_TIME1 , buf , sizeof ( buf ) ) ;
return err ;
}
static int s35390a_get_datetime ( struct i2c_client * client , struct rtc_time * tm )
{
struct s35390a * s35390a = i2c_get_clientdata ( client ) ;
char buf [ 7 ] ;
int i , err ;
err = s35390a_get_reg ( s35390a , S35390A_CMD_TIME1 , buf , sizeof ( buf ) ) ;
if ( err < 0 )
return err ;
/* This chip returns the bits of each byte in reverse order */
for ( i = 0 ; i < 7 ; + + i )
buf [ i ] = bitrev8 ( buf [ i ] ) ;
2008-10-19 07:28:41 +04:00
tm - > tm_sec = bcd2bin ( buf [ S35390A_BYTE_SECS ] ) ;
tm - > tm_min = bcd2bin ( buf [ S35390A_BYTE_MINS ] ) ;
2008-03-05 01:28:25 +03:00
tm - > tm_hour = s35390a_reg2hr ( s35390a , buf [ S35390A_BYTE_HOURS ] ) ;
2008-10-19 07:28:41 +04:00
tm - > tm_wday = bcd2bin ( buf [ S35390A_BYTE_WDAY ] ) ;
tm - > tm_mday = bcd2bin ( buf [ S35390A_BYTE_DAY ] ) ;
tm - > tm_mon = bcd2bin ( buf [ S35390A_BYTE_MONTH ] ) - 1 ;
tm - > tm_year = bcd2bin ( buf [ S35390A_BYTE_YEAR ] ) + 100 ;
2008-03-05 01:28:25 +03:00
dev_dbg ( & client - > dev , " %s: tm is secs=%d, mins=%d, hours=%d, mday=%d, "
" mon=%d, year=%d, wday=%d \n " , __func__ , tm - > tm_sec ,
tm - > tm_min , tm - > tm_hour , tm - > tm_mday , tm - > tm_mon , tm - > tm_year ,
tm - > tm_wday ) ;
return rtc_valid_tm ( tm ) ;
}
2012-10-05 04:14:37 +04:00
static int s35390a_set_alarm ( struct i2c_client * client , struct rtc_wkalrm * alm )
{
struct s35390a * s35390a = i2c_get_clientdata ( client ) ;
char buf [ 3 ] , sts = 0 ;
int err , i ;
dev_dbg ( & client - > dev , " %s: alm is secs=%d, mins=%d, hours=%d mday=%d, " \
" mon=%d, year=%d, wday=%d \n " , __func__ , alm - > time . tm_sec ,
alm - > time . tm_min , alm - > time . tm_hour , alm - > time . tm_mday ,
alm - > time . tm_mon , alm - > time . tm_year , alm - > time . tm_wday ) ;
/* disable interrupt */
err = s35390a_set_reg ( s35390a , S35390A_CMD_STATUS2 , & sts , sizeof ( sts ) ) ;
if ( err < 0 )
return err ;
/* clear pending interrupt, if any */
err = s35390a_get_reg ( s35390a , S35390A_CMD_STATUS1 , & sts , sizeof ( sts ) ) ;
if ( err < 0 )
return err ;
if ( alm - > enabled )
sts = S35390A_INT2_MODE_ALARM ;
else
sts = S35390A_INT2_MODE_NOINTR ;
/* This chip expects the bits of each byte to be in reverse order */
sts = bitrev8 ( sts ) ;
/* set interupt mode*/
err = s35390a_set_reg ( s35390a , S35390A_CMD_STATUS2 , & sts , sizeof ( sts ) ) ;
if ( err < 0 )
return err ;
if ( alm - > time . tm_wday ! = - 1 )
buf [ S35390A_ALRM_BYTE_WDAY ] = bin2bcd ( alm - > time . tm_wday ) | 0x80 ;
2016-07-02 18:28:08 +03:00
else
buf [ S35390A_ALRM_BYTE_WDAY ] = 0 ;
2012-10-05 04:14:37 +04:00
buf [ S35390A_ALRM_BYTE_HOURS ] = s35390a_hr2reg ( s35390a ,
alm - > time . tm_hour ) | 0x80 ;
buf [ S35390A_ALRM_BYTE_MINS ] = bin2bcd ( alm - > time . tm_min ) | 0x80 ;
if ( alm - > time . tm_hour > = 12 )
buf [ S35390A_ALRM_BYTE_HOURS ] | = 0x40 ;
for ( i = 0 ; i < 3 ; + + i )
buf [ i ] = bitrev8 ( buf [ i ] ) ;
err = s35390a_set_reg ( s35390a , S35390A_CMD_INT2_REG1 , buf ,
sizeof ( buf ) ) ;
return err ;
}
static int s35390a_read_alarm ( struct i2c_client * client , struct rtc_wkalrm * alm )
{
struct s35390a * s35390a = i2c_get_clientdata ( client ) ;
char buf [ 3 ] , sts ;
int i , err ;
err = s35390a_get_reg ( s35390a , S35390A_CMD_STATUS2 , & sts , sizeof ( sts ) ) ;
if ( err < 0 )
return err ;
2016-07-02 18:28:08 +03:00
if ( ( bitrev8 ( sts ) & S35390A_INT2_MODE_MASK ) ! = S35390A_INT2_MODE_ALARM ) {
/*
* When the alarm isn ' t enabled , the register to configure
* the alarm time isn ' t accessible .
*/
alm - > enabled = 0 ;
return 0 ;
} else {
alm - > enabled = 1 ;
}
2012-10-05 04:14:37 +04:00
err = s35390a_get_reg ( s35390a , S35390A_CMD_INT2_REG1 , buf , sizeof ( buf ) ) ;
if ( err < 0 )
return err ;
/* This chip returns the bits of each byte in reverse order */
2016-07-02 18:28:08 +03:00
for ( i = 0 ; i < 3 ; + + i )
2012-10-05 04:14:37 +04:00
buf [ i ] = bitrev8 ( buf [ i ] ) ;
2016-07-02 18:28:08 +03:00
/*
* B0 of the three matching registers is an enable flag . Iff it is set
* the configured value is used for matching .
*/
if ( buf [ S35390A_ALRM_BYTE_WDAY ] & 0x80 )
alm - > time . tm_wday =
bcd2bin ( buf [ S35390A_ALRM_BYTE_WDAY ] & ~ 0x80 ) ;
if ( buf [ S35390A_ALRM_BYTE_HOURS ] & 0x80 )
alm - > time . tm_hour =
s35390a_reg2hr ( s35390a ,
buf [ S35390A_ALRM_BYTE_HOURS ] & ~ 0x80 ) ;
if ( buf [ S35390A_ALRM_BYTE_MINS ] & 0x80 )
alm - > time . tm_min = bcd2bin ( buf [ S35390A_ALRM_BYTE_MINS ] & ~ 0x80 ) ;
/* alarm triggers always at s=0 */
alm - > time . tm_sec = 0 ;
2012-10-05 04:14:37 +04:00
dev_dbg ( & client - > dev , " %s: alm is mins=%d, hours=%d, wday=%d \n " ,
__func__ , alm - > time . tm_min , alm - > time . tm_hour ,
alm - > time . tm_wday ) ;
return 0 ;
}
static int s35390a_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
return s35390a_read_alarm ( to_i2c_client ( dev ) , alm ) ;
}
static int s35390a_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
return s35390a_set_alarm ( to_i2c_client ( dev ) , alm ) ;
}
2008-03-05 01:28:25 +03:00
static int s35390a_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
return s35390a_get_datetime ( to_i2c_client ( dev ) , tm ) ;
}
static int s35390a_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
return s35390a_set_datetime ( to_i2c_client ( dev ) , tm ) ;
}
static const struct rtc_class_ops s35390a_rtc_ops = {
. read_time = s35390a_rtc_read_time ,
. set_time = s35390a_rtc_set_time ,
2012-10-05 04:14:37 +04:00
. set_alarm = s35390a_rtc_set_alarm ,
. read_alarm = s35390a_rtc_read_alarm ,
2008-03-05 01:28:25 +03:00
} ;
static struct i2c_driver s35390a_driver ;
2008-04-30 01:11:39 +04:00
static int s35390a_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2008-03-05 01:28:25 +03:00
{
2016-07-02 18:28:10 +03:00
int err , err_reset ;
2008-03-05 01:28:25 +03:00
unsigned int i ;
struct s35390a * s35390a ;
struct rtc_time tm ;
2016-07-02 18:28:10 +03:00
char buf , status1 ;
2008-03-05 01:28:25 +03:00
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
err = - ENODEV ;
goto exit ;
}
2013-04-30 03:20:53 +04:00
s35390a = devm_kzalloc ( & client - > dev , sizeof ( struct s35390a ) ,
GFP_KERNEL ) ;
2008-03-05 01:28:25 +03:00
if ( ! s35390a ) {
err = - ENOMEM ;
goto exit ;
}
s35390a - > client [ 0 ] = client ;
i2c_set_clientdata ( client , s35390a ) ;
/* This chip uses multiple addresses, use dummy devices for them */
for ( i = 1 ; i < 8 ; + + i ) {
s35390a - > client [ i ] = i2c_new_dummy ( client - > adapter ,
2008-05-11 22:37:06 +04:00
client - > addr + i ) ;
2008-03-05 01:28:25 +03:00
if ( ! s35390a - > client [ i ] ) {
dev_err ( & client - > dev , " Address %02x unavailable \n " ,
client - > addr + i ) ;
err = - EBUSY ;
goto exit_dummy ;
}
}
2016-07-02 18:28:10 +03:00
err_reset = s35390a_reset ( s35390a , & status1 ) ;
if ( err_reset < 0 ) {
err = err_reset ;
2008-03-05 01:28:25 +03:00
dev_err ( & client - > dev , " error resetting chip \n " ) ;
goto exit_dummy ;
}
2016-07-02 18:28:10 +03:00
if ( status1 & S35390A_FLAG_24H )
2008-03-05 01:28:25 +03:00
s35390a - > twentyfourhour = 1 ;
else
s35390a - > twentyfourhour = 0 ;
2016-07-02 18:28:10 +03:00
if ( status1 & S35390A_FLAG_INT2 ) {
/* disable alarm (and maybe test mode) */
buf = 0 ;
err = s35390a_set_reg ( s35390a , S35390A_CMD_STATUS2 , & buf , 1 ) ;
if ( err < 0 ) {
dev_err ( & client - > dev , " error disabling alarm " ) ;
goto exit_dummy ;
}
} else {
err = s35390a_disable_test_mode ( s35390a ) ;
if ( err < 0 ) {
dev_err ( & client - > dev , " error disabling test mode \n " ) ;
goto exit_dummy ;
}
}
if ( err_reset > 0 | | s35390a_get_datetime ( client , & tm ) < 0 )
2008-03-05 01:28:25 +03:00
dev_warn ( & client - > dev , " clock needs to be set \n " ) ;
2012-10-05 04:14:37 +04:00
device_set_wakeup_capable ( & client - > dev , 1 ) ;
2013-04-30 03:20:53 +04:00
s35390a - > rtc = devm_rtc_device_register ( & client - > dev ,
s35390a_driver . driver . name ,
& s35390a_rtc_ops , THIS_MODULE ) ;
2008-03-05 01:28:25 +03:00
if ( IS_ERR ( s35390a - > rtc ) ) {
err = PTR_ERR ( s35390a - > rtc ) ;
goto exit_dummy ;
}
2016-07-02 18:28:10 +03:00
if ( status1 & S35390A_FLAG_INT2 )
rtc_update_irq ( s35390a - > rtc , 1 , RTC_AF ) ;
2008-03-05 01:28:25 +03:00
return 0 ;
exit_dummy :
for ( i = 1 ; i < 8 ; + + i )
if ( s35390a - > client [ i ] )
i2c_unregister_device ( s35390a - > client [ i ] ) ;
exit :
return err ;
}
static int s35390a_remove ( struct i2c_client * client )
{
unsigned int i ;
struct s35390a * s35390a = i2c_get_clientdata ( client ) ;
2013-04-30 03:20:53 +04:00
2008-03-05 01:28:25 +03:00
for ( i = 1 ; i < 8 ; + + i )
if ( s35390a - > client [ i ] )
i2c_unregister_device ( s35390a - > client [ i ] ) ;
return 0 ;
}
static struct i2c_driver s35390a_driver = {
. driver = {
. name = " rtc-s35390a " ,
} ,
. probe = s35390a_probe ,
. remove = s35390a_remove ,
2008-04-30 01:11:40 +04:00
. id_table = s35390a_id ,
2008-03-05 01:28:25 +03:00
} ;
2012-03-24 02:02:31 +04:00
module_i2c_driver ( s35390a_driver ) ;
2008-03-05 01:28:25 +03:00
MODULE_AUTHOR ( " Byron Bradley <byron.bbradley@gmail.com> " ) ;
MODULE_DESCRIPTION ( " S35390A RTC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;