2006-03-27 13:16:45 +04:00
/*
2008-10-16 09:03:03 +04:00
* An I2C driver for Ricoh RS5C372 , R2025S / D and RV5C38 [ 67 ] RTCs
2006-03-27 13:16:45 +04:00
*
* Copyright ( C ) 2005 Pavel Mironchik < pmironchik @ optifacio . net >
* Copyright ( C ) 2006 Tower Technologies
2008-10-16 09:03:01 +04:00
* Copyright ( C ) 2008 Paul Mundt
2006-03-27 13:16:45 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/i2c.h>
# include <linux/rtc.h>
# include <linux/bcd.h>
2008-10-16 09:03:01 +04:00
# define DRV_VERSION "0.6"
2006-03-27 13:16:45 +04:00
2007-01-06 03:36:37 +03:00
/*
* Ricoh has a family of I2C based RTCs , which differ only slightly from
* each other . Differences center on pinout ( e . g . how many interrupts ,
* output clock , etc ) and how the control registers are used . The ' 372
* is significant only because that ' s the one this driver first supported .
*/
2006-03-27 13:16:45 +04:00
# define RS5C372_REG_SECS 0
# define RS5C372_REG_MINS 1
# define RS5C372_REG_HOURS 2
# define RS5C372_REG_WDAY 3
# define RS5C372_REG_DAY 4
# define RS5C372_REG_MONTH 5
# define RS5C372_REG_YEAR 6
# define RS5C372_REG_TRIM 7
2007-01-06 03:36:37 +03:00
# define RS5C372_TRIM_XSL 0x80
# define RS5C372_TRIM_MASK 0x7F
# define RS5C_REG_ALARM_A_MIN 8 /* or ALARM_W */
# define RS5C_REG_ALARM_A_HOURS 9
# define RS5C_REG_ALARM_A_WDAY 10
# define RS5C_REG_ALARM_B_MIN 11 /* or ALARM_D */
# define RS5C_REG_ALARM_B_HOURS 12
# define RS5C_REG_ALARM_B_WDAY 13 /* (ALARM_B only) */
# define RS5C_REG_CTRL1 14
# define RS5C_CTRL1_AALE (1 << 7) /* or WALE */
# define RS5C_CTRL1_BALE (1 << 6) /* or DALE */
# define RV5C387_CTRL1_24 (1 << 5)
# define RS5C372A_CTRL1_SL1 (1 << 5)
# define RS5C_CTRL1_CT_MASK (7 << 0)
# define RS5C_CTRL1_CT0 (0 << 0) /* no periodic irq */
# define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */
# define RS5C_REG_CTRL2 15
# define RS5C372_CTRL2_24 (1 << 5)
2008-10-16 09:03:03 +04:00
# define R2025_CTRL2_XST (1 << 5)
# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2025S/D */
2007-01-06 03:36:37 +03:00
# define RS5C_CTRL2_CTFG (1 << 2)
# define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */
# define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */
/* to read (style 1) or write registers starting at R */
# define RS5C_ADDR(R) (((R) << 4) | 0)
enum rtc_type {
rtc_undef = 0 ,
2008-10-16 09:03:03 +04:00
rtc_r2025sd ,
2007-01-06 03:36:37 +03:00
rtc_rs5c372a ,
rtc_rs5c372b ,
rtc_rv5c386 ,
rtc_rv5c387a ,
} ;
2006-03-27 13:16:45 +04:00
2008-04-30 01:11:40 +04:00
static const struct i2c_device_id rs5c372_id [ ] = {
2008-10-16 09:03:03 +04:00
{ " r2025sd " , rtc_r2025sd } ,
2008-04-30 01:11:40 +04:00
{ " rs5c372a " , rtc_rs5c372a } ,
{ " rs5c372b " , rtc_rs5c372b } ,
{ " rv5c386 " , rtc_rv5c386 } ,
{ " rv5c387a " , rtc_rv5c387a } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , rs5c372_id ) ;
2007-01-06 03:36:37 +03:00
/* REVISIT: this assumes that:
* - we ' re in the 21 st century , so it ' s safe to ignore the century
* bit for rv5c38 [ 67 ] ( REG_MONTH bit 7 ) ;
* - we should use ALARM_A not ALARM_B ( may be wrong on some boards )
*/
struct rs5c372 {
struct i2c_client * client ;
struct rtc_device * rtc ;
enum rtc_type type ;
unsigned time24 : 1 ;
unsigned has_irq : 1 ;
2008-10-16 09:03:01 +04:00
unsigned smbus : 1 ;
2007-01-06 03:36:37 +03:00
char buf [ 17 ] ;
char * regs ;
} ;
2006-03-27 13:16:45 +04:00
2007-01-06 03:36:37 +03:00
static int rs5c_get_regs ( struct rs5c372 * rs5c )
{
struct i2c_client * client = rs5c - > client ;
struct i2c_msg msgs [ ] = {
{ client - > addr , I2C_M_RD , sizeof rs5c - > buf , rs5c - > buf } ,
} ;
/* This implements the third reading method from the datasheet, using
* an internal address that ' s reset after each transaction ( by STOP )
* to 0x0f . . . so we read extra registers , and skip the first one .
*
* The first method doesn ' t work with the iop3xx adapter driver , on at
* least 80219 chips ; this works around that bug .
2008-10-16 09:03:01 +04:00
*
* The third method on the other hand doesn ' t work for the SMBus - only
* configurations , so we use the the first method there , stripping off
* the extra register in the process .
2007-01-06 03:36:37 +03:00
*/
2008-10-16 09:03:01 +04:00
if ( rs5c - > smbus ) {
int addr = RS5C_ADDR ( RS5C372_REG_SECS ) ;
int size = sizeof ( rs5c - > buf ) - 1 ;
if ( i2c_smbus_read_i2c_block_data ( client , addr , size ,
rs5c - > buf + 1 ) ! = size ) {
dev_warn ( & client - > dev , " can't read registers \n " ) ;
return - EIO ;
}
} else {
if ( ( i2c_transfer ( client - > adapter , msgs , 1 ) ) ! = 1 ) {
dev_warn ( & client - > dev , " can't read registers \n " ) ;
return - EIO ;
}
2007-01-06 03:36:37 +03:00
}
2006-03-27 13:16:45 +04:00
2007-01-06 03:36:37 +03:00
dev_dbg ( & client - > dev ,
" %02x %02x %02x (%02x) %02x %02x %02x (%02x), "
" %02x %02x %02x, %02x %02x %02x; %02x %02x \n " ,
rs5c - > regs [ 0 ] , rs5c - > regs [ 1 ] , rs5c - > regs [ 2 ] , rs5c - > regs [ 3 ] ,
rs5c - > regs [ 4 ] , rs5c - > regs [ 5 ] , rs5c - > regs [ 6 ] , rs5c - > regs [ 7 ] ,
rs5c - > regs [ 8 ] , rs5c - > regs [ 9 ] , rs5c - > regs [ 10 ] , rs5c - > regs [ 11 ] ,
rs5c - > regs [ 12 ] , rs5c - > regs [ 13 ] , rs5c - > regs [ 14 ] , rs5c - > regs [ 15 ] ) ;
2006-03-27 13:16:45 +04:00
2007-01-06 03:36:37 +03:00
return 0 ;
}
2006-12-07 07:39:13 +03:00
2007-01-06 03:36:37 +03:00
static unsigned rs5c_reg2hr ( struct rs5c372 * rs5c , unsigned reg )
{
unsigned hour ;
2006-03-27 13:16:45 +04:00
2007-01-06 03:36:37 +03:00
if ( rs5c - > time24 )
2008-10-19 07:28:41 +04:00
return bcd2bin ( reg & 0x3f ) ;
2007-01-06 03:36:37 +03:00
2008-10-19 07:28:41 +04:00
hour = bcd2bin ( reg & 0x1f ) ;
2007-01-06 03:36:37 +03:00
if ( hour = = 12 )
hour = 0 ;
if ( reg & 0x20 )
hour + = 12 ;
return hour ;
}
static unsigned rs5c_hr2reg ( struct rs5c372 * rs5c , unsigned hour )
2006-03-27 13:16:45 +04:00
{
2007-01-06 03:36:37 +03:00
if ( rs5c - > time24 )
2008-10-19 07:28:41 +04:00
return bin2bcd ( hour ) ;
2007-01-06 03:36:37 +03:00
if ( hour > 12 )
2008-10-19 07:28:41 +04:00
return 0x20 | bin2bcd ( hour - 12 ) ;
2007-01-06 03:36:37 +03:00
if ( hour = = 12 )
2008-10-19 07:28:41 +04:00
return 0x20 | bin2bcd ( 12 ) ;
2007-01-06 03:36:37 +03:00
if ( hour = = 0 )
2008-10-19 07:28:41 +04:00
return bin2bcd ( 12 ) ;
return bin2bcd ( hour ) ;
2007-01-06 03:36:37 +03:00
}
2006-03-27 13:16:45 +04:00
2007-01-06 03:36:37 +03:00
static int rs5c372_get_datetime ( struct i2c_client * client , struct rtc_time * tm )
{
struct rs5c372 * rs5c = i2c_get_clientdata ( client ) ;
int status = rs5c_get_regs ( rs5c ) ;
2006-12-07 07:39:13 +03:00
2007-01-06 03:36:37 +03:00
if ( status < 0 )
return status ;
2006-03-27 13:16:45 +04:00
2008-10-19 07:28:41 +04:00
tm - > tm_sec = bcd2bin ( rs5c - > regs [ RS5C372_REG_SECS ] & 0x7f ) ;
tm - > tm_min = bcd2bin ( rs5c - > regs [ RS5C372_REG_MINS ] & 0x7f ) ;
2007-01-06 03:36:37 +03:00
tm - > tm_hour = rs5c_reg2hr ( rs5c , rs5c - > regs [ RS5C372_REG_HOURS ] ) ;
2006-03-27 13:16:45 +04:00
2008-10-19 07:28:41 +04:00
tm - > tm_wday = bcd2bin ( rs5c - > regs [ RS5C372_REG_WDAY ] & 0x07 ) ;
tm - > tm_mday = bcd2bin ( rs5c - > regs [ RS5C372_REG_DAY ] & 0x3f ) ;
2006-03-27 13:16:45 +04:00
/* tm->tm_mon is zero-based */
2008-10-19 07:28:41 +04:00
tm - > tm_mon = bcd2bin ( rs5c - > regs [ RS5C372_REG_MONTH ] & 0x1f ) - 1 ;
2006-03-27 13:16:45 +04:00
/* year is 1900 + tm->tm_year */
2008-10-19 07:28:41 +04:00
tm - > tm_year = bcd2bin ( rs5c - > regs [ RS5C372_REG_YEAR ] ) + 100 ;
2006-03-27 13:16:45 +04:00
dev_dbg ( & client - > dev , " %s: tm is secs=%d, mins=%d, hours=%d, "
" mday=%d, mon=%d, year=%d, wday=%d \n " ,
2008-04-28 13:12:00 +04:00
__func__ ,
2006-03-27 13:16:45 +04:00
tm - > tm_sec , tm - > tm_min , tm - > tm_hour ,
tm - > tm_mday , tm - > tm_mon , tm - > tm_year , tm - > tm_wday ) ;
2007-01-06 03:36:37 +03:00
/* rtc might need initialization */
return rtc_valid_tm ( tm ) ;
2006-03-27 13:16:45 +04:00
}
static int rs5c372_set_datetime ( struct i2c_client * client , struct rtc_time * tm )
{
2007-01-06 03:36:37 +03:00
struct rs5c372 * rs5c = i2c_get_clientdata ( client ) ;
unsigned char buf [ 8 ] ;
2008-10-16 09:03:01 +04:00
int addr ;
2006-03-27 13:16:45 +04:00
2007-01-06 03:36:37 +03:00
dev_dbg ( & client - > dev , " %s: tm is secs=%d, mins=%d, hours=%d "
2006-03-27 13:16:45 +04:00
" mday=%d, mon=%d, year=%d, wday=%d \n " ,
2008-04-28 13:12:00 +04:00
__func__ ,
2007-01-06 03:36:37 +03:00
tm - > tm_sec , tm - > tm_min , tm - > tm_hour ,
2006-03-27 13:16:45 +04:00
tm - > tm_mday , tm - > tm_mon , tm - > tm_year , tm - > tm_wday ) ;
2008-10-16 09:03:01 +04:00
addr = RS5C_ADDR ( RS5C372_REG_SECS ) ;
2008-10-19 07:28:41 +04:00
buf [ 0 ] = bin2bcd ( tm - > tm_sec ) ;
buf [ 1 ] = bin2bcd ( tm - > tm_min ) ;
2008-10-16 09:03:01 +04:00
buf [ 2 ] = rs5c_hr2reg ( rs5c , tm - > tm_hour ) ;
2008-10-19 07:28:41 +04:00
buf [ 3 ] = bin2bcd ( tm - > tm_wday ) ;
buf [ 4 ] = bin2bcd ( tm - > tm_mday ) ;
buf [ 5 ] = bin2bcd ( tm - > tm_mon + 1 ) ;
buf [ 6 ] = bin2bcd ( tm - > tm_year - 100 ) ;
2006-03-27 13:16:45 +04:00
2008-10-16 09:03:01 +04:00
if ( i2c_smbus_write_i2c_block_data ( client , addr , sizeof ( buf ) , buf ) < 0 ) {
2008-04-28 13:12:00 +04:00
dev_err ( & client - > dev , " %s: write error \n " , __func__ ) ;
2006-03-27 13:16:45 +04:00
return - EIO ;
}
return 0 ;
}
2007-01-06 03:36:37 +03:00
# if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
# define NEED_TRIM
# endif
# if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
# define NEED_TRIM
# endif
# ifdef NEED_TRIM
2006-03-27 13:16:45 +04:00
static int rs5c372_get_trim ( struct i2c_client * client , int * osc , int * trim )
{
2006-12-07 07:39:13 +03:00
struct rs5c372 * rs5c372 = i2c_get_clientdata ( client ) ;
2007-01-06 03:36:37 +03:00
u8 tmp = rs5c372 - > regs [ RS5C372_REG_TRIM ] ;
2006-03-27 13:16:45 +04:00
if ( osc )
2006-12-07 07:39:13 +03:00
* osc = ( tmp & RS5C372_TRIM_XSL ) ? 32000 : 32768 ;
2006-03-27 13:16:45 +04:00
2006-11-25 22:09:29 +03:00
if ( trim ) {
2008-04-28 13:12:00 +04:00
dev_dbg ( & client - > dev , " %s: raw trim=%x \n " , __func__ , tmp ) ;
2007-01-06 03:36:37 +03:00
tmp & = RS5C372_TRIM_MASK ;
if ( tmp & 0x3e ) {
int t = tmp & 0x3f ;
if ( tmp & 0x40 )
t = ( ~ t | ( s8 ) 0xc0 ) + 1 ;
else
t = t - 1 ;
tmp = t * 2 ;
} else
tmp = 0 ;
* trim = tmp ;
2006-11-25 22:09:29 +03:00
}
2006-03-27 13:16:45 +04:00
return 0 ;
}
2007-01-06 03:36:37 +03:00
# endif
2006-03-27 13:16:45 +04:00
static int rs5c372_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
return rs5c372_get_datetime ( to_i2c_client ( dev ) , tm ) ;
}
static int rs5c372_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
return rs5c372_set_datetime ( to_i2c_client ( dev ) , tm ) ;
}
2007-01-06 03:36:37 +03:00
# if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE)
static int
rs5c_rtc_ioctl ( struct device * dev , unsigned int cmd , unsigned long arg )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct rs5c372 * rs5c = i2c_get_clientdata ( client ) ;
2008-10-16 09:03:01 +04:00
unsigned char buf ;
int status , addr ;
2007-01-06 03:36:37 +03:00
2008-10-16 09:03:01 +04:00
buf = rs5c - > regs [ RS5C_REG_CTRL1 ] ;
2007-01-06 03:36:37 +03:00
switch ( cmd ) {
case RTC_UIE_OFF :
case RTC_UIE_ON :
/* some 327a modes use a different IRQ pin for 1Hz irqs */
if ( rs5c - > type = = rtc_rs5c372a
2008-10-16 09:03:01 +04:00
& & ( buf & RS5C372A_CTRL1_SL1 ) )
2007-01-06 03:36:37 +03:00
return - ENOIOCTLCMD ;
case RTC_AIE_OFF :
case RTC_AIE_ON :
/* these irq management calls only make sense for chips
* which are wired up to an IRQ .
*/
if ( ! rs5c - > has_irq )
return - ENOIOCTLCMD ;
break ;
default :
return - ENOIOCTLCMD ;
}
status = rs5c_get_regs ( rs5c ) ;
if ( status < 0 )
return status ;
2008-10-16 09:03:01 +04:00
addr = RS5C_ADDR ( RS5C_REG_CTRL1 ) ;
2007-01-06 03:36:37 +03:00
switch ( cmd ) {
case RTC_AIE_OFF : /* alarm off */
2008-10-16 09:03:01 +04:00
buf & = ~ RS5C_CTRL1_AALE ;
2007-01-06 03:36:37 +03:00
break ;
case RTC_AIE_ON : /* alarm on */
2008-10-16 09:03:01 +04:00
buf | = RS5C_CTRL1_AALE ;
2007-01-06 03:36:37 +03:00
break ;
case RTC_UIE_OFF : /* update off */
2008-10-16 09:03:01 +04:00
buf & = ~ RS5C_CTRL1_CT_MASK ;
2007-01-06 03:36:37 +03:00
break ;
case RTC_UIE_ON : /* update on */
2008-10-16 09:03:01 +04:00
buf & = ~ RS5C_CTRL1_CT_MASK ;
buf | = RS5C_CTRL1_CT4 ;
2007-01-06 03:36:37 +03:00
break ;
}
2008-10-16 09:03:01 +04:00
if ( i2c_smbus_write_byte_data ( client , addr , buf ) < 0 ) {
2007-01-06 03:36:37 +03:00
printk ( KERN_WARNING " %s: can't update alarm \n " ,
rs5c - > rtc - > name ) ;
status = - EIO ;
} else
2008-10-16 09:03:01 +04:00
rs5c - > regs [ RS5C_REG_CTRL1 ] = buf ;
2007-01-06 03:36:37 +03:00
return status ;
}
# else
# define rs5c_rtc_ioctl NULL
# endif
/* NOTE: Since RTC_WKALM_{RD,SET} were originally defined for EFI,
* which only exposes a polled programming interface ; and since
* these calls map directly to those EFI requests ; we don ' t demand
* we have an IRQ for this chip when we go through this API .
*
* The older x86_pc derived RTC_ALM_ { READ , SET } calls require irqs
* though , managed through RTC_AIE_ { ON , OFF } requests .
*/
static int rs5c_read_alarm ( struct device * dev , struct rtc_wkalrm * t )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct rs5c372 * rs5c = i2c_get_clientdata ( client ) ;
int status ;
status = rs5c_get_regs ( rs5c ) ;
if ( status < 0 )
return status ;
/* report alarm time */
t - > time . tm_sec = 0 ;
2008-10-19 07:28:41 +04:00
t - > time . tm_min = bcd2bin ( rs5c - > regs [ RS5C_REG_ALARM_A_MIN ] & 0x7f ) ;
2007-01-06 03:36:37 +03:00
t - > time . tm_hour = rs5c_reg2hr ( rs5c , rs5c - > regs [ RS5C_REG_ALARM_A_HOURS ] ) ;
t - > time . tm_mday = - 1 ;
t - > time . tm_mon = - 1 ;
t - > time . tm_year = - 1 ;
t - > time . tm_wday = - 1 ;
t - > time . tm_yday = - 1 ;
t - > time . tm_isdst = - 1 ;
/* ... and status */
t - > enabled = ! ! ( rs5c - > regs [ RS5C_REG_CTRL1 ] & RS5C_CTRL1_AALE ) ;
t - > pending = ! ! ( rs5c - > regs [ RS5C_REG_CTRL2 ] & RS5C_CTRL2_AAFG ) ;
return 0 ;
}
static int rs5c_set_alarm ( struct device * dev , struct rtc_wkalrm * t )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct rs5c372 * rs5c = i2c_get_clientdata ( client ) ;
2008-10-16 09:03:01 +04:00
int status , addr , i ;
unsigned char buf [ 3 ] ;
2007-01-06 03:36:37 +03:00
/* only handle up to 24 hours in the future, like RTC_ALM_SET */
if ( t - > time . tm_mday ! = - 1
| | t - > time . tm_mon ! = - 1
| | t - > time . tm_year ! = - 1 )
return - EINVAL ;
/* REVISIT: round up tm_sec */
/* if needed, disable irq (clears pending status) */
status = rs5c_get_regs ( rs5c ) ;
if ( status < 0 )
return status ;
if ( rs5c - > regs [ RS5C_REG_CTRL1 ] & RS5C_CTRL1_AALE ) {
2008-10-16 09:03:01 +04:00
addr = RS5C_ADDR ( RS5C_REG_CTRL1 ) ;
buf [ 0 ] = rs5c - > regs [ RS5C_REG_CTRL1 ] & ~ RS5C_CTRL1_AALE ;
if ( i2c_smbus_write_byte_data ( client , addr , buf [ 0 ] ) < 0 ) {
2007-01-06 03:36:37 +03:00
pr_debug ( " %s: can't disable alarm \n " , rs5c - > rtc - > name ) ;
return - EIO ;
}
2008-10-16 09:03:01 +04:00
rs5c - > regs [ RS5C_REG_CTRL1 ] = buf [ 0 ] ;
2007-01-06 03:36:37 +03:00
}
/* set alarm */
2008-10-19 07:28:41 +04:00
buf [ 0 ] = bin2bcd ( t - > time . tm_min ) ;
2008-10-16 09:03:01 +04:00
buf [ 1 ] = rs5c_hr2reg ( rs5c , t - > time . tm_hour ) ;
buf [ 2 ] = 0x7f ; /* any/all days */
for ( i = 0 ; i < sizeof ( buf ) ; i + + ) {
addr = RS5C_ADDR ( RS5C_REG_ALARM_A_MIN + i ) ;
if ( i2c_smbus_write_byte_data ( client , addr , buf [ i ] ) < 0 ) {
pr_debug ( " %s: can't set alarm time \n " , rs5c - > rtc - > name ) ;
return - EIO ;
}
2007-01-06 03:36:37 +03:00
}
/* ... and maybe enable its irq */
if ( t - > enabled ) {
2008-10-16 09:03:01 +04:00
addr = RS5C_ADDR ( RS5C_REG_CTRL1 ) ;
buf [ 0 ] = rs5c - > regs [ RS5C_REG_CTRL1 ] | RS5C_CTRL1_AALE ;
if ( i2c_smbus_write_byte_data ( client , addr , buf [ 0 ] ) < 0 )
2007-01-06 03:36:37 +03:00
printk ( KERN_WARNING " %s: can't enable alarm \n " ,
rs5c - > rtc - > name ) ;
2008-10-16 09:03:01 +04:00
rs5c - > regs [ RS5C_REG_CTRL1 ] = buf [ 0 ] ;
2007-01-06 03:36:37 +03:00
}
return 0 ;
}
# if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
2006-03-27 13:16:45 +04:00
static int rs5c372_rtc_proc ( struct device * dev , struct seq_file * seq )
{
int err , osc , trim ;
2006-04-11 09:54:43 +04:00
err = rs5c372_get_trim ( to_i2c_client ( dev ) , & osc , & trim ) ;
if ( err = = 0 ) {
2007-01-06 03:36:37 +03:00
seq_printf ( seq , " crystal \t \t : %d.%03d KHz \n " ,
osc / 1000 , osc % 1000 ) ;
seq_printf ( seq , " trim \t \t : %d \n " , trim ) ;
2006-03-27 13:16:45 +04:00
}
return 0 ;
}
2007-01-06 03:36:37 +03:00
# else
# define rs5c372_rtc_proc NULL
# endif
2006-10-01 10:28:17 +04:00
static const struct rtc_class_ops rs5c372_rtc_ops = {
2006-03-27 13:16:45 +04:00
. proc = rs5c372_rtc_proc ,
2007-01-06 03:36:37 +03:00
. ioctl = rs5c_rtc_ioctl ,
2006-03-27 13:16:45 +04:00
. read_time = rs5c372_rtc_read_time ,
. set_time = rs5c372_rtc_set_time ,
2007-01-06 03:36:37 +03:00
. read_alarm = rs5c_read_alarm ,
. set_alarm = rs5c_set_alarm ,
2006-03-27 13:16:45 +04:00
} ;
2007-01-06 03:36:37 +03:00
# if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
2006-03-27 13:16:45 +04:00
static ssize_t rs5c372_sysfs_show_trim ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2006-04-11 09:54:44 +04:00
int err , trim ;
2006-03-27 13:16:45 +04:00
2006-04-11 09:54:44 +04:00
err = rs5c372_get_trim ( to_i2c_client ( dev ) , NULL , & trim ) ;
if ( err )
return err ;
2006-03-27 13:16:45 +04:00
2007-01-06 03:36:37 +03:00
return sprintf ( buf , " %d \n " , trim ) ;
2006-03-27 13:16:45 +04:00
}
static DEVICE_ATTR ( trim , S_IRUGO , rs5c372_sysfs_show_trim , NULL ) ;
static ssize_t rs5c372_sysfs_show_osc ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2006-04-11 09:54:44 +04:00
int err , osc ;
2006-03-27 13:16:45 +04:00
2006-04-11 09:54:44 +04:00
err = rs5c372_get_trim ( to_i2c_client ( dev ) , & osc , NULL ) ;
if ( err )
return err ;
2006-03-27 13:16:45 +04:00
2006-04-11 09:54:44 +04:00
return sprintf ( buf , " %d.%03d KHz \n " , osc / 1000 , osc % 1000 ) ;
2006-03-27 13:16:45 +04:00
}
static DEVICE_ATTR ( osc , S_IRUGO , rs5c372_sysfs_show_osc , NULL ) ;
2007-01-06 03:36:37 +03:00
static int rs5c_sysfs_register ( struct device * dev )
2006-03-27 13:16:45 +04:00
{
2007-01-06 03:36:37 +03:00
int err ;
err = device_create_file ( dev , & dev_attr_trim ) ;
if ( err )
return err ;
err = device_create_file ( dev , & dev_attr_osc ) ;
if ( err )
device_remove_file ( dev , & dev_attr_trim ) ;
return err ;
}
2007-07-17 15:04:55 +04:00
static void rs5c_sysfs_unregister ( struct device * dev )
{
device_remove_file ( dev , & dev_attr_trim ) ;
device_remove_file ( dev , & dev_attr_osc ) ;
}
2007-01-06 03:36:37 +03:00
# else
static int rs5c_sysfs_register ( struct device * dev )
{
return 0 ;
2006-03-27 13:16:45 +04:00
}
2007-07-17 15:04:55 +04:00
static void rs5c_sysfs_unregister ( struct device * dev )
{
/* nothing */
}
2007-01-06 03:36:37 +03:00
# endif /* SYSFS */
static struct i2c_driver rs5c372_driver ;
2006-03-27 13:16:45 +04:00
2008-10-16 09:03:01 +04:00
static int rs5c_oscillator_setup ( struct rs5c372 * rs5c372 )
{
unsigned char buf [ 2 ] ;
int addr , i , ret = 0 ;
2008-10-16 09:03:03 +04:00
if ( rs5c372 - > type = = rtc_r2025sd ) {
if ( ! ( rs5c372 - > regs [ RS5C_REG_CTRL2 ] & R2025_CTRL2_XST ) )
return ret ;
rs5c372 - > regs [ RS5C_REG_CTRL2 ] & = ~ R2025_CTRL2_XST ;
} else {
if ( ! ( rs5c372 - > regs [ RS5C_REG_CTRL2 ] & RS5C_CTRL2_XSTP ) )
return ret ;
rs5c372 - > regs [ RS5C_REG_CTRL2 ] & = ~ RS5C_CTRL2_XSTP ;
}
2008-10-16 09:03:01 +04:00
addr = RS5C_ADDR ( RS5C_REG_CTRL1 ) ;
buf [ 0 ] = rs5c372 - > regs [ RS5C_REG_CTRL1 ] ;
buf [ 1 ] = rs5c372 - > regs [ RS5C_REG_CTRL2 ] ;
/* use 24hr mode */
switch ( rs5c372 - > type ) {
case rtc_rs5c372a :
case rtc_rs5c372b :
buf [ 1 ] | = RS5C372_CTRL2_24 ;
rs5c372 - > time24 = 1 ;
break ;
2008-10-16 09:03:03 +04:00
case rtc_r2025sd :
2008-10-16 09:03:01 +04:00
case rtc_rv5c386 :
case rtc_rv5c387a :
buf [ 0 ] | = RV5C387_CTRL1_24 ;
rs5c372 - > time24 = 1 ;
break ;
default :
/* impossible */
break ;
}
for ( i = 0 ; i < sizeof ( buf ) ; i + + ) {
addr = RS5C_ADDR ( RS5C_REG_CTRL1 + i ) ;
ret = i2c_smbus_write_byte_data ( rs5c372 - > client , addr , buf [ i ] ) ;
if ( unlikely ( ret < 0 ) )
return ret ;
}
rs5c372 - > regs [ RS5C_REG_CTRL1 ] = buf [ 0 ] ;
rs5c372 - > regs [ RS5C_REG_CTRL2 ] = buf [ 1 ] ;
return 0 ;
}
2008-04-30 01:11:39 +04:00
static int rs5c372_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2006-03-27 13:16:45 +04:00
{
int err = 0 ;
2008-10-16 09:03:01 +04:00
int smbus_mode = 0 ;
2006-12-07 07:39:13 +03:00
struct rs5c372 * rs5c372 ;
2007-01-06 03:36:37 +03:00
struct rtc_time tm ;
2006-03-27 13:16:45 +04:00
2008-04-28 13:12:00 +04:00
dev_dbg ( & client - > dev , " %s \n " , __func__ ) ;
2006-03-27 13:16:45 +04:00
2008-10-16 09:03:01 +04:00
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK ) ) {
/*
* If we don ' t have any master mode adapter , try breaking
* it down in to the barest of capabilities .
*/
if ( i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK ) )
smbus_mode = 1 ;
else {
/* Still no good, give up */
err = - ENODEV ;
goto exit ;
}
2006-03-27 13:16:45 +04:00
}
2006-12-07 07:39:13 +03:00
if ( ! ( rs5c372 = kzalloc ( sizeof ( struct rs5c372 ) , GFP_KERNEL ) ) ) {
2006-03-27 13:16:45 +04:00
err = - ENOMEM ;
goto exit ;
}
2007-01-06 03:36:37 +03:00
rs5c372 - > client = client ;
2006-12-07 07:39:13 +03:00
i2c_set_clientdata ( client , rs5c372 ) ;
2008-04-30 01:11:40 +04:00
rs5c372 - > type = id - > driver_data ;
2006-12-07 07:39:13 +03:00
2008-04-28 13:11:57 +04:00
/* we read registers 0x0f then 0x00-0x0f; skip the first one */
rs5c372 - > regs = & rs5c372 - > buf [ 1 ] ;
2008-10-16 09:03:01 +04:00
rs5c372 - > smbus = smbus_mode ;
2008-04-28 13:11:57 +04:00
2007-01-06 03:36:37 +03:00
err = rs5c_get_regs ( rs5c372 ) ;
if ( err < 0 )
2007-07-17 15:04:55 +04:00
goto exit_kfree ;
2007-01-06 03:36:37 +03:00
/* clock may be set for am/pm or 24 hr time */
switch ( rs5c372 - > type ) {
case rtc_rs5c372a :
case rtc_rs5c372b :
/* alarm uses ALARM_A; and nINTRA on 372a, nINTR on 372b.
* so does periodic irq , except some 327 a modes .
*/
if ( rs5c372 - > regs [ RS5C_REG_CTRL2 ] & RS5C372_CTRL2_24 )
rs5c372 - > time24 = 1 ;
break ;
2008-10-16 09:03:03 +04:00
case rtc_r2025sd :
2007-01-06 03:36:37 +03:00
case rtc_rv5c386 :
case rtc_rv5c387a :
if ( rs5c372 - > regs [ RS5C_REG_CTRL1 ] & RV5C387_CTRL1_24 )
rs5c372 - > time24 = 1 ;
/* alarm uses ALARM_W; and nINTRB for alarm and periodic
* irq , on both 386 and 387
*/
break ;
default :
dev_err ( & client - > dev , " unknown RTC type \n " ) ;
2007-07-17 15:04:55 +04:00
goto exit_kfree ;
2007-01-06 03:36:37 +03:00
}
/* if the oscillator lost power and no other software (like
* the bootloader ) set it up , do it here .
2008-10-16 09:03:03 +04:00
*
* The R2025S / D does this a little differently than the other
* parts , so we special case that . .
2007-01-06 03:36:37 +03:00
*/
2008-10-16 09:03:01 +04:00
err = rs5c_oscillator_setup ( rs5c372 ) ;
if ( unlikely ( err < 0 ) ) {
dev_err ( & client - > dev , " setup error \n " ) ;
goto exit_kfree ;
2007-01-06 03:36:37 +03:00
}
if ( rs5c372_get_datetime ( client , & tm ) < 0 )
dev_warn ( & client - > dev , " clock needs to be set \n " ) ;
dev_info ( & client - > dev , " %s found, %s, driver version " DRV_VERSION " \n " ,
( { char * s ; switch ( rs5c372 - > type ) {
2008-10-16 09:03:03 +04:00
case rtc_r2025sd : s = " r2025sd " ; break ;
2007-01-06 03:36:37 +03:00
case rtc_rs5c372a : s = " rs5c372a " ; break ;
case rtc_rs5c372b : s = " rs5c372b " ; break ;
case rtc_rv5c386 : s = " rv5c386 " ; break ;
case rtc_rv5c387a : s = " rv5c387a " ; break ;
default : s = " chip " ; break ;
} ; s ; } ) ,
rs5c372 - > time24 ? " 24hr " : " am/pm "
) ;
2007-07-17 15:04:55 +04:00
/* REVISIT use client->irq to register alarm irq ... */
2006-03-27 13:16:45 +04:00
2006-12-07 07:39:13 +03:00
rs5c372 - > rtc = rtc_device_register ( rs5c372_driver . driver . name ,
& client - > dev , & rs5c372_rtc_ops , THIS_MODULE ) ;
2006-03-27 13:16:45 +04:00
2006-12-07 07:39:13 +03:00
if ( IS_ERR ( rs5c372 - > rtc ) ) {
err = PTR_ERR ( rs5c372 - > rtc ) ;
2007-07-17 15:04:55 +04:00
goto exit_kfree ;
2006-03-27 13:16:45 +04:00
}
2007-01-06 03:36:37 +03:00
err = rs5c_sysfs_register ( & client - > dev ) ;
2006-12-07 07:39:13 +03:00
if ( err )
goto exit_devreg ;
2006-03-27 13:16:45 +04:00
return 0 ;
2006-12-07 07:35:34 +03:00
exit_devreg :
2006-12-07 07:39:13 +03:00
rtc_device_unregister ( rs5c372 - > rtc ) ;
2006-12-07 07:35:34 +03:00
2006-03-27 13:16:45 +04:00
exit_kfree :
2006-12-07 07:39:13 +03:00
kfree ( rs5c372 ) ;
2006-03-27 13:16:45 +04:00
exit :
return err ;
}
2007-07-17 15:04:55 +04:00
static int rs5c372_remove ( struct i2c_client * client )
2007-01-06 03:36:37 +03:00
{
2006-12-07 07:39:13 +03:00
struct rs5c372 * rs5c372 = i2c_get_clientdata ( client ) ;
2006-03-27 13:16:45 +04:00
2007-07-17 15:04:55 +04:00
rtc_device_unregister ( rs5c372 - > rtc ) ;
rs5c_sysfs_unregister ( & client - > dev ) ;
2006-12-07 07:39:13 +03:00
kfree ( rs5c372 ) ;
2006-03-27 13:16:45 +04:00
return 0 ;
}
2007-01-06 03:36:37 +03:00
static struct i2c_driver rs5c372_driver = {
. driver = {
. name = " rtc-rs5c372 " ,
} ,
2007-07-17 15:04:55 +04:00
. probe = rs5c372_probe ,
. remove = rs5c372_remove ,
2008-04-30 01:11:40 +04:00
. id_table = rs5c372_id ,
2007-01-06 03:36:37 +03:00
} ;
2006-03-27 13:16:45 +04:00
static __init int rs5c372_init ( void )
{
return i2c_add_driver ( & rs5c372_driver ) ;
}
static __exit void rs5c372_exit ( void )
{
i2c_del_driver ( & rs5c372_driver ) ;
}
module_init ( rs5c372_init ) ;
module_exit ( rs5c372_exit ) ;
MODULE_AUTHOR (
" Pavel Mironchik <pmironchik@optifacio.net>, "
2008-10-16 09:03:01 +04:00
" Alessandro Zummo <a.zummo@towertech.it>, "
" Paul Mundt <lethal@linux-sh.org> " ) ;
2006-03-27 13:16:45 +04:00
MODULE_DESCRIPTION ( " Ricoh RS5C372 RTC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRV_VERSION ) ;