2005-04-17 02:20:36 +04:00
/*
* linux / drivers / i2c / chips / ds1337 . c
*
* Copyright ( C ) 2005 James Chapman < jchapman @ katalix . com >
*
2005-04-08 17:00:21 +04:00
* based on linux / drivers / acorn / char / pcf8583 . c
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 2000 Russell King
*
* 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 .
*
2005-05-10 16:08:04 +04:00
* Driver for Dallas Semiconductor DS1337 and DS1339 real time clock chip
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/string.h>
# include <linux/rtc.h> /* get the user-level API */
# include <linux/bcd.h>
# include <linux/list.h>
/* Device registers */
# define DS1337_REG_HOUR 2
# define DS1337_REG_DAY 3
# define DS1337_REG_DATE 4
# define DS1337_REG_MONTH 5
# define DS1337_REG_CONTROL 14
# define DS1337_REG_STATUS 15
/* FIXME - how do we export these interface constants? */
# define DS1337_GET_DATE 0
# define DS1337_SET_DATE 1
/*
* Functions declaration
*/
static unsigned short normal_i2c [ ] = { 0x68 , I2C_CLIENT_END } ;
2005-07-31 23:49:03 +04:00
I2C_CLIENT_INSMOD_1 ( ds1337 ) ;
2005-04-17 02:20:36 +04:00
static int ds1337_attach_adapter ( struct i2c_adapter * adapter ) ;
static int ds1337_detect ( struct i2c_adapter * adapter , int address , int kind ) ;
static void ds1337_init_client ( struct i2c_client * client ) ;
static int ds1337_detach_client ( struct i2c_client * client ) ;
static int ds1337_command ( struct i2c_client * client , unsigned int cmd ,
void * arg ) ;
/*
* Driver data ( common to all clients )
*/
static struct i2c_driver ds1337_driver = {
2005-11-26 22:36:00 +03:00
. driver = {
. name = " ds1337 " ,
} ,
2005-04-17 02:20:36 +04:00
. attach_adapter = ds1337_attach_adapter ,
. detach_client = ds1337_detach_client ,
. command = ds1337_command ,
} ;
/*
* Client data ( each client gets its own )
*/
struct ds1337_data {
struct i2c_client client ;
struct list_head list ;
} ;
/*
* Internal variables
*/
static LIST_HEAD ( ds1337_clients ) ;
static inline int ds1337_read ( struct i2c_client * client , u8 reg , u8 * value )
{
s32 tmp = i2c_smbus_read_byte_data ( client , reg ) ;
if ( tmp < 0 )
return - EIO ;
* value = tmp ;
return 0 ;
}
/*
* Chip access functions
*/
static int ds1337_get_datetime ( struct i2c_client * client , struct rtc_time * dt )
{
int result ;
u8 buf [ 7 ] ;
u8 val ;
struct i2c_msg msg [ 2 ] ;
u8 offs = 0 ;
if ( ! dt ) {
2005-04-08 17:06:39 +04:00
dev_dbg ( & client - > dev , " %s: EINVAL: dt=NULL \n " , __FUNCTION__ ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
msg [ 0 ] . addr = client - > addr ;
msg [ 0 ] . flags = 0 ;
msg [ 0 ] . len = 1 ;
msg [ 0 ] . buf = & offs ;
msg [ 1 ] . addr = client - > addr ;
msg [ 1 ] . flags = I2C_M_RD ;
msg [ 1 ] . len = sizeof ( buf ) ;
msg [ 1 ] . buf = & buf [ 0 ] ;
2005-04-08 17:00:21 +04:00
result = i2c_transfer ( client - > adapter , msg , 2 ) ;
2005-04-17 02:20:36 +04:00
2005-04-08 17:06:39 +04:00
dev_dbg ( & client - > dev , " %s: [%d] %02x %02x %02x %02x %02x %02x %02x \n " ,
2005-04-17 02:20:36 +04:00
__FUNCTION__ , result , buf [ 0 ] , buf [ 1 ] , buf [ 2 ] , buf [ 3 ] ,
buf [ 4 ] , buf [ 5 ] , buf [ 6 ] ) ;
2005-05-04 10:13:54 +04:00
if ( result = = 2 ) {
2005-04-08 17:02:16 +04:00
dt - > tm_sec = BCD2BIN ( buf [ 0 ] ) ;
dt - > tm_min = BCD2BIN ( buf [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
val = buf [ 2 ] & 0x3f ;
2005-04-08 17:02:16 +04:00
dt - > tm_hour = BCD2BIN ( val ) ;
dt - > tm_wday = BCD2BIN ( buf [ 3 ] ) - 1 ;
dt - > tm_mday = BCD2BIN ( buf [ 4 ] ) ;
2005-04-17 02:20:36 +04:00
val = buf [ 5 ] & 0x7f ;
2005-05-04 10:13:13 +04:00
dt - > tm_mon = BCD2BIN ( val ) - 1 ;
dt - > tm_year = BCD2BIN ( buf [ 6 ] ) ;
2005-04-17 02:20:36 +04:00
if ( buf [ 5 ] & 0x80 )
dt - > tm_year + = 100 ;
2005-04-08 17:06:39 +04:00
dev_dbg ( & client - > dev , " %s: secs=%d, mins=%d, "
2005-04-17 02:20:36 +04:00
" hours=%d, mday=%d, mon=%d, year=%d, wday=%d \n " ,
__FUNCTION__ , dt - > tm_sec , dt - > tm_min ,
dt - > tm_hour , dt - > tm_mday ,
dt - > tm_mon , dt - > tm_year , dt - > tm_wday ) ;
2005-05-04 10:13:54 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-05-04 10:13:54 +04:00
dev_err ( & client - > dev , " error reading data! %d \n " , result ) ;
return - EIO ;
2005-04-17 02:20:36 +04:00
}
static int ds1337_set_datetime ( struct i2c_client * client , struct rtc_time * dt )
{
int result ;
u8 buf [ 8 ] ;
u8 val ;
struct i2c_msg msg [ 1 ] ;
if ( ! dt ) {
2005-04-08 17:06:39 +04:00
dev_dbg ( & client - > dev , " %s: EINVAL: dt=NULL \n " , __FUNCTION__ ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2005-04-08 17:06:39 +04:00
dev_dbg ( & client - > dev , " %s: secs=%d, mins=%d, hours=%d, "
2005-04-17 02:20:36 +04:00
" mday=%d, mon=%d, year=%d, wday=%d \n " , __FUNCTION__ ,
dt - > tm_sec , dt - > tm_min , dt - > tm_hour ,
dt - > tm_mday , dt - > tm_mon , dt - > tm_year , dt - > tm_wday ) ;
buf [ 0 ] = 0 ; /* reg offset */
2005-04-08 17:02:16 +04:00
buf [ 1 ] = BIN2BCD ( dt - > tm_sec ) ;
buf [ 2 ] = BIN2BCD ( dt - > tm_min ) ;
2005-07-29 23:15:00 +04:00
buf [ 3 ] = BIN2BCD ( dt - > tm_hour ) ;
2005-11-07 01:07:38 +03:00
buf [ 4 ] = BIN2BCD ( dt - > tm_wday + 1 ) ;
2005-04-08 17:02:16 +04:00
buf [ 5 ] = BIN2BCD ( dt - > tm_mday ) ;
2005-11-07 01:07:38 +03:00
buf [ 6 ] = BIN2BCD ( dt - > tm_mon + 1 ) ;
2005-05-04 10:13:13 +04:00
val = dt - > tm_year ;
if ( val > = 100 ) {
val - = 100 ;
2005-04-17 02:20:36 +04:00
buf [ 6 ] | = ( 1 < < 7 ) ;
}
2005-04-08 17:02:16 +04:00
buf [ 7 ] = BIN2BCD ( val ) ;
2005-04-17 02:20:36 +04:00
msg [ 0 ] . addr = client - > addr ;
msg [ 0 ] . flags = 0 ;
msg [ 0 ] . len = sizeof ( buf ) ;
msg [ 0 ] . buf = & buf [ 0 ] ;
2005-04-08 17:00:21 +04:00
result = i2c_transfer ( client - > adapter , msg , 1 ) ;
2005-05-04 10:13:54 +04:00
if ( result = = 1 )
return 0 ;
2005-04-17 02:20:36 +04:00
2005-05-04 10:13:54 +04:00
dev_err ( & client - > dev , " error writing data! %d \n " , result ) ;
return - EIO ;
2005-04-17 02:20:36 +04:00
}
static int ds1337_command ( struct i2c_client * client , unsigned int cmd ,
void * arg )
{
2005-04-08 17:06:39 +04:00
dev_dbg ( & client - > dev , " %s: cmd=%d \n " , __FUNCTION__ , cmd ) ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case DS1337_GET_DATE :
return ds1337_get_datetime ( client , arg ) ;
case DS1337_SET_DATE :
return ds1337_set_datetime ( client , arg ) ;
default :
return - EINVAL ;
}
}
/*
* Public API for access to specific device . Useful for low - level
* RTC access from kernel code .
*/
2005-05-04 10:14:38 +04:00
int ds1337_do_command ( int bus , int cmd , void * arg )
2005-04-17 02:20:36 +04:00
{
struct list_head * walk ;
struct list_head * tmp ;
struct ds1337_data * data ;
list_for_each_safe ( walk , tmp , & ds1337_clients ) {
data = list_entry ( walk , struct ds1337_data , list ) ;
2005-05-04 10:14:38 +04:00
if ( data - > client . adapter - > nr = = bus )
2005-04-17 02:20:36 +04:00
return ds1337_command ( & data - > client , cmd , arg ) ;
}
return - ENODEV ;
}
static int ds1337_attach_adapter ( struct i2c_adapter * adapter )
{
2005-07-31 23:42:02 +04:00
return i2c_probe ( adapter , & addr_data , ds1337_detect ) ;
2005-04-17 02:20:36 +04:00
}
/*
* The following function does more than just detection . If detection
* succeeds , it also registers the new chip .
*/
static int ds1337_detect ( struct i2c_adapter * adapter , int address , int kind )
{
struct i2c_client * new_client ;
struct ds1337_data * data ;
int err = 0 ;
const char * name = " " ;
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_I2C ) )
goto exit ;
2005-10-18 01:09:43 +04:00
if ( ! ( data = kzalloc ( sizeof ( struct ds1337_data ) , GFP_KERNEL ) ) ) {
2005-04-17 02:20:36 +04:00
err = - ENOMEM ;
goto exit ;
}
INIT_LIST_HEAD ( & data - > list ) ;
/* The common I2C client data is placed right before the
* DS1337 - specific data .
*/
new_client = & data - > client ;
i2c_set_clientdata ( new_client , data ) ;
new_client - > addr = address ;
new_client - > adapter = adapter ;
new_client - > driver = & ds1337_driver ;
new_client - > flags = 0 ;
/*
* Now we do the remaining detection . A negative kind means that
* the driver was loaded with no force parameter ( default ) , so we
* must both detect and identify the chip . A zero kind means that
* the driver was loaded with the force parameter , the detection
* step shall be skipped . A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested , so both the detection and the identification steps
* are skipped .
*
* For detection , we read registers that are most likely to cause
* detection failure , i . e . those that have more bits with fixed
* or reserved values .
*/
/* Default to an DS1337 if forced */
if ( kind = = 0 )
kind = ds1337 ;
if ( kind < 0 ) { /* detection and identification */
u8 data ;
/* Check that status register bits 6-2 are zero */
if ( ( ds1337_read ( new_client , DS1337_REG_STATUS , & data ) < 0 ) | |
( data & 0x7c ) )
goto exit_free ;
/* Check for a valid day register value */
if ( ( ds1337_read ( new_client , DS1337_REG_DAY , & data ) < 0 ) | |
( data = = 0 ) | | ( data & 0xf8 ) )
goto exit_free ;
/* Check for a valid date register value */
if ( ( ds1337_read ( new_client , DS1337_REG_DATE , & data ) < 0 ) | |
( data = = 0 ) | | ( data & 0xc0 ) | | ( ( data & 0x0f ) > 9 ) | |
( data > = 0x32 ) )
goto exit_free ;
/* Check for a valid month register value */
if ( ( ds1337_read ( new_client , DS1337_REG_MONTH , & data ) < 0 ) | |
( data = = 0 ) | | ( data & 0x60 ) | | ( ( data & 0x0f ) > 9 ) | |
( ( data > = 0x13 ) & & ( data < = 0x19 ) ) )
goto exit_free ;
/* Check that control register bits 6-5 are zero */
if ( ( ds1337_read ( new_client , DS1337_REG_CONTROL , & data ) < 0 ) | |
( data & 0x60 ) )
goto exit_free ;
kind = ds1337 ;
}
if ( kind = = ds1337 )
name = " ds1337 " ;
/* We can fill in the remaining client fields */
strlcpy ( new_client - > name , name , I2C_NAME_SIZE ) ;
/* Tell the I2C layer a new client has arrived */
if ( ( err = i2c_attach_client ( new_client ) ) )
goto exit_free ;
/* Initialize the DS1337 chip */
ds1337_init_client ( new_client ) ;
/* Add client to local list */
list_add ( & data - > list , & ds1337_clients ) ;
return 0 ;
exit_free :
kfree ( data ) ;
exit :
return err ;
}
static void ds1337_init_client ( struct i2c_client * client )
{
2005-11-08 00:30:14 +03:00
u8 status , control ;
2005-04-17 02:20:36 +04:00
2005-11-08 00:30:14 +03:00
/* On some boards, the RTC isn't configured by boot firmware.
* Handle that case by starting / configuring the RTC now .
*/
status = i2c_smbus_read_byte_data ( client , DS1337_REG_STATUS ) ;
control = i2c_smbus_read_byte_data ( client , DS1337_REG_CONTROL ) ;
if ( ( status & 0x80 ) | | ( control & 0x80 ) ) {
/* RTC not running */
u8 buf [ 16 ] ;
struct i2c_msg msg [ 1 ] ;
dev_dbg ( & client - > dev , " %s: RTC not running! \n " , __FUNCTION__ ) ;
/* Initialize all, including STATUS and CONTROL to zero */
memset ( buf , 0 , sizeof ( buf ) ) ;
msg [ 0 ] . addr = client - > addr ;
msg [ 0 ] . flags = 0 ;
msg [ 0 ] . len = sizeof ( buf ) ;
msg [ 0 ] . buf = & buf [ 0 ] ;
i2c_transfer ( client - > adapter , msg , 1 ) ;
} else {
/* Running: ensure that device is set in 24-hour mode */
s32 val ;
val = i2c_smbus_read_byte_data ( client , DS1337_REG_HOUR ) ;
if ( ( val > = 0 ) & & ( val & ( 1 < < 6 ) ) )
i2c_smbus_write_byte_data ( client , DS1337_REG_HOUR ,
val & 0x3f ) ;
}
2005-04-17 02:20:36 +04:00
}
static int ds1337_detach_client ( struct i2c_client * client )
{
int err ;
struct ds1337_data * data = i2c_get_clientdata ( client ) ;
2005-07-28 00:14:49 +04:00
if ( ( err = i2c_detach_client ( client ) ) )
2005-04-17 02:20:36 +04:00
return err ;
list_del ( & data - > list ) ;
kfree ( data ) ;
return 0 ;
}
static int __init ds1337_init ( void )
{
return i2c_add_driver ( & ds1337_driver ) ;
}
static void __exit ds1337_exit ( void )
{
i2c_del_driver ( & ds1337_driver ) ;
}
MODULE_AUTHOR ( " James Chapman <jchapman@katalix.com> " ) ;
MODULE_DESCRIPTION ( " DS1337 RTC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-05-11 12:32:54 +04:00
EXPORT_SYMBOL_GPL ( ds1337_do_command ) ;
2005-04-17 02:20:36 +04:00
module_init ( ds1337_init ) ;
module_exit ( ds1337_exit ) ;