2019-04-19 11:24:59 +03:00
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2012 Sven Schnelle <svens@stackframe.org>
2012-10-05 04:13:47 +04:00
# 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>
# include <linux/delay.h>
2023-08-07 10:31:06 +03:00
# include <linux/gpio/consumer.h>
2012-10-05 04:13:47 +04:00
# 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
# define DS2404_RST 0
# define DS2404_CLK 1
# define DS2404_DQ 2
struct ds2404 {
2023-08-07 10:31:06 +03:00
struct device * dev ;
struct gpio_desc * rst_gpiod ;
struct gpio_desc * clk_gpiod ;
struct gpio_desc * dq_gpiod ;
2012-10-05 04:13:47 +04:00
struct rtc_device * rtc ;
} ;
2023-08-07 10:31:06 +03:00
static int ds2404_gpio_map ( struct ds2404 * chip , struct platform_device * pdev )
2012-10-05 04:13:47 +04:00
{
2023-08-07 10:31:06 +03:00
struct device * dev = & pdev - > dev ;
2012-10-05 04:13:47 +04:00
2023-08-07 10:31:06 +03:00
/* This will de-assert RESET, declare this GPIO as GPIOD_ACTIVE_LOW */
chip - > rst_gpiod = devm_gpiod_get ( dev , " rst " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( chip - > rst_gpiod ) )
return PTR_ERR ( chip - > rst_gpiod ) ;
2012-10-05 04:13:47 +04:00
2023-08-07 10:31:06 +03:00
chip - > clk_gpiod = devm_gpiod_get ( dev , " clk " , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( chip - > clk_gpiod ) )
return PTR_ERR ( chip - > clk_gpiod ) ;
2012-10-05 04:13:47 +04:00
2023-08-07 10:31:06 +03:00
chip - > dq_gpiod = devm_gpiod_get ( dev , " dq " , GPIOD_ASIS ) ;
if ( IS_ERR ( chip - > dq_gpiod ) )
return PTR_ERR ( chip - > dq_gpiod ) ;
2012-10-05 04:13:47 +04:00
2023-08-07 10:31:06 +03:00
return 0 ;
2012-10-05 04:13:47 +04:00
}
2023-08-07 10:31:06 +03:00
static void ds2404_reset ( struct ds2404 * chip )
2012-10-05 04:13:47 +04:00
{
2023-08-07 10:31:06 +03:00
gpiod_set_value ( chip - > rst_gpiod , 1 ) ;
2012-10-05 04:13:47 +04:00
udelay ( 1000 ) ;
2023-08-07 10:31:06 +03:00
gpiod_set_value ( chip - > rst_gpiod , 0 ) ;
gpiod_set_value ( chip - > clk_gpiod , 0 ) ;
gpiod_direction_output ( chip - > dq_gpiod , 0 ) ;
2012-10-05 04:13:47 +04:00
udelay ( 10 ) ;
}
2023-08-07 10:31:06 +03:00
static void ds2404_write_byte ( struct ds2404 * chip , u8 byte )
2012-10-05 04:13:47 +04:00
{
int i ;
2023-08-07 10:31:06 +03:00
gpiod_direction_output ( chip - > dq_gpiod , 1 ) ;
2012-10-05 04:13:47 +04:00
for ( i = 0 ; i < 8 ; i + + ) {
2023-08-07 10:31:06 +03:00
gpiod_set_value ( chip - > dq_gpiod , byte & ( 1 < < i ) ) ;
2012-10-05 04:13:47 +04:00
udelay ( 10 ) ;
2023-08-07 10:31:06 +03:00
gpiod_set_value ( chip - > clk_gpiod , 1 ) ;
2012-10-05 04:13:47 +04:00
udelay ( 10 ) ;
2023-08-07 10:31:06 +03:00
gpiod_set_value ( chip - > clk_gpiod , 0 ) ;
2012-10-05 04:13:47 +04:00
udelay ( 10 ) ;
}
}
2023-08-07 10:31:06 +03:00
static u8 ds2404_read_byte ( struct ds2404 * chip )
2012-10-05 04:13:47 +04:00
{
int i ;
u8 ret = 0 ;
2023-08-07 10:31:06 +03:00
gpiod_direction_input ( chip - > dq_gpiod ) ;
2012-10-05 04:13:47 +04:00
for ( i = 0 ; i < 8 ; i + + ) {
2023-08-07 10:31:06 +03:00
gpiod_set_value ( chip - > clk_gpiod , 0 ) ;
2012-10-05 04:13:47 +04:00
udelay ( 10 ) ;
2023-08-07 10:31:06 +03:00
if ( gpiod_get_value ( chip - > dq_gpiod ) )
2012-10-05 04:13:47 +04:00
ret | = 1 < < i ;
2023-08-07 10:31:06 +03:00
gpiod_set_value ( chip - > clk_gpiod , 1 ) ;
2012-10-05 04:13:47 +04:00
udelay ( 10 ) ;
}
return ret ;
}
2023-08-07 10:31:06 +03:00
static void ds2404_read_memory ( struct ds2404 * chip , u16 offset ,
2012-10-05 04:13:47 +04:00
int length , u8 * out )
{
2023-08-07 10:31:06 +03:00
ds2404_reset ( chip ) ;
ds2404_write_byte ( chip , DS2404_READ_MEMORY_CMD ) ;
ds2404_write_byte ( chip , offset & 0xff ) ;
ds2404_write_byte ( chip , ( offset > > 8 ) & 0xff ) ;
2012-10-05 04:13:47 +04:00
while ( length - - )
2023-08-07 10:31:06 +03:00
* out + + = ds2404_read_byte ( chip ) ;
2012-10-05 04:13:47 +04:00
}
2023-08-07 10:31:06 +03:00
static void ds2404_write_memory ( struct ds2404 * chip , u16 offset ,
2012-10-05 04:13:47 +04:00
int length , u8 * out )
{
int i ;
u8 ta01 , ta02 , es ;
2023-08-07 10:31:06 +03:00
ds2404_reset ( chip ) ;
ds2404_write_byte ( chip , DS2404_WRITE_SCRATCHPAD_CMD ) ;
ds2404_write_byte ( chip , offset & 0xff ) ;
ds2404_write_byte ( chip , ( offset > > 8 ) & 0xff ) ;
2012-10-05 04:13:47 +04:00
for ( i = 0 ; i < length ; i + + )
2023-08-07 10:31:06 +03:00
ds2404_write_byte ( chip , out [ i ] ) ;
2012-10-05 04:13:47 +04:00
2023-08-07 10:31:06 +03:00
ds2404_reset ( chip ) ;
ds2404_write_byte ( chip , DS2404_READ_SCRATCHPAD_CMD ) ;
2012-10-05 04:13:47 +04:00
2023-08-07 10:31:06 +03:00
ta01 = ds2404_read_byte ( chip ) ;
ta02 = ds2404_read_byte ( chip ) ;
es = ds2404_read_byte ( chip ) ;
2012-10-05 04:13:47 +04:00
for ( i = 0 ; i < length ; i + + ) {
2023-08-07 10:31:06 +03:00
if ( out [ i ] ! = ds2404_read_byte ( chip ) ) {
dev_err ( chip - > dev , " read invalid data \n " ) ;
2012-10-05 04:13:47 +04:00
return ;
}
}
2023-08-07 10:31:06 +03:00
ds2404_reset ( chip ) ;
ds2404_write_byte ( chip , DS2404_COPY_SCRATCHPAD_CMD ) ;
ds2404_write_byte ( chip , ta01 ) ;
ds2404_write_byte ( chip , ta02 ) ;
ds2404_write_byte ( chip , es ) ;
2012-10-05 04:13:47 +04:00
2023-08-07 10:31:06 +03:00
while ( gpiod_get_value ( chip - > dq_gpiod ) )
2012-10-05 04:13:47 +04:00
;
}
2023-08-07 10:31:06 +03:00
static void ds2404_enable_osc ( struct ds2404 * chip )
2012-10-05 04:13:47 +04:00
{
u8 in [ 1 ] = { 0x10 } ; /* enable oscillator */
2023-08-07 10:31:06 +03:00
ds2404_write_memory ( chip , 0x201 , 1 , in ) ;
2012-10-05 04:13:47 +04:00
}
static int ds2404_read_time ( struct device * dev , struct rtc_time * dt )
{
2023-08-07 10:31:06 +03:00
struct ds2404 * chip = dev_get_drvdata ( dev ) ;
2012-10-05 04:13:47 +04:00
unsigned long time = 0 ;
2019-05-09 05:13:55 +03:00
__le32 hw_time = 0 ;
2012-10-05 04:13:47 +04:00
2023-08-07 10:31:06 +03:00
ds2404_read_memory ( chip , 0x203 , 4 , ( u8 * ) & hw_time ) ;
2019-05-09 05:13:55 +03:00
time = le32_to_cpu ( hw_time ) ;
2012-10-05 04:13:47 +04:00
2019-04-19 11:24:57 +03:00
rtc_time64_to_tm ( time , dt ) ;
2018-02-19 18:23:56 +03:00
return 0 ;
2012-10-05 04:13:47 +04:00
}
2019-04-19 11:24:58 +03:00
static int ds2404_set_time ( struct device * dev , struct rtc_time * dt )
2012-10-05 04:13:47 +04:00
{
2023-08-07 10:31:06 +03:00
struct ds2404 * chip = dev_get_drvdata ( dev ) ;
2019-04-19 11:24:58 +03:00
u32 time = cpu_to_le32 ( rtc_tm_to_time64 ( dt ) ) ;
2023-08-07 10:31:06 +03:00
ds2404_write_memory ( chip , 0x203 , 4 , ( u8 * ) & time ) ;
2012-10-05 04:13:47 +04:00
return 0 ;
}
static const struct rtc_class_ops ds2404_rtc_ops = {
. read_time = ds2404_read_time ,
2019-04-19 11:24:58 +03:00
. set_time = ds2404_set_time ,
2012-10-05 04:13:47 +04:00
} ;
static int rtc_probe ( struct platform_device * pdev )
{
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 ;
2023-08-07 10:31:06 +03:00
chip - > dev = & pdev - > dev ;
2019-04-19 11:24:56 +03:00
chip - > rtc = devm_rtc_allocate_device ( & pdev - > dev ) ;
if ( IS_ERR ( chip - > rtc ) )
return PTR_ERR ( chip - > rtc ) ;
2023-08-07 10:31:06 +03:00
retval = ds2404_gpio_map ( chip , pdev ) ;
2012-10-05 04:13:47 +04:00
if ( retval )
2019-04-19 11:25:01 +03:00
return retval ;
2012-10-05 04:13:47 +04:00
platform_set_drvdata ( pdev , chip ) ;
2019-04-19 11:24:56 +03:00
chip - > rtc - > ops = & ds2404_rtc_ops ;
chip - > rtc - > range_max = U32_MAX ;
2020-11-09 19:34:08 +03:00
retval = devm_rtc_register_device ( chip - > rtc ) ;
2019-04-19 11:24:56 +03:00
if ( retval )
2019-04-19 11:25:01 +03:00
return retval ;
2012-10-05 04:13:47 +04:00
2023-08-07 10:31:06 +03:00
ds2404_enable_osc ( chip ) ;
2012-10-05 04:13:47 +04:00
return 0 ;
}
static struct platform_driver rtc_device_driver = {
. probe = rtc_probe ,
. 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 " ) ;