2019-03-20 15:43:35 +03:00
// SPDX-License-Identifier: GPL-2.0+
2009-02-05 02:12:01 +03:00
/*
* rtc - dm355evm . c - access battery - backed counter in MSP430 firmware
*
* Copyright ( c ) 2008 by David Brownell
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/rtc.h>
# include <linux/platform_device.h>
2017-08-14 19:34:22 +03:00
# include <linux/mfd/dm355evm_msp.h>
2011-05-27 17:57:25 +04:00
# include <linux/module.h>
2009-02-05 02:12:01 +03:00
/*
* The MSP430 firmware on the DM355 EVM uses a watch crystal to feed
* a 1 Hz counter . When a backup battery is supplied , that makes a
* reasonable RTC for applications where alarms and non - NTP drift
* compensation aren ' t important .
*
* The only real glitch is the inability to read or write all four
* counter bytes atomically : the count may increment in the middle
* of an operation , causing trouble when the LSB rolls over .
*
* This driver was tested with firmware revision A4 .
*/
union evm_time {
u8 bytes [ 4 ] ;
u32 value ;
} ;
static int dm355evm_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
union evm_time time ;
int status ;
int tries = 0 ;
do {
/*
* Read LSB ( 0 ) to MSB ( 3 ) bytes . Defend against the counter
* rolling over by re - reading until the value is stable ,
* and assuming the four reads take at most a few seconds .
*/
status = dm355evm_msp_read ( DM355EVM_MSP_RTC_0 ) ;
if ( status < 0 )
return status ;
if ( tries & & time . bytes [ 0 ] = = status )
break ;
time . bytes [ 0 ] = status ;
status = dm355evm_msp_read ( DM355EVM_MSP_RTC_1 ) ;
if ( status < 0 )
return status ;
if ( tries & & time . bytes [ 1 ] = = status )
break ;
time . bytes [ 1 ] = status ;
status = dm355evm_msp_read ( DM355EVM_MSP_RTC_2 ) ;
if ( status < 0 )
return status ;
if ( tries & & time . bytes [ 2 ] = = status )
break ;
time . bytes [ 2 ] = status ;
status = dm355evm_msp_read ( DM355EVM_MSP_RTC_3 ) ;
if ( status < 0 )
return status ;
if ( tries & & time . bytes [ 3 ] = = status )
break ;
time . bytes [ 3 ] = status ;
} while ( + + tries < 5 ) ;
dev_dbg ( dev , " read timestamp %08x \n " , time . value ) ;
2019-03-20 15:43:34 +03:00
rtc_time64_to_tm ( le32_to_cpu ( time . value ) , tm ) ;
2009-02-05 02:12:01 +03:00
return 0 ;
}
static int dm355evm_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
union evm_time time ;
unsigned long value ;
int status ;
2019-03-20 15:43:34 +03:00
value = rtc_tm_to_time64 ( tm ) ;
2009-02-05 02:12:01 +03:00
time . value = cpu_to_le32 ( value ) ;
dev_dbg ( dev , " write timestamp %08x \n " , time . value ) ;
/*
* REVISIT handle non - atomic writes . . . maybe just retry until
* byte [ 1 ] sticks ( no rollover ) ?
*/
status = dm355evm_msp_write ( time . bytes [ 0 ] , DM355EVM_MSP_RTC_0 ) ;
if ( status < 0 )
return status ;
status = dm355evm_msp_write ( time . bytes [ 1 ] , DM355EVM_MSP_RTC_1 ) ;
if ( status < 0 )
return status ;
status = dm355evm_msp_write ( time . bytes [ 2 ] , DM355EVM_MSP_RTC_2 ) ;
if ( status < 0 )
return status ;
status = dm355evm_msp_write ( time . bytes [ 3 ] , DM355EVM_MSP_RTC_3 ) ;
if ( status < 0 )
return status ;
return 0 ;
}
2017-01-05 19:55:05 +03:00
static const struct rtc_class_ops dm355evm_rtc_ops = {
2009-02-05 02:12:01 +03:00
. read_time = dm355evm_rtc_read_time ,
. set_time = dm355evm_rtc_set_time ,
} ;
/*----------------------------------------------------------------------*/
2012-12-22 01:09:38 +04:00
static int dm355evm_rtc_probe ( struct platform_device * pdev )
2009-02-05 02:12:01 +03:00
{
struct rtc_device * rtc ;
2019-03-20 15:43:32 +03:00
rtc = devm_rtc_allocate_device ( & pdev - > dev ) ;
if ( IS_ERR ( rtc ) )
2009-02-05 02:12:01 +03:00
return PTR_ERR ( rtc ) ;
2019-03-20 15:43:32 +03:00
2009-02-05 02:12:01 +03:00
platform_set_drvdata ( pdev , rtc ) ;
2019-03-20 15:43:32 +03:00
rtc - > ops = & dm355evm_rtc_ops ;
2019-03-20 15:43:33 +03:00
rtc - > range_max = U32_MAX ;
2019-03-20 15:43:32 +03:00
return rtc_register_device ( rtc ) ;
2009-02-05 02:12:01 +03:00
}
/*
* I2C is used to talk to the MSP430 , but this platform device is
* exposed by an MFD driver that manages I2C communications .
*/
static struct platform_driver rtc_dm355evm_driver = {
. probe = dm355evm_rtc_probe ,
. driver = {
. name = " rtc-dm355evm " ,
} ,
} ;
2012-01-11 03:10:48 +04:00
module_platform_driver ( rtc_dm355evm_driver ) ;
2009-02-05 02:12:01 +03:00
MODULE_LICENSE ( " GPL " ) ;