2006-07-14 11:24:11 +04:00
/*
* Intersil ISL1208 rtc class driver
*
* Copyright 2005 , 2006 Hebert Valerio Riedel < hvr @ gnu . org >
*
* 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/i2c.h>
# include <linux/bcd.h>
# include <linux/rtc.h>
2018-07-24 14:31:21 +03:00
# include "rtc-core.h"
2018-07-24 14:31:21 +03:00
# include <linux/of_irq.h>
2006-07-14 11:24:11 +04:00
/* Register map */
/* rtc section */
# define ISL1208_REG_SC 0x00
# define ISL1208_REG_MN 0x01
# define ISL1208_REG_HR 0x02
2008-04-28 13:11:53 +04:00
# define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */
# define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */
2006-07-14 11:24:11 +04:00
# define ISL1208_REG_DT 0x03
# define ISL1208_REG_MO 0x04
# define ISL1208_REG_YR 0x05
# define ISL1208_REG_DW 0x06
# define ISL1208_RTC_SECTION_LEN 7
/* control/status section */
# define ISL1208_REG_SR 0x07
2008-04-28 13:11:53 +04:00
# define ISL1208_REG_SR_ARST (1<<7) /* auto reset */
# define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */
# define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */
2018-07-24 14:31:21 +03:00
# define ISL1208_REG_SR_EVT (1<<3) /* event */
2008-04-28 13:11:53 +04:00
# define ISL1208_REG_SR_ALM (1<<2) /* alarm */
# define ISL1208_REG_SR_BAT (1<<1) /* battery */
# define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */
2006-07-14 11:24:11 +04:00
# define ISL1208_REG_INT 0x08
2011-03-23 02:34:53 +03:00
# define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */
# define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */
2018-07-24 14:31:21 +03:00
# define ISL1219_REG_EV 0x09
# define ISL1219_REG_EV_EVEN (1<<4) /* event detection enable */
2006-07-14 11:24:11 +04:00
# define ISL1208_REG_ATR 0x0a
# define ISL1208_REG_DTR 0x0b
/* alarm section */
# define ISL1208_REG_SCA 0x0c
# define ISL1208_REG_MNA 0x0d
# define ISL1208_REG_HRA 0x0e
# define ISL1208_REG_DTA 0x0f
# define ISL1208_REG_MOA 0x10
# define ISL1208_REG_DWA 0x11
# define ISL1208_ALARM_SECTION_LEN 6
/* user section */
# define ISL1208_REG_USR1 0x12
# define ISL1208_REG_USR2 0x13
# define ISL1208_USR_SECTION_LEN 2
2018-07-24 14:31:21 +03:00
/* event section */
# define ISL1219_REG_SCT 0x14
# define ISL1219_REG_MNT 0x15
# define ISL1219_REG_HRT 0x16
# define ISL1219_REG_DTT 0x17
# define ISL1219_REG_MOT 0x18
# define ISL1219_REG_YRT 0x19
# define ISL1219_EVT_SECTION_LEN 6
2008-04-28 13:11:53 +04:00
static struct i2c_driver isl1208_driver ;
2006-07-14 11:24:11 +04:00
2018-07-24 14:31:21 +03:00
/* ISL1208 various variants */
enum {
TYPE_ISL1208 = 0 ,
TYPE_ISL1218 ,
TYPE_ISL1219 ,
} ;
2006-07-14 11:24:11 +04:00
/* block read */
static int
isl1208_i2c_read_regs ( struct i2c_client * client , u8 reg , u8 buf [ ] ,
2008-04-28 13:11:53 +04:00
unsigned len )
2006-07-14 11:24:11 +04:00
{
u8 reg_addr [ 1 ] = { reg } ;
struct i2c_msg msgs [ 2 ] = {
2012-10-05 04:14:27 +04:00
{
. addr = client - > addr ,
. len = sizeof ( reg_addr ) ,
. buf = reg_addr
} ,
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = len ,
. buf = buf
}
2006-07-14 11:24:11 +04:00
} ;
int ret ;
2018-07-24 14:31:21 +03:00
WARN_ON ( reg > ISL1219_REG_YRT ) ;
WARN_ON ( reg + len > ISL1219_REG_YRT + 1 ) ;
2006-07-14 11:24:11 +04:00
ret = i2c_transfer ( client - > adapter , msgs , 2 ) ;
if ( ret > 0 )
ret = 0 ;
return ret ;
}
/* block write */
static int
isl1208_i2c_set_regs ( struct i2c_client * client , u8 reg , u8 const buf [ ] ,
2008-04-28 13:11:53 +04:00
unsigned len )
2006-07-14 11:24:11 +04:00
{
u8 i2c_buf [ ISL1208_REG_USR2 + 2 ] ;
struct i2c_msg msgs [ 1 ] = {
2012-10-05 04:14:27 +04:00
{
. addr = client - > addr ,
. len = len + 1 ,
. buf = i2c_buf
}
2006-07-14 11:24:11 +04:00
} ;
int ret ;
2018-07-24 14:31:21 +03:00
WARN_ON ( reg > ISL1219_REG_YRT ) ;
WARN_ON ( reg + len > ISL1219_REG_YRT + 1 ) ;
2006-07-14 11:24:11 +04:00
i2c_buf [ 0 ] = reg ;
memcpy ( & i2c_buf [ 1 ] , & buf [ 0 ] , len ) ;
ret = i2c_transfer ( client - > adapter , msgs , 1 ) ;
if ( ret > 0 )
ret = 0 ;
return ret ;
}
2012-09-20 05:48:00 +04:00
/* simple check to see whether we have a isl1208 */
2008-04-28 13:11:53 +04:00
static int
isl1208_i2c_validate_client ( struct i2c_client * client )
2006-07-14 11:24:11 +04:00
{
u8 regs [ ISL1208_RTC_SECTION_LEN ] = { 0 , } ;
u8 zero_mask [ ISL1208_RTC_SECTION_LEN ] = {
0x80 , 0x80 , 0x40 , 0xc0 , 0xe0 , 0x00 , 0xf8
} ;
int i ;
int ret ;
ret = isl1208_i2c_read_regs ( client , 0 , regs , ISL1208_RTC_SECTION_LEN ) ;
if ( ret < 0 )
return ret ;
for ( i = 0 ; i < ISL1208_RTC_SECTION_LEN ; + + i ) {
2008-04-28 13:11:53 +04:00
if ( regs [ i ] & zero_mask [ i ] ) /* check if bits are cleared */
2006-07-14 11:24:11 +04:00
return - ENODEV ;
}
return 0 ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_i2c_get_sr ( struct i2c_client * client )
2006-07-14 11:24:11 +04:00
{
2013-11-13 03:10:26 +04:00
return i2c_smbus_read_byte_data ( client , ISL1208_REG_SR ) ;
2006-07-14 11:24:11 +04:00
}
2008-04-28 13:11:53 +04:00
static int
isl1208_i2c_get_atr ( struct i2c_client * client )
2006-07-14 11:24:11 +04:00
{
int atr = i2c_smbus_read_byte_data ( client , ISL1208_REG_ATR ) ;
if ( atr < 0 )
2008-04-28 13:11:53 +04:00
return atr ;
2006-07-14 11:24:11 +04:00
/* The 6bit value in the ATR register controls the load
* capacitance C_load * in steps of 0.25 pF
*
* bit ( 1 < < 5 ) of the ATR register is inverted
*
* C_load ( ATR = 0x20 ) = 4.50 pF
* C_load ( ATR = 0x00 ) = 12.50 pF
* C_load ( ATR = 0x1f ) = 20.25 pF
*
*/
2008-04-28 13:11:53 +04:00
atr & = 0x3f ; /* mask out lsb */
atr ^ = 1 < < 5 ; /* invert 6th bit */
atr + = 2 * 9 ; /* add offset of 4.5pF; unit[atr] = 0.25pF */
2006-07-14 11:24:11 +04:00
return atr ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_i2c_get_dtr ( struct i2c_client * client )
2006-07-14 11:24:11 +04:00
{
int dtr = i2c_smbus_read_byte_data ( client , ISL1208_REG_DTR ) ;
if ( dtr < 0 )
return - EIO ;
/* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */
2008-04-28 13:11:53 +04:00
dtr = ( ( dtr & 0x3 ) * 20 ) * ( dtr & ( 1 < < 2 ) ? - 1 : 1 ) ;
2006-07-14 11:24:11 +04:00
return dtr ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_i2c_get_usr ( struct i2c_client * client )
2006-07-14 11:24:11 +04:00
{
u8 buf [ ISL1208_USR_SECTION_LEN ] = { 0 , } ;
int ret ;
2008-04-28 13:11:53 +04:00
ret = isl1208_i2c_read_regs ( client , ISL1208_REG_USR1 , buf ,
ISL1208_USR_SECTION_LEN ) ;
2006-07-14 11:24:11 +04:00
if ( ret < 0 )
return ret ;
return ( buf [ 1 ] < < 8 ) | buf [ 0 ] ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_i2c_set_usr ( struct i2c_client * client , u16 usr )
2006-07-14 11:24:11 +04:00
{
u8 buf [ ISL1208_USR_SECTION_LEN ] ;
buf [ 0 ] = usr & 0xff ;
buf [ 1 ] = ( usr > > 8 ) & 0xff ;
2008-04-28 13:11:53 +04:00
return isl1208_i2c_set_regs ( client , ISL1208_REG_USR1 , buf ,
ISL1208_USR_SECTION_LEN ) ;
2006-07-14 11:24:11 +04:00
}
2011-03-23 02:34:53 +03:00
static int
isl1208_rtc_toggle_alarm ( struct i2c_client * client , int enable )
{
int icr = i2c_smbus_read_byte_data ( client , ISL1208_REG_INT ) ;
if ( icr < 0 ) {
dev_err ( & client - > dev , " %s: reading INT failed \n " , __func__ ) ;
return icr ;
}
if ( enable )
icr | = ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM ;
else
icr & = ~ ( ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM ) ;
icr = i2c_smbus_write_byte_data ( client , ISL1208_REG_INT , icr ) ;
if ( icr < 0 ) {
dev_err ( & client - > dev , " %s: writing INT failed \n " , __func__ ) ;
return icr ;
}
return 0 ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_rtc_proc ( struct device * dev , struct seq_file * seq )
2006-07-14 11:24:11 +04:00
{
struct i2c_client * const client = to_i2c_client ( dev ) ;
int sr , dtr , atr , usr ;
sr = isl1208_i2c_get_sr ( client ) ;
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: reading SR failed \n " , __func__ ) ;
return sr ;
}
seq_printf ( seq , " status_reg \t :%s%s%s%s%s%s (0x%.2x) \n " ,
( sr & ISL1208_REG_SR_RTCF ) ? " RTCF " : " " ,
( sr & ISL1208_REG_SR_BAT ) ? " BAT " : " " ,
( sr & ISL1208_REG_SR_ALM ) ? " ALM " : " " ,
( sr & ISL1208_REG_SR_WRTC ) ? " WRTC " : " " ,
( sr & ISL1208_REG_SR_XTOSCB ) ? " XTOSCB " : " " ,
2008-04-28 13:11:53 +04:00
( sr & ISL1208_REG_SR_ARST ) ? " ARST " : " " , sr ) ;
2006-07-14 11:24:11 +04:00
seq_printf ( seq , " batt_status \t : %s \n " ,
( sr & ISL1208_REG_SR_RTCF ) ? " bad " : " okay " ) ;
dtr = isl1208_i2c_get_dtr ( client ) ;
2008-04-28 13:11:53 +04:00
if ( dtr > = 0 - 1 )
2006-07-14 11:24:11 +04:00
seq_printf ( seq , " digital_trim \t : %d ppm \n " , dtr ) ;
atr = isl1208_i2c_get_atr ( client ) ;
if ( atr > = 0 )
seq_printf ( seq , " analog_trim \t : %d.%.2d pF \n " ,
2008-04-28 13:11:53 +04:00
atr > > 2 , ( atr & 0x3 ) * 25 ) ;
2006-07-14 11:24:11 +04:00
usr = isl1208_i2c_get_usr ( client ) ;
if ( usr > = 0 )
seq_printf ( seq , " user_data \t : 0x%.4x \n " , usr ) ;
return 0 ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_i2c_read_time ( struct i2c_client * client , struct rtc_time * tm )
2006-07-14 11:24:11 +04:00
{
int sr ;
u8 regs [ ISL1208_RTC_SECTION_LEN ] = { 0 , } ;
sr = isl1208_i2c_get_sr ( client ) ;
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: reading SR failed \n " , __func__ ) ;
return - EIO ;
}
sr = isl1208_i2c_read_regs ( client , 0 , regs , ISL1208_RTC_SECTION_LEN ) ;
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: reading RTC section failed \n " ,
__func__ ) ;
return sr ;
}
2008-10-19 07:28:41 +04:00
tm - > tm_sec = bcd2bin ( regs [ ISL1208_REG_SC ] ) ;
tm - > tm_min = bcd2bin ( regs [ ISL1208_REG_MN ] ) ;
2008-04-28 13:11:53 +04:00
/* HR field has a more complex interpretation */
{
2006-07-14 11:24:11 +04:00
const u8 _hr = regs [ ISL1208_REG_HR ] ;
2008-04-28 13:11:53 +04:00
if ( _hr & ISL1208_REG_HR_MIL ) /* 24h format */
2008-10-19 07:28:41 +04:00
tm - > tm_hour = bcd2bin ( _hr & 0x3f ) ;
2008-04-28 13:11:53 +04:00
else {
/* 12h format */
2008-10-19 07:28:41 +04:00
tm - > tm_hour = bcd2bin ( _hr & 0x1f ) ;
2008-04-28 13:11:53 +04:00
if ( _hr & ISL1208_REG_HR_PM ) /* PM flag set */
2006-07-14 11:24:11 +04:00
tm - > tm_hour + = 12 ;
}
}
2008-10-19 07:28:41 +04:00
tm - > tm_mday = bcd2bin ( regs [ ISL1208_REG_DT ] ) ;
tm - > tm_mon = bcd2bin ( regs [ ISL1208_REG_MO ] ) - 1 ; /* rtc starts at 1 */
tm - > tm_year = bcd2bin ( regs [ ISL1208_REG_YR ] ) + 100 ;
tm - > tm_wday = bcd2bin ( regs [ ISL1208_REG_DW ] ) ;
2006-07-14 11:24:11 +04:00
return 0 ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_i2c_read_alarm ( struct i2c_client * client , struct rtc_wkalrm * alarm )
2006-07-14 11:24:11 +04:00
{
struct rtc_time * const tm = & alarm - > time ;
u8 regs [ ISL1208_ALARM_SECTION_LEN ] = { 0 , } ;
2011-03-23 02:34:53 +03:00
int icr , yr , sr = isl1208_i2c_get_sr ( client ) ;
2006-07-14 11:24:11 +04:00
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: reading SR failed \n " , __func__ ) ;
return sr ;
}
sr = isl1208_i2c_read_regs ( client , ISL1208_REG_SCA , regs ,
2008-04-28 13:11:53 +04:00
ISL1208_ALARM_SECTION_LEN ) ;
2006-07-14 11:24:11 +04:00
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: reading alarm section failed \n " ,
__func__ ) ;
return sr ;
}
/* MSB of each alarm register is an enable bit */
2008-10-19 07:28:41 +04:00
tm - > tm_sec = bcd2bin ( regs [ ISL1208_REG_SCA - ISL1208_REG_SCA ] & 0x7f ) ;
tm - > tm_min = bcd2bin ( regs [ ISL1208_REG_MNA - ISL1208_REG_SCA ] & 0x7f ) ;
tm - > tm_hour = bcd2bin ( regs [ ISL1208_REG_HRA - ISL1208_REG_SCA ] & 0x3f ) ;
tm - > tm_mday = bcd2bin ( regs [ ISL1208_REG_DTA - ISL1208_REG_SCA ] & 0x3f ) ;
2008-04-28 13:11:53 +04:00
tm - > tm_mon =
2008-10-19 07:28:41 +04:00
bcd2bin ( regs [ ISL1208_REG_MOA - ISL1208_REG_SCA ] & 0x1f ) - 1 ;
tm - > tm_wday = bcd2bin ( regs [ ISL1208_REG_DWA - ISL1208_REG_SCA ] & 0x03 ) ;
2006-07-14 11:24:11 +04:00
2011-03-23 02:34:53 +03:00
/* The alarm doesn't store the year so get it from the rtc section */
yr = i2c_smbus_read_byte_data ( client , ISL1208_REG_YR ) ;
if ( yr < 0 ) {
dev_err ( & client - > dev , " %s: reading RTC YR failed \n " , __func__ ) ;
return yr ;
}
tm - > tm_year = bcd2bin ( yr ) + 100 ;
icr = i2c_smbus_read_byte_data ( client , ISL1208_REG_INT ) ;
if ( icr < 0 ) {
dev_err ( & client - > dev , " %s: reading INT failed \n " , __func__ ) ;
return icr ;
}
alarm - > enabled = ! ! ( icr & ISL1208_REG_INT_ALME ) ;
return 0 ;
}
static int
isl1208_i2c_set_alarm ( struct i2c_client * client , struct rtc_wkalrm * alarm )
{
struct rtc_time * alarm_tm = & alarm - > time ;
u8 regs [ ISL1208_ALARM_SECTION_LEN ] = { 0 , } ;
const int offs = ISL1208_REG_SCA ;
struct rtc_time rtc_tm ;
int err , enable ;
err = isl1208_i2c_read_time ( client , & rtc_tm ) ;
if ( err )
return err ;
/* If the alarm time is before the current time disable the alarm */
2015-06-12 05:04:11 +03:00
if ( ! alarm - > enabled | | rtc_tm_sub ( alarm_tm , & rtc_tm ) < = 0 )
2011-03-23 02:34:53 +03:00
enable = 0x00 ;
else
enable = 0x80 ;
/* Program the alarm and enable it for each setting */
regs [ ISL1208_REG_SCA - offs ] = bin2bcd ( alarm_tm - > tm_sec ) | enable ;
regs [ ISL1208_REG_MNA - offs ] = bin2bcd ( alarm_tm - > tm_min ) | enable ;
regs [ ISL1208_REG_HRA - offs ] = bin2bcd ( alarm_tm - > tm_hour ) |
ISL1208_REG_HR_MIL | enable ;
regs [ ISL1208_REG_DTA - offs ] = bin2bcd ( alarm_tm - > tm_mday ) | enable ;
regs [ ISL1208_REG_MOA - offs ] = bin2bcd ( alarm_tm - > tm_mon + 1 ) | enable ;
regs [ ISL1208_REG_DWA - offs ] = bin2bcd ( alarm_tm - > tm_wday & 7 ) | enable ;
/* write ALARM registers */
err = isl1208_i2c_set_regs ( client , offs , regs ,
ISL1208_ALARM_SECTION_LEN ) ;
if ( err < 0 ) {
dev_err ( & client - > dev , " %s: writing ALARM section failed \n " ,
__func__ ) ;
return err ;
}
err = isl1208_rtc_toggle_alarm ( client , enable ) ;
if ( err )
return err ;
2006-07-14 11:24:11 +04:00
return 0 ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_rtc_read_time ( struct device * dev , struct rtc_time * tm )
2006-07-14 11:24:11 +04:00
{
return isl1208_i2c_read_time ( to_i2c_client ( dev ) , tm ) ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_i2c_set_time ( struct i2c_client * client , struct rtc_time const * tm )
2006-07-14 11:24:11 +04:00
{
int sr ;
u8 regs [ ISL1208_RTC_SECTION_LEN ] = { 0 , } ;
2008-12-24 00:57:10 +03:00
/* The clock has an 8 bit wide bcd-coded register (they never learn)
* for the year . tm_year is an offset from 1900 and we are interested
* in the 2000 - 2099 range , so any value less than 100 is invalid .
*/
if ( tm - > tm_year < 100 )
return - EINVAL ;
2008-10-19 07:28:41 +04:00
regs [ ISL1208_REG_SC ] = bin2bcd ( tm - > tm_sec ) ;
regs [ ISL1208_REG_MN ] = bin2bcd ( tm - > tm_min ) ;
regs [ ISL1208_REG_HR ] = bin2bcd ( tm - > tm_hour ) | ISL1208_REG_HR_MIL ;
2006-07-14 11:24:11 +04:00
2008-10-19 07:28:41 +04:00
regs [ ISL1208_REG_DT ] = bin2bcd ( tm - > tm_mday ) ;
regs [ ISL1208_REG_MO ] = bin2bcd ( tm - > tm_mon + 1 ) ;
regs [ ISL1208_REG_YR ] = bin2bcd ( tm - > tm_year - 100 ) ;
2006-07-14 11:24:11 +04:00
2008-10-19 07:28:41 +04:00
regs [ ISL1208_REG_DW ] = bin2bcd ( tm - > tm_wday & 7 ) ;
2006-07-14 11:24:11 +04:00
sr = isl1208_i2c_get_sr ( client ) ;
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: reading SR failed \n " , __func__ ) ;
return sr ;
}
/* set WRTC */
2008-04-28 13:11:53 +04:00
sr = i2c_smbus_write_byte_data ( client , ISL1208_REG_SR ,
2006-07-14 11:24:11 +04:00
sr | ISL1208_REG_SR_WRTC ) ;
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: writing SR failed \n " , __func__ ) ;
return sr ;
}
/* write RTC registers */
sr = isl1208_i2c_set_regs ( client , 0 , regs , ISL1208_RTC_SECTION_LEN ) ;
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: writing RTC section failed \n " ,
__func__ ) ;
return sr ;
}
/* clear WRTC again */
2018-01-23 15:17:58 +03:00
sr = isl1208_i2c_get_sr ( client ) ;
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: reading SR failed \n " , __func__ ) ;
return sr ;
}
2008-04-28 13:11:53 +04:00
sr = i2c_smbus_write_byte_data ( client , ISL1208_REG_SR ,
2006-07-14 11:24:11 +04:00
sr & ~ ISL1208_REG_SR_WRTC ) ;
if ( sr < 0 ) {
dev_err ( & client - > dev , " %s: writing SR failed \n " , __func__ ) ;
return sr ;
}
return 0 ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_rtc_set_time ( struct device * dev , struct rtc_time * tm )
2006-07-14 11:24:11 +04:00
{
return isl1208_i2c_set_time ( to_i2c_client ( dev ) , tm ) ;
}
2008-04-28 13:11:53 +04:00
static int
isl1208_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alarm )
2006-07-14 11:24:11 +04:00
{
return isl1208_i2c_read_alarm ( to_i2c_client ( dev ) , alarm ) ;
}
2011-03-23 02:34:53 +03:00
static int
isl1208_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alarm )
{
return isl1208_i2c_set_alarm ( to_i2c_client ( dev ) , alarm ) ;
}
2018-07-24 14:31:21 +03:00
static ssize_t timestamp0_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct i2c_client * client = dev_get_drvdata ( dev ) ;
int sr ;
sr = isl1208_i2c_get_sr ( client ) ;
if ( sr < 0 ) {
dev_err ( dev , " %s: reading SR failed \n " , __func__ ) ;
return sr ;
}
sr & = ~ ISL1208_REG_SR_EVT ;
sr = i2c_smbus_write_byte_data ( client , ISL1208_REG_SR , sr ) ;
if ( sr < 0 )
dev_err ( dev , " %s: writing SR failed \n " ,
__func__ ) ;
return count ;
} ;
static ssize_t timestamp0_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct i2c_client * client = dev_get_drvdata ( dev ) ;
u8 regs [ ISL1219_EVT_SECTION_LEN ] = { 0 , } ;
struct rtc_time tm ;
int sr ;
sr = isl1208_i2c_get_sr ( client ) ;
if ( sr < 0 ) {
dev_err ( dev , " %s: reading SR failed \n " , __func__ ) ;
return sr ;
}
if ( ! ( sr & ISL1208_REG_SR_EVT ) )
return 0 ;
sr = isl1208_i2c_read_regs ( client , ISL1219_REG_SCT , regs ,
ISL1219_EVT_SECTION_LEN ) ;
if ( sr < 0 ) {
dev_err ( dev , " %s: reading event section failed \n " ,
__func__ ) ;
return 0 ;
}
/* MSB of each alarm register is an enable bit */
tm . tm_sec = bcd2bin ( regs [ ISL1219_REG_SCT - ISL1219_REG_SCT ] & 0x7f ) ;
tm . tm_min = bcd2bin ( regs [ ISL1219_REG_MNT - ISL1219_REG_SCT ] & 0x7f ) ;
tm . tm_hour = bcd2bin ( regs [ ISL1219_REG_HRT - ISL1219_REG_SCT ] & 0x3f ) ;
tm . tm_mday = bcd2bin ( regs [ ISL1219_REG_DTT - ISL1219_REG_SCT ] & 0x3f ) ;
tm . tm_mon =
bcd2bin ( regs [ ISL1219_REG_MOT - ISL1219_REG_SCT ] & 0x1f ) - 1 ;
tm . tm_year = bcd2bin ( regs [ ISL1219_REG_YRT - ISL1219_REG_SCT ] ) + 100 ;
sr = rtc_valid_tm ( & tm ) ;
if ( sr )
return sr ;
return sprintf ( buf , " %llu \n " ,
( unsigned long long ) rtc_tm_to_time64 ( & tm ) ) ;
} ;
static DEVICE_ATTR_RW ( timestamp0 ) ;
2011-03-23 02:34:53 +03:00
static irqreturn_t
isl1208_rtc_interrupt ( int irq , void * data )
{
unsigned long timeout = jiffies + msecs_to_jiffies ( 1000 ) ;
struct i2c_client * client = data ;
2013-02-05 02:28:53 +04:00
struct rtc_device * rtc = i2c_get_clientdata ( client ) ;
2011-03-23 02:34:53 +03:00
int handled = 0 , sr , err ;
/*
* I2C reads get NAK ' ed if we read straight away after an interrupt ?
* Using a mdelay / msleep didn ' t seem to help either , so we work around
* this by continually trying to read the register for a short time .
*/
while ( 1 ) {
sr = isl1208_i2c_get_sr ( client ) ;
if ( sr > = 0 )
break ;
if ( time_after ( jiffies , timeout ) ) {
dev_err ( & client - > dev , " %s: reading SR failed \n " ,
__func__ ) ;
return sr ;
}
}
if ( sr & ISL1208_REG_SR_ALM ) {
dev_dbg ( & client - > dev , " alarm! \n " ) ;
2013-02-05 02:28:53 +04:00
rtc_update_irq ( rtc , 1 , RTC_IRQF | RTC_AF ) ;
2011-03-23 02:34:53 +03:00
/* Clear the alarm */
sr & = ~ ISL1208_REG_SR_ALM ;
sr = i2c_smbus_write_byte_data ( client , ISL1208_REG_SR , sr ) ;
if ( sr < 0 )
dev_err ( & client - > dev , " %s: writing SR failed \n " ,
__func__ ) ;
else
handled = 1 ;
/* Disable the alarm */
err = isl1208_rtc_toggle_alarm ( client , 0 ) ;
if ( err )
return err ;
}
2018-07-24 14:31:21 +03:00
if ( sr & ISL1208_REG_SR_EVT ) {
sysfs_notify ( & rtc - > dev . kobj , NULL ,
dev_attr_timestamp0 . attr . name ) ;
dev_warn ( & client - > dev , " event detected " ) ;
handled = 1 ;
}
2011-03-23 02:34:53 +03:00
return handled ? IRQ_HANDLED : IRQ_NONE ;
}
2006-10-01 10:28:17 +04:00
static const struct rtc_class_ops isl1208_rtc_ops = {
2008-04-28 13:11:53 +04:00
. proc = isl1208_rtc_proc ,
. read_time = isl1208_rtc_read_time ,
. set_time = isl1208_rtc_set_time ,
. read_alarm = isl1208_rtc_read_alarm ,
2011-03-23 02:34:53 +03:00
. set_alarm = isl1208_rtc_set_alarm ,
2006-07-14 11:24:11 +04:00
} ;
/* sysfs interface */
2008-04-28 13:11:53 +04:00
static ssize_t
isl1208_sysfs_show_atrim ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-07-14 11:24:11 +04:00
{
2008-04-28 13:11:53 +04:00
int atr = isl1208_i2c_get_atr ( to_i2c_client ( dev ) ) ;
2006-07-14 11:24:11 +04:00
if ( atr < 0 )
return atr ;
2008-04-28 13:11:53 +04:00
return sprintf ( buf , " %d.%.2d pF \n " , atr > > 2 , ( atr & 0x3 ) * 25 ) ;
2006-07-14 11:24:11 +04:00
}
2008-04-28 13:11:53 +04:00
2006-07-14 11:24:11 +04:00
static DEVICE_ATTR ( atrim , S_IRUGO , isl1208_sysfs_show_atrim , NULL ) ;
2008-04-28 13:11:53 +04:00
static ssize_t
isl1208_sysfs_show_dtrim ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-07-14 11:24:11 +04:00
{
2008-04-28 13:11:53 +04:00
int dtr = isl1208_i2c_get_dtr ( to_i2c_client ( dev ) ) ;
2006-07-14 11:24:11 +04:00
if ( dtr < 0 )
return dtr ;
return sprintf ( buf , " %d ppm \n " , dtr ) ;
}
2008-04-28 13:11:53 +04:00
2006-07-14 11:24:11 +04:00
static DEVICE_ATTR ( dtrim , S_IRUGO , isl1208_sysfs_show_dtrim , NULL ) ;
2008-04-28 13:11:53 +04:00
static ssize_t
isl1208_sysfs_show_usr ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-07-14 11:24:11 +04:00
{
2008-04-28 13:11:53 +04:00
int usr = isl1208_i2c_get_usr ( to_i2c_client ( dev ) ) ;
2006-07-14 11:24:11 +04:00
if ( usr < 0 )
return usr ;
return sprintf ( buf , " 0x%.4x \n " , usr ) ;
}
2008-04-28 13:11:53 +04:00
static ssize_t
isl1208_sysfs_store_usr ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2006-07-14 11:24:11 +04:00
{
int usr = - 1 ;
if ( buf [ 0 ] = = ' 0 ' & & ( buf [ 1 ] = = ' x ' | | buf [ 1 ] = = ' X ' ) ) {
if ( sscanf ( buf , " %x " , & usr ) ! = 1 )
return - EINVAL ;
} else {
if ( sscanf ( buf , " %d " , & usr ) ! = 1 )
return - EINVAL ;
}
if ( usr < 0 | | usr > 0xffff )
return - EINVAL ;
return isl1208_i2c_set_usr ( to_i2c_client ( dev ) , usr ) ? - EIO : count ;
}
2008-04-28 13:11:53 +04:00
2006-07-14 11:24:11 +04:00
static DEVICE_ATTR ( usr , S_IRUGO | S_IWUSR , isl1208_sysfs_show_usr ,
isl1208_sysfs_store_usr ) ;
2010-05-25 01:33:46 +04:00
static struct attribute * isl1208_rtc_attrs [ ] = {
& dev_attr_atrim . attr ,
& dev_attr_dtrim . attr ,
& dev_attr_usr . attr ,
NULL
} ;
2008-04-28 13:11:53 +04:00
2010-05-25 01:33:46 +04:00
static const struct attribute_group isl1208_rtc_sysfs_files = {
. attrs = isl1208_rtc_attrs ,
} ;
2008-04-28 13:11:53 +04:00
2018-07-24 14:31:21 +03:00
static struct attribute * isl1219_rtc_attrs [ ] = {
& dev_attr_timestamp0 . attr ,
NULL
} ;
static const struct attribute_group isl1219_rtc_sysfs_files = {
. attrs = isl1219_rtc_attrs ,
} ;
2018-07-24 14:31:21 +03:00
static int isl1208_setup_irq ( struct i2c_client * client , int irq )
{
int rc = devm_request_threaded_irq ( & client - > dev , irq , NULL ,
isl1208_rtc_interrupt ,
IRQF_SHARED | IRQF_ONESHOT ,
isl1208_driver . driver . name ,
client ) ;
if ( ! rc ) {
device_init_wakeup ( & client - > dev , 1 ) ;
enable_irq_wake ( irq ) ;
} else {
dev_err ( & client - > dev ,
" Unable to request irq %d, no alarm support \n " ,
irq ) ;
}
return rc ;
}
2008-04-28 13:11:53 +04:00
static int
2008-04-30 01:11:39 +04:00
isl1208_probe ( struct i2c_client * client , const struct i2c_device_id * id )
2008-04-28 13:11:53 +04:00
{
int rc = 0 ;
struct rtc_device * rtc ;
2018-07-24 14:31:21 +03:00
int evdet_irq = - 1 ;
2006-07-14 11:24:11 +04:00
2008-04-28 13:11:53 +04:00
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) )
return - ENODEV ;
2006-07-14 11:24:11 +04:00
2008-04-28 13:11:53 +04:00
if ( isl1208_i2c_validate_client ( client ) < 0 )
return - ENODEV ;
2018-03-05 13:43:53 +03:00
rtc = devm_rtc_allocate_device ( & client - > dev ) ;
2013-11-13 03:10:30 +04:00
if ( IS_ERR ( rtc ) )
return PTR_ERR ( rtc ) ;
2006-07-14 11:24:11 +04:00
2018-03-05 13:43:53 +03:00
rtc - > ops = & isl1208_rtc_ops ;
2008-04-28 13:11:53 +04:00
i2c_set_clientdata ( client , rtc ) ;
2018-07-24 14:31:21 +03:00
dev_set_drvdata ( & rtc - > dev , client ) ;
2006-07-14 11:24:11 +04:00
2008-04-28 13:11:53 +04:00
rc = isl1208_i2c_get_sr ( client ) ;
2006-07-14 11:24:11 +04:00
if ( rc < 0 ) {
2008-04-28 13:11:53 +04:00
dev_err ( & client - > dev , " reading status failed \n " ) ;
2013-11-13 03:10:30 +04:00
return rc ;
2006-07-14 11:24:11 +04:00
}
if ( rc & ISL1208_REG_SR_RTCF )
2008-04-28 13:11:53 +04:00
dev_warn ( & client - > dev , " rtc power failure detected, "
2006-07-14 11:24:11 +04:00
" please set clock. \n " ) ;
2018-07-24 14:31:21 +03:00
if ( id - > driver_data = = TYPE_ISL1219 ) {
rc = i2c_smbus_write_byte_data ( client , ISL1219_REG_EV ,
ISL1219_REG_EV_EVEN ) ;
if ( rc < 0 ) {
dev_err ( & client - > dev , " could not enable tamper detection \n " ) ;
return rc ;
}
rc = rtc_add_group ( rtc , & isl1219_rtc_sysfs_files ) ;
if ( rc )
return rc ;
2018-07-24 14:31:21 +03:00
evdet_irq = of_irq_get_byname ( client - > dev . of_node , " evdet " ) ;
2018-07-24 14:31:21 +03:00
}
2010-05-25 01:33:46 +04:00
rc = sysfs_create_group ( & client - > dev . kobj , & isl1208_rtc_sysfs_files ) ;
2008-04-28 13:11:53 +04:00
if ( rc )
2013-11-13 03:10:30 +04:00
return rc ;
2006-07-14 11:24:11 +04:00
2018-07-24 14:31:21 +03:00
if ( client - > irq > 0 )
rc = isl1208_setup_irq ( client , client - > irq ) ;
if ( rc )
return rc ;
if ( evdet_irq > 0 & & evdet_irq ! = client - > irq )
rc = isl1208_setup_irq ( client , evdet_irq ) ;
if ( rc )
return rc ;
2018-03-05 13:43:53 +03:00
2018-03-05 13:43:53 +03:00
return rtc_register_device ( rtc ) ;
2006-07-14 11:24:11 +04:00
}
static int
2008-04-28 13:11:53 +04:00
isl1208_remove ( struct i2c_client * client )
2006-07-14 11:24:11 +04:00
{
2010-05-25 01:33:46 +04:00
sysfs_remove_group ( & client - > dev . kobj , & isl1208_rtc_sysfs_files ) ;
2006-07-14 11:24:11 +04:00
return 0 ;
}
2008-04-30 01:11:40 +04:00
static const struct i2c_device_id isl1208_id [ ] = {
2018-07-24 14:31:21 +03:00
{ " isl1208 " , TYPE_ISL1208 } ,
{ " isl1218 " , TYPE_ISL1218 } ,
{ " isl1219 " , TYPE_ISL1219 } ,
2008-04-30 01:11:40 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , isl1208_id ) ;
2017-03-03 17:29:20 +03:00
static const struct of_device_id isl1208_of_match [ ] = {
{ . compatible = " isil,isl1208 " } ,
{ . compatible = " isil,isl1218 " } ,
2018-07-24 14:31:21 +03:00
{ . compatible = " isil,isl1219 " } ,
2017-03-03 17:29:20 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , isl1208_of_match ) ;
2008-04-28 13:11:53 +04:00
static struct i2c_driver isl1208_driver = {
. driver = {
2017-03-03 17:29:20 +03:00
. name = " rtc-isl1208 " ,
. of_match_table = of_match_ptr ( isl1208_of_match ) ,
} ,
2008-04-28 13:11:53 +04:00
. probe = isl1208_probe ,
. remove = isl1208_remove ,
2008-04-30 01:11:40 +04:00
. id_table = isl1208_id ,
2008-04-28 13:11:53 +04:00
} ;
2006-07-14 11:24:11 +04:00
2012-03-24 02:02:31 +04:00
module_i2c_driver ( isl1208_driver ) ;
2006-07-14 11:24:11 +04:00
MODULE_AUTHOR ( " Herbert Valerio Riedel <hvr@gnu.org> " ) ;
MODULE_DESCRIPTION ( " Intersil ISL1208 RTC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;