2007-05-08 11:26:39 +04:00
/*
* rtc class driver for the Maxim MAX6900 chip
*
* Author : Dale Farnsworth < dale @ farnsworth . org >
*
* based on previously existing rtc class drivers
*
* 2007 ( c ) MontaVista , Software , Inc . This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed " as is " without any warranty of any kind , whether express
* or implied .
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/bcd.h>
# include <linux/rtc.h>
# include <linux/delay.h>
2008-10-16 09:03:08 +04:00
# define DRV_VERSION "0.2"
2007-05-08 11:26:39 +04:00
/*
* register indices
*/
2008-10-16 09:03:08 +04:00
# define MAX6900_REG_SC 0 /* seconds 00-59 */
# define MAX6900_REG_MN 1 /* minutes 00-59 */
# define MAX6900_REG_HR 2 /* hours 00-23 */
# define MAX6900_REG_DT 3 /* day of month 00-31 */
# define MAX6900_REG_MO 4 /* month 01-12 */
# define MAX6900_REG_DW 5 /* day of week 1-7 */
# define MAX6900_REG_YR 6 /* year 00-99 */
2007-05-08 11:26:39 +04:00
# define MAX6900_REG_CT 7 /* control */
2007-07-21 15:37:57 +04:00
/* register 8 is undocumented */
# define MAX6900_REG_CENTURY 9 /* century */
# define MAX6900_REG_LEN 10
# define MAX6900_BURST_LEN 8 /* can burst r/w first 8 regs */
2007-05-08 11:26:39 +04:00
# define MAX6900_REG_CT_WP (1 << 7) /* Write Protect */
/*
* register read / write commands
*/
# define MAX6900_REG_CONTROL_WRITE 0x8e
2007-07-21 15:37:57 +04:00
# define MAX6900_REG_CENTURY_WRITE 0x92
# define MAX6900_REG_CENTURY_READ 0x93
2007-05-08 11:26:39 +04:00
# define MAX6900_REG_RESERVED_READ 0x96
2007-07-21 15:37:57 +04:00
# define MAX6900_REG_BURST_WRITE 0xbe
# define MAX6900_REG_BURST_READ 0xbf
2007-05-08 11:26:39 +04:00
# define MAX6900_IDLE_TIME_AFTER_WRITE 3 /* specification says 2.5 mS */
2008-10-16 09:03:08 +04:00
static struct i2c_driver max6900_driver ;
2007-05-08 11:26:39 +04:00
static int max6900_i2c_read_regs ( struct i2c_client * client , u8 * buf )
{
2007-07-21 15:37:57 +04:00
u8 reg_burst_read [ 1 ] = { MAX6900_REG_BURST_READ } ;
u8 reg_century_read [ 1 ] = { MAX6900_REG_CENTURY_READ } ;
struct i2c_msg msgs [ 4 ] = {
2007-05-08 11:26:39 +04:00
{
2008-10-16 09:03:08 +04:00
. addr = client - > addr ,
. flags = 0 , /* write */
. len = sizeof ( reg_burst_read ) ,
. buf = reg_burst_read }
,
2007-05-08 11:26:39 +04:00
{
2008-10-16 09:03:08 +04:00
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = MAX6900_BURST_LEN ,
. buf = buf }
,
2007-07-21 15:37:57 +04:00
{
2008-10-16 09:03:08 +04:00
. addr = client - > addr ,
. flags = 0 , /* write */
. len = sizeof ( reg_century_read ) ,
. buf = reg_century_read }
,
2007-07-21 15:37:57 +04:00
{
2008-10-16 09:03:08 +04:00
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = sizeof ( buf [ MAX6900_REG_CENTURY ] ) ,
. buf = & buf [ MAX6900_REG_CENTURY ]
}
2007-05-08 11:26:39 +04:00
} ;
int rc ;
rc = i2c_transfer ( client - > adapter , msgs , ARRAY_SIZE ( msgs ) ) ;
if ( rc ! = ARRAY_SIZE ( msgs ) ) {
2008-10-16 09:03:08 +04:00
dev_err ( & client - > dev , " %s: register read failed \n " , __func__ ) ;
2007-05-08 11:26:39 +04:00
return - EIO ;
}
return 0 ;
}
static int max6900_i2c_write_regs ( struct i2c_client * client , u8 const * buf )
{
2007-07-21 15:37:57 +04:00
u8 i2c_century_buf [ 1 + 1 ] = { MAX6900_REG_CENTURY_WRITE } ;
struct i2c_msg century_msgs [ 1 ] = {
2007-05-08 11:26:39 +04:00
{
2008-10-16 09:03:08 +04:00
. addr = client - > addr ,
. flags = 0 , /* write */
. len = sizeof ( i2c_century_buf ) ,
. buf = i2c_century_buf }
2007-07-21 15:37:57 +04:00
} ;
u8 i2c_burst_buf [ MAX6900_BURST_LEN + 1 ] = { MAX6900_REG_BURST_WRITE } ;
struct i2c_msg burst_msgs [ 1 ] = {
{
2008-10-16 09:03:08 +04:00
. addr = client - > addr ,
. flags = 0 , /* write */
. len = sizeof ( i2c_burst_buf ) ,
. buf = i2c_burst_buf }
2007-05-08 11:26:39 +04:00
} ;
int rc ;
2007-07-21 15:37:57 +04:00
/*
* We have to make separate calls to i2c_transfer because of
* the need to delay after each write to the chip . Also ,
* we write the century byte first , since we set the write - protect
* bit as part of the burst write .
*/
i2c_century_buf [ 1 ] = buf [ MAX6900_REG_CENTURY ] ;
2008-10-16 09:03:08 +04:00
2007-07-21 15:37:57 +04:00
rc = i2c_transfer ( client - > adapter , century_msgs ,
ARRAY_SIZE ( century_msgs ) ) ;
if ( rc ! = ARRAY_SIZE ( century_msgs ) )
goto write_failed ;
2008-10-16 09:03:08 +04:00
2007-07-21 15:37:57 +04:00
msleep ( MAX6900_IDLE_TIME_AFTER_WRITE ) ;
2007-05-08 11:26:39 +04:00
2007-07-21 15:37:57 +04:00
memcpy ( & i2c_burst_buf [ 1 ] , buf , MAX6900_BURST_LEN ) ;
rc = i2c_transfer ( client - > adapter , burst_msgs , ARRAY_SIZE ( burst_msgs ) ) ;
if ( rc ! = ARRAY_SIZE ( burst_msgs ) )
goto write_failed ;
2007-05-08 11:26:39 +04:00
msleep ( MAX6900_IDLE_TIME_AFTER_WRITE ) ;
2007-07-21 15:37:57 +04:00
2007-05-08 11:26:39 +04:00
return 0 ;
2007-07-21 15:37:57 +04:00
2008-10-16 09:03:08 +04:00
write_failed :
dev_err ( & client - > dev , " %s: register write failed \n " , __func__ ) ;
2007-07-21 15:37:57 +04:00
return - EIO ;
2007-05-08 11:26:39 +04:00
}
static int max6900_i2c_read_time ( struct i2c_client * client , struct rtc_time * tm )
{
int rc ;
u8 regs [ MAX6900_REG_LEN ] ;
rc = max6900_i2c_read_regs ( client , regs ) ;
if ( rc < 0 )
return rc ;
2008-10-19 07:28:41 +04:00
tm - > tm_sec = bcd2bin ( regs [ MAX6900_REG_SC ] ) ;
tm - > tm_min = bcd2bin ( regs [ MAX6900_REG_MN ] ) ;
tm - > tm_hour = bcd2bin ( regs [ MAX6900_REG_HR ] & 0x3f ) ;
tm - > tm_mday = bcd2bin ( regs [ MAX6900_REG_DT ] ) ;
tm - > tm_mon = bcd2bin ( regs [ MAX6900_REG_MO ] ) - 1 ;
tm - > tm_year = bcd2bin ( regs [ MAX6900_REG_YR ] ) +
bcd2bin ( regs [ MAX6900_REG_CENTURY ] ) * 100 - 1900 ;
tm - > tm_wday = bcd2bin ( regs [ MAX6900_REG_DW ] ) ;
2007-05-08 11:26:39 +04:00
return 0 ;
}
static int max6900_i2c_clear_write_protect ( struct i2c_client * client )
{
int rc ;
2008-10-16 09:03:08 +04:00
rc = i2c_smbus_write_byte_data ( client , MAX6900_REG_CONTROL_WRITE , 0 ) ;
2007-05-08 11:26:39 +04:00
if ( rc < 0 ) {
dev_err ( & client - > dev , " %s: control register write failed \n " ,
2008-04-28 13:12:00 +04:00
__func__ ) ;
2007-05-08 11:26:39 +04:00
return - EIO ;
}
return 0 ;
}
2008-10-16 09:03:08 +04:00
static int
max6900_i2c_set_time ( struct i2c_client * client , struct rtc_time const * tm )
2007-05-08 11:26:39 +04:00
{
u8 regs [ MAX6900_REG_LEN ] ;
int rc ;
rc = max6900_i2c_clear_write_protect ( client ) ;
if ( rc < 0 )
return rc ;
2008-10-19 07:28:41 +04:00
regs [ MAX6900_REG_SC ] = bin2bcd ( tm - > tm_sec ) ;
regs [ MAX6900_REG_MN ] = bin2bcd ( tm - > tm_min ) ;
regs [ MAX6900_REG_HR ] = bin2bcd ( tm - > tm_hour ) ;
regs [ MAX6900_REG_DT ] = bin2bcd ( tm - > tm_mday ) ;
regs [ MAX6900_REG_MO ] = bin2bcd ( tm - > tm_mon + 1 ) ;
regs [ MAX6900_REG_DW ] = bin2bcd ( tm - > tm_wday ) ;
regs [ MAX6900_REG_YR ] = bin2bcd ( tm - > tm_year % 100 ) ;
regs [ MAX6900_REG_CENTURY ] = bin2bcd ( ( tm - > tm_year + 1900 ) / 100 ) ;
2007-07-21 15:37:57 +04:00
/* set write protect */
regs [ MAX6900_REG_CT ] = MAX6900_REG_CT_WP ;
2007-05-08 11:26:39 +04:00
rc = max6900_i2c_write_regs ( client , regs ) ;
if ( rc < 0 )
return rc ;
return 0 ;
}
static int max6900_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
return max6900_i2c_read_time ( to_i2c_client ( dev ) , tm ) ;
}
static int max6900_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
return max6900_i2c_set_time ( to_i2c_client ( dev ) , tm ) ;
}
2008-10-16 09:03:08 +04:00
static int max6900_remove ( struct i2c_client * client )
2007-05-08 11:26:39 +04:00
{
2008-10-16 09:03:08 +04:00
struct rtc_device * rtc = i2c_get_clientdata ( client ) ;
2007-05-08 11:26:39 +04:00
if ( rtc )
rtc_device_unregister ( rtc ) ;
2008-10-16 09:03:08 +04:00
return 0 ;
2007-05-08 11:26:39 +04:00
}
static const struct rtc_class_ops max6900_rtc_ops = {
2008-10-16 09:03:08 +04:00
. read_time = max6900_rtc_read_time ,
. set_time = max6900_rtc_set_time ,
2007-05-08 11:26:39 +04:00
} ;
2008-10-16 09:03:08 +04:00
static int
max6900_probe ( struct i2c_client * client , const struct i2c_device_id * id )
2007-05-08 11:26:39 +04:00
{
2008-10-16 09:03:08 +04:00
struct rtc_device * rtc ;
2007-05-08 11:26:39 +04:00
2008-10-16 09:03:08 +04:00
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) )
return - ENODEV ;
2007-05-08 11:26:39 +04:00
2008-10-16 09:03:08 +04:00
dev_info ( & client - > dev , " chip found, driver version " DRV_VERSION " \n " ) ;
2007-05-08 11:26:39 +04:00
rtc = rtc_device_register ( max6900_driver . driver . name ,
2008-10-16 09:03:08 +04:00
& client - > dev , & max6900_rtc_ops , THIS_MODULE ) ;
if ( IS_ERR ( rtc ) )
return PTR_ERR ( rtc ) ;
2007-05-08 11:26:39 +04:00
i2c_set_clientdata ( client , rtc ) ;
return 0 ;
}
2008-12-10 00:14:11 +03:00
static struct i2c_device_id max6900_id [ ] = {
{ " max6900 " , 0 } ,
{ }
} ;
2008-10-16 09:03:08 +04:00
static struct i2c_driver max6900_driver = {
. driver = {
. name = " rtc-max6900 " ,
} ,
. probe = max6900_probe ,
. remove = max6900_remove ,
2008-12-10 00:14:11 +03:00
. id_table = max6900_id ,
2008-10-16 09:03:08 +04:00
} ;
2007-05-08 11:26:39 +04:00
static int __init max6900_init ( void )
{
return i2c_add_driver ( & max6900_driver ) ;
}
static void __exit max6900_exit ( void )
{
i2c_del_driver ( & max6900_driver ) ;
}
MODULE_DESCRIPTION ( " Maxim MAX6900 RTC driver " ) ;
2008-10-16 09:03:08 +04:00
MODULE_AUTHOR ( " Dale Farnsworth <dale@farnsworth.org> " ) ;
2007-05-08 11:26:39 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRV_VERSION ) ;
module_init ( max6900_init ) ;
module_exit ( max6900_exit ) ;