2012-10-05 04:13:47 +04:00
/*
* Copyright ( C ) 2012 Sven Schnelle < svens @ stackframe . org >
*
* 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/platform_device.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/rtc.h>
# include <linux/types.h>
# include <linux/bcd.h>
2016-06-27 00:38:44 +03:00
# include <linux/platform_data/rtc-ds2404.h>
2012-10-05 04:13:47 +04:00
# include <linux/delay.h>
# include <linux/gpio.h>
# include <linux/slab.h>
# include <linux/io.h>
# define DS2404_STATUS_REG 0x200
# define DS2404_CONTROL_REG 0x201
# define DS2404_RTC_REG 0x202
# define DS2404_WRITE_SCRATCHPAD_CMD 0x0f
# define DS2404_READ_SCRATCHPAD_CMD 0xaa
# define DS2404_COPY_SCRATCHPAD_CMD 0x55
# define DS2404_READ_MEMORY_CMD 0xf0
struct ds2404 ;
struct ds2404_chip_ops {
int ( * map_io ) ( struct ds2404 * chip , struct platform_device * pdev ,
struct ds2404_platform_data * pdata ) ;
void ( * unmap_io ) ( struct ds2404 * chip ) ;
} ;
# define DS2404_RST 0
# define DS2404_CLK 1
# define DS2404_DQ 2
struct ds2404_gpio {
const char * name ;
unsigned int gpio ;
} ;
struct ds2404 {
struct ds2404_gpio * gpio ;
2015-12-30 23:59:34 +03:00
const struct ds2404_chip_ops * ops ;
2012-10-05 04:13:47 +04:00
struct rtc_device * rtc ;
} ;
static struct ds2404_gpio ds2404_gpio [ ] = {
{ " RTC RST " , 0 } ,
{ " RTC CLK " , 0 } ,
{ " RTC DQ " , 0 } ,
} ;
static int ds2404_gpio_map ( struct ds2404 * chip , struct platform_device * pdev ,
struct ds2404_platform_data * pdata )
{
int i , err ;
ds2404_gpio [ DS2404_RST ] . gpio = pdata - > gpio_rst ;
ds2404_gpio [ DS2404_CLK ] . gpio = pdata - > gpio_clk ;
ds2404_gpio [ DS2404_DQ ] . gpio = pdata - > gpio_dq ;
for ( i = 0 ; i < ARRAY_SIZE ( ds2404_gpio ) ; i + + ) {
err = gpio_request ( ds2404_gpio [ i ] . gpio , ds2404_gpio [ i ] . name ) ;
if ( err ) {
2013-02-22 04:45:26 +04:00
dev_err ( & pdev - > dev , " error mapping gpio %s: %d \n " ,
2012-10-05 04:13:47 +04:00
ds2404_gpio [ i ] . name , err ) ;
goto err_request ;
}
if ( i ! = DS2404_DQ )
gpio_direction_output ( ds2404_gpio [ i ] . gpio , 1 ) ;
}
chip - > gpio = ds2404_gpio ;
return 0 ;
err_request :
while ( - - i > = 0 )
gpio_free ( ds2404_gpio [ i ] . gpio ) ;
return err ;
}
static void ds2404_gpio_unmap ( struct ds2404 * chip )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( ds2404_gpio ) ; i + + )
gpio_free ( ds2404_gpio [ i ] . gpio ) ;
}
2015-12-30 23:59:34 +03:00
static const struct ds2404_chip_ops ds2404_gpio_ops = {
2012-10-05 04:13:47 +04:00
. map_io = ds2404_gpio_map ,
. unmap_io = ds2404_gpio_unmap ,
} ;
static void ds2404_reset ( struct device * dev )
{
gpio_set_value ( ds2404_gpio [ DS2404_RST ] . gpio , 0 ) ;
udelay ( 1000 ) ;
gpio_set_value ( ds2404_gpio [ DS2404_RST ] . gpio , 1 ) ;
gpio_set_value ( ds2404_gpio [ DS2404_CLK ] . gpio , 0 ) ;
gpio_direction_output ( ds2404_gpio [ DS2404_DQ ] . gpio , 0 ) ;
udelay ( 10 ) ;
}
static void ds2404_write_byte ( struct device * dev , u8 byte )
{
int i ;
gpio_direction_output ( ds2404_gpio [ DS2404_DQ ] . gpio , 1 ) ;
for ( i = 0 ; i < 8 ; i + + ) {
gpio_set_value ( ds2404_gpio [ DS2404_DQ ] . gpio , byte & ( 1 < < i ) ) ;
udelay ( 10 ) ;
gpio_set_value ( ds2404_gpio [ DS2404_CLK ] . gpio , 1 ) ;
udelay ( 10 ) ;
gpio_set_value ( ds2404_gpio [ DS2404_CLK ] . gpio , 0 ) ;
udelay ( 10 ) ;
}
}
static u8 ds2404_read_byte ( struct device * dev )
{
int i ;
u8 ret = 0 ;
gpio_direction_input ( ds2404_gpio [ DS2404_DQ ] . gpio ) ;
for ( i = 0 ; i < 8 ; i + + ) {
gpio_set_value ( ds2404_gpio [ DS2404_CLK ] . gpio , 0 ) ;
udelay ( 10 ) ;
if ( gpio_get_value ( ds2404_gpio [ DS2404_DQ ] . gpio ) )
ret | = 1 < < i ;
gpio_set_value ( ds2404_gpio [ DS2404_CLK ] . gpio , 1 ) ;
udelay ( 10 ) ;
}
return ret ;
}
static void ds2404_read_memory ( struct device * dev , u16 offset ,
int length , u8 * out )
{
ds2404_reset ( dev ) ;
ds2404_write_byte ( dev , DS2404_READ_MEMORY_CMD ) ;
ds2404_write_byte ( dev , offset & 0xff ) ;
ds2404_write_byte ( dev , ( offset > > 8 ) & 0xff ) ;
while ( length - - )
* out + + = ds2404_read_byte ( dev ) ;
}
static void ds2404_write_memory ( struct device * dev , u16 offset ,
int length , u8 * out )
{
int i ;
u8 ta01 , ta02 , es ;
ds2404_reset ( dev ) ;
ds2404_write_byte ( dev , DS2404_WRITE_SCRATCHPAD_CMD ) ;
ds2404_write_byte ( dev , offset & 0xff ) ;
ds2404_write_byte ( dev , ( offset > > 8 ) & 0xff ) ;
for ( i = 0 ; i < length ; i + + )
ds2404_write_byte ( dev , out [ i ] ) ;
ds2404_reset ( dev ) ;
ds2404_write_byte ( dev , DS2404_READ_SCRATCHPAD_CMD ) ;
ta01 = ds2404_read_byte ( dev ) ;
ta02 = ds2404_read_byte ( dev ) ;
es = ds2404_read_byte ( dev ) ;
for ( i = 0 ; i < length ; i + + ) {
if ( out [ i ] ! = ds2404_read_byte ( dev ) ) {
2013-02-22 04:45:26 +04:00
dev_err ( dev , " read invalid data \n " ) ;
2012-10-05 04:13:47 +04:00
return ;
}
}
ds2404_reset ( dev ) ;
ds2404_write_byte ( dev , DS2404_COPY_SCRATCHPAD_CMD ) ;
ds2404_write_byte ( dev , ta01 ) ;
ds2404_write_byte ( dev , ta02 ) ;
ds2404_write_byte ( dev , es ) ;
gpio_direction_input ( ds2404_gpio [ DS2404_DQ ] . gpio ) ;
while ( gpio_get_value ( ds2404_gpio [ DS2404_DQ ] . gpio ) )
;
}
static void ds2404_enable_osc ( struct device * dev )
{
u8 in [ 1 ] = { 0x10 } ; /* enable oscillator */
ds2404_write_memory ( dev , 0x201 , 1 , in ) ;
}
static int ds2404_read_time ( struct device * dev , struct rtc_time * dt )
{
unsigned long time = 0 ;
ds2404_read_memory ( dev , 0x203 , 4 , ( u8 * ) & time ) ;
time = le32_to_cpu ( time ) ;
rtc_time_to_tm ( time , dt ) ;
return rtc_valid_tm ( dt ) ;
}
static int ds2404_set_mmss ( struct device * dev , unsigned long secs )
{
u32 time = cpu_to_le32 ( secs ) ;
ds2404_write_memory ( dev , 0x203 , 4 , ( u8 * ) & time ) ;
return 0 ;
}
static const struct rtc_class_ops ds2404_rtc_ops = {
. read_time = ds2404_read_time ,
. set_mmss = ds2404_set_mmss ,
} ;
static int rtc_probe ( struct platform_device * pdev )
{
2013-11-13 03:10:42 +04:00
struct ds2404_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2012-10-05 04:13:47 +04:00
struct ds2404 * chip ;
int retval = - EBUSY ;
2013-04-30 03:20:40 +04:00
chip = devm_kzalloc ( & pdev - > dev , sizeof ( struct ds2404 ) , GFP_KERNEL ) ;
2012-10-05 04:13:47 +04:00
if ( ! chip )
return - ENOMEM ;
chip - > ops = & ds2404_gpio_ops ;
retval = chip - > ops - > map_io ( chip , pdev , pdata ) ;
if ( retval )
goto err_chip ;
dev_info ( & pdev - > dev , " using GPIOs RST:%d, CLK:%d, DQ:%d \n " ,
chip - > gpio [ DS2404_RST ] . gpio , chip - > gpio [ DS2404_CLK ] . gpio ,
chip - > gpio [ DS2404_DQ ] . gpio ) ;
platform_set_drvdata ( pdev , chip ) ;
2013-04-30 03:20:40 +04:00
chip - > rtc = devm_rtc_device_register ( & pdev - > dev , " ds2404 " ,
& ds2404_rtc_ops , THIS_MODULE ) ;
2012-10-05 04:13:47 +04:00
if ( IS_ERR ( chip - > rtc ) ) {
retval = PTR_ERR ( chip - > rtc ) ;
goto err_io ;
}
ds2404_enable_osc ( & pdev - > dev ) ;
return 0 ;
err_io :
chip - > ops - > unmap_io ( chip ) ;
err_chip :
return retval ;
}
static int rtc_remove ( struct platform_device * dev )
{
struct ds2404 * chip = platform_get_drvdata ( dev ) ;
chip - > ops - > unmap_io ( chip ) ;
return 0 ;
}
static struct platform_driver rtc_device_driver = {
. probe = rtc_probe ,
. remove = rtc_remove ,
. driver = {
. name = " ds2404 " ,
} ,
} ;
2013-02-22 04:44:31 +04:00
module_platform_driver ( rtc_device_driver ) ;
2012-10-05 04:13:47 +04:00
MODULE_DESCRIPTION ( " DS2404 RTC " ) ;
MODULE_AUTHOR ( " Sven Schnelle " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:ds2404 " ) ;