2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2012-03-24 02:02:36 +04:00
/*
* Real time clock driver for DA9052
*
* Copyright ( c ) 2012 Dialog Semiconductor Ltd .
*
* Author : Dajun Dajun Chen < dajun . chen @ diasemi . com >
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/rtc.h>
2013-07-04 02:07:48 +04:00
# include <linux/err.h>
2015-04-16 22:45:26 +03:00
# include <linux/delay.h>
2012-03-24 02:02:36 +04:00
# include <linux/mfd/da9052/da9052.h>
# include <linux/mfd/da9052/reg.h>
2014-06-07 01:35:54 +04:00
# define rtc_err(rtc, fmt, ...) \
dev_err ( rtc - > da9052 - > dev , " %s: " fmt , __func__ , # # __VA_ARGS__ )
2012-03-24 02:02:36 +04:00
2015-04-16 22:45:26 +03:00
# define DA9052_GET_TIME_RETRIES 5
2012-03-24 02:02:36 +04:00
struct da9052_rtc {
struct rtc_device * rtc ;
struct da9052 * da9052 ;
} ;
2014-06-07 01:35:54 +04:00
static int da9052_rtc_enable_alarm ( struct da9052_rtc * rtc , bool enable )
2012-03-24 02:02:36 +04:00
{
int ret ;
if ( enable ) {
2014-06-07 01:35:54 +04:00
ret = da9052_reg_update ( rtc - > da9052 , DA9052_ALARM_Y_REG ,
DA9052_ALARM_Y_ALARM_ON | DA9052_ALARM_Y_TICK_ON ,
DA9052_ALARM_Y_ALARM_ON ) ;
2012-03-24 02:02:36 +04:00
if ( ret ! = 0 )
2014-06-07 01:35:54 +04:00
rtc_err ( rtc , " Failed to enable ALM: %d \n " , ret ) ;
2012-03-24 02:02:36 +04:00
} else {
2014-06-07 01:35:54 +04:00
ret = da9052_reg_update ( rtc - > da9052 , DA9052_ALARM_Y_REG ,
DA9052_ALARM_Y_ALARM_ON | DA9052_ALARM_Y_TICK_ON , 0 ) ;
2012-03-24 02:02:36 +04:00
if ( ret ! = 0 )
2014-06-07 01:35:54 +04:00
rtc_err ( rtc , " Write error: %d \n " , ret ) ;
2012-03-24 02:02:36 +04:00
}
return ret ;
}
static irqreturn_t da9052_rtc_irq ( int irq , void * data )
{
struct da9052_rtc * rtc = data ;
2014-06-07 01:35:54 +04:00
rtc_update_irq ( rtc - > rtc , 1 , RTC_IRQF | RTC_AF ) ;
2012-03-24 02:02:36 +04:00
return IRQ_HANDLED ;
}
2014-06-07 01:35:54 +04:00
static int da9052_read_alarm ( struct da9052_rtc * rtc , struct rtc_time * rtc_tm )
2012-03-24 02:02:36 +04:00
{
int ret ;
2015-04-16 22:45:26 +03:00
uint8_t v [ 2 ] [ 5 ] ;
int idx = 1 ;
int timeout = DA9052_GET_TIME_RETRIES ;
2012-03-24 02:02:36 +04:00
2015-04-16 22:45:26 +03:00
ret = da9052_group_read ( rtc - > da9052 , DA9052_ALARM_MI_REG , 5 , & v [ 0 ] [ 0 ] ) ;
if ( ret ) {
2014-06-07 01:35:54 +04:00
rtc_err ( rtc , " Failed to group read ALM: %d \n " , ret ) ;
2012-03-24 02:02:36 +04:00
return ret ;
}
2015-04-16 22:45:26 +03:00
do {
ret = da9052_group_read ( rtc - > da9052 ,
DA9052_ALARM_MI_REG , 5 , & v [ idx ] [ 0 ] ) ;
if ( ret ) {
rtc_err ( rtc , " Failed to group read ALM: %d \n " , ret ) ;
return ret ;
}
2012-03-24 02:02:36 +04:00
2015-04-16 22:45:26 +03:00
if ( memcmp ( & v [ 0 ] [ 0 ] , & v [ 1 ] [ 0 ] , 5 ) = = 0 ) {
rtc_tm - > tm_year = ( v [ 0 ] [ 4 ] & DA9052_RTC_YEAR ) + 100 ;
rtc_tm - > tm_mon = ( v [ 0 ] [ 3 ] & DA9052_RTC_MONTH ) - 1 ;
rtc_tm - > tm_mday = v [ 0 ] [ 2 ] & DA9052_RTC_DAY ;
rtc_tm - > tm_hour = v [ 0 ] [ 1 ] & DA9052_RTC_HOUR ;
rtc_tm - > tm_min = v [ 0 ] [ 0 ] & DA9052_RTC_MIN ;
2016-07-11 11:05:27 +03:00
rtc_tm - > tm_sec = 0 ;
2015-04-16 22:45:26 +03:00
ret = rtc_valid_tm ( rtc_tm ) ;
return ret ;
}
idx = ( 1 - idx ) ;
msleep ( 20 ) ;
} while ( timeout - - ) ;
rtc_err ( rtc , " Timed out reading alarm time \n " ) ;
return - EIO ;
2012-03-24 02:02:36 +04:00
}
2014-06-07 01:35:54 +04:00
static int da9052_set_alarm ( struct da9052_rtc * rtc , struct rtc_time * rtc_tm )
2012-03-24 02:02:36 +04:00
{
2014-06-07 01:35:54 +04:00
struct da9052 * da9052 = rtc - > da9052 ;
unsigned long alm_time ;
2012-03-24 02:02:36 +04:00
int ret ;
uint8_t v [ 3 ] ;
2020-03-06 10:35:47 +03:00
alm_time = rtc_tm_to_time64 ( rtc_tm ) ;
2014-06-07 01:35:54 +04:00
if ( rtc_tm - > tm_sec > 0 ) {
alm_time + = 60 - rtc_tm - > tm_sec ;
2020-03-06 10:35:47 +03:00
rtc_time64_to_tm ( alm_time , rtc_tm ) ;
2014-06-07 01:35:54 +04:00
}
BUG_ON ( rtc_tm - > tm_sec ) ; /* it will cause repeated irqs if not zero */
2012-03-24 02:02:36 +04:00
rtc_tm - > tm_year - = 100 ;
rtc_tm - > tm_mon + = 1 ;
ret = da9052_reg_update ( da9052 , DA9052_ALARM_MI_REG ,
DA9052_RTC_MIN , rtc_tm - > tm_min ) ;
if ( ret ! = 0 ) {
2014-06-07 01:35:54 +04:00
rtc_err ( rtc , " Failed to write ALRM MIN: %d \n " , ret ) ;
2012-03-24 02:02:36 +04:00
return ret ;
}
v [ 0 ] = rtc_tm - > tm_hour ;
v [ 1 ] = rtc_tm - > tm_mday ;
v [ 2 ] = rtc_tm - > tm_mon ;
ret = da9052_group_write ( da9052 , DA9052_ALARM_H_REG , 3 , v ) ;
if ( ret < 0 )
return ret ;
ret = da9052_reg_update ( da9052 , DA9052_ALARM_Y_REG ,
DA9052_RTC_YEAR , rtc_tm - > tm_year ) ;
if ( ret ! = 0 )
2014-06-07 01:35:54 +04:00
rtc_err ( rtc , " Failed to write ALRM YEAR: %d \n " , ret ) ;
2012-03-24 02:02:36 +04:00
return ret ;
}
2014-06-07 01:35:54 +04:00
static int da9052_rtc_get_alarm_status ( struct da9052_rtc * rtc )
2012-03-24 02:02:36 +04:00
{
int ret ;
2014-06-07 01:35:54 +04:00
ret = da9052_reg_read ( rtc - > da9052 , DA9052_ALARM_Y_REG ) ;
2012-03-24 02:02:36 +04:00
if ( ret < 0 ) {
2014-06-07 01:35:54 +04:00
rtc_err ( rtc , " Failed to read ALM: %d \n " , ret ) ;
2012-03-24 02:02:36 +04:00
return ret ;
}
2014-06-07 01:35:54 +04:00
return ! ! ( ret & DA9052_ALARM_Y_ALARM_ON ) ;
2012-03-24 02:02:36 +04:00
}
static int da9052_rtc_read_time ( struct device * dev , struct rtc_time * rtc_tm )
{
struct da9052_rtc * rtc = dev_get_drvdata ( dev ) ;
int ret ;
2015-04-16 22:45:26 +03:00
uint8_t v [ 2 ] [ 6 ] ;
int idx = 1 ;
int timeout = DA9052_GET_TIME_RETRIES ;
2012-03-24 02:02:36 +04:00
2015-04-16 22:45:26 +03:00
ret = da9052_group_read ( rtc - > da9052 , DA9052_COUNT_S_REG , 6 , & v [ 0 ] [ 0 ] ) ;
if ( ret ) {
2014-06-07 01:35:54 +04:00
rtc_err ( rtc , " Failed to read RTC time : %d \n " , ret ) ;
2012-03-24 02:02:36 +04:00
return ret ;
}
2015-04-16 22:45:26 +03:00
do {
ret = da9052_group_read ( rtc - > da9052 ,
DA9052_COUNT_S_REG , 6 , & v [ idx ] [ 0 ] ) ;
if ( ret ) {
rtc_err ( rtc , " Failed to read RTC time : %d \n " , ret ) ;
return ret ;
}
2012-03-24 02:02:36 +04:00
2015-04-16 22:45:26 +03:00
if ( memcmp ( & v [ 0 ] [ 0 ] , & v [ 1 ] [ 0 ] , 6 ) = = 0 ) {
rtc_tm - > tm_year = ( v [ 0 ] [ 5 ] & DA9052_RTC_YEAR ) + 100 ;
rtc_tm - > tm_mon = ( v [ 0 ] [ 4 ] & DA9052_RTC_MONTH ) - 1 ;
rtc_tm - > tm_mday = v [ 0 ] [ 3 ] & DA9052_RTC_DAY ;
rtc_tm - > tm_hour = v [ 0 ] [ 2 ] & DA9052_RTC_HOUR ;
rtc_tm - > tm_min = v [ 0 ] [ 1 ] & DA9052_RTC_MIN ;
rtc_tm - > tm_sec = v [ 0 ] [ 0 ] & DA9052_RTC_SEC ;
2018-03-02 12:08:47 +03:00
return 0 ;
2015-04-16 22:45:26 +03:00
}
idx = ( 1 - idx ) ;
msleep ( 20 ) ;
} while ( timeout - - ) ;
rtc_err ( rtc , " Timed out reading time \n " ) ;
return - EIO ;
2012-03-24 02:02:36 +04:00
}
static int da9052_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
struct da9052_rtc * rtc ;
uint8_t v [ 6 ] ;
2014-06-07 01:35:54 +04:00
int ret ;
2012-03-24 02:02:36 +04:00
2015-04-16 22:45:29 +03:00
/* DA9052 only has 6 bits for year - to represent 2000-2063 */
if ( ( tm - > tm_year < 100 ) | | ( tm - > tm_year > 163 ) )
return - EINVAL ;
2012-03-24 02:02:36 +04:00
rtc = dev_get_drvdata ( dev ) ;
v [ 0 ] = tm - > tm_sec ;
v [ 1 ] = tm - > tm_min ;
v [ 2 ] = tm - > tm_hour ;
v [ 3 ] = tm - > tm_mday ;
v [ 4 ] = tm - > tm_mon + 1 ;
v [ 5 ] = tm - > tm_year - 100 ;
2014-06-07 01:35:54 +04:00
ret = da9052_group_write ( rtc - > da9052 , DA9052_COUNT_S_REG , 6 , v ) ;
if ( ret < 0 )
rtc_err ( rtc , " failed to set RTC time: %d \n " , ret ) ;
return ret ;
2012-03-24 02:02:36 +04:00
}
static int da9052_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
int ret ;
struct rtc_time * tm = & alrm - > time ;
struct da9052_rtc * rtc = dev_get_drvdata ( dev ) ;
2014-06-07 01:35:54 +04:00
ret = da9052_read_alarm ( rtc , tm ) ;
if ( ret < 0 ) {
rtc_err ( rtc , " failed to read RTC alarm: %d \n " , ret ) ;
2012-03-24 02:02:36 +04:00
return ret ;
2014-06-07 01:35:54 +04:00
}
2012-03-24 02:02:36 +04:00
2014-06-07 01:35:54 +04:00
alrm - > enabled = da9052_rtc_get_alarm_status ( rtc ) ;
2012-03-24 02:02:36 +04:00
return 0 ;
}
static int da9052_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
int ret ;
struct rtc_time * tm = & alrm - > time ;
struct da9052_rtc * rtc = dev_get_drvdata ( dev ) ;
2015-04-16 22:45:29 +03:00
/* DA9052 only has 6 bits for year - to represent 2000-2063 */
if ( ( tm - > tm_year < 100 ) | | ( tm - > tm_year > 163 ) )
return - EINVAL ;
2014-06-07 01:35:54 +04:00
ret = da9052_rtc_enable_alarm ( rtc , 0 ) ;
2012-03-24 02:02:36 +04:00
if ( ret < 0 )
return ret ;
2014-06-07 01:35:54 +04:00
ret = da9052_set_alarm ( rtc , tm ) ;
if ( ret < 0 )
2012-03-24 02:02:36 +04:00
return ret ;
2014-06-07 01:35:54 +04:00
ret = da9052_rtc_enable_alarm ( rtc , 1 ) ;
2012-03-24 02:02:36 +04:00
return ret ;
}
static int da9052_rtc_alarm_irq_enable ( struct device * dev , unsigned int enabled )
{
struct da9052_rtc * rtc = dev_get_drvdata ( dev ) ;
2014-06-07 01:35:54 +04:00
return da9052_rtc_enable_alarm ( rtc , enabled ) ;
2012-03-24 02:02:36 +04:00
}
static const struct rtc_class_ops da9052_rtc_ops = {
. read_time = da9052_rtc_read_time ,
. set_time = da9052_rtc_set_time ,
. read_alarm = da9052_rtc_read_alarm ,
. set_alarm = da9052_rtc_set_alarm ,
. alarm_irq_enable = da9052_rtc_alarm_irq_enable ,
} ;
2012-12-22 01:09:38 +04:00
static int da9052_rtc_probe ( struct platform_device * pdev )
2012-03-24 02:02:36 +04:00
{
struct da9052_rtc * rtc ;
int ret ;
rtc = devm_kzalloc ( & pdev - > dev , sizeof ( struct da9052_rtc ) , GFP_KERNEL ) ;
if ( ! rtc )
return - ENOMEM ;
rtc - > da9052 = dev_get_drvdata ( pdev - > dev . parent ) ;
platform_set_drvdata ( pdev , rtc ) ;
2014-06-07 01:35:54 +04:00
ret = da9052_reg_write ( rtc - > da9052 , DA9052_BBAT_CONT_REG , 0xFE ) ;
if ( ret < 0 ) {
rtc_err ( rtc ,
" Failed to setup RTC battery charging: %d \n " , ret ) ;
return ret ;
}
ret = da9052_reg_update ( rtc - > da9052 , DA9052_ALARM_Y_REG ,
DA9052_ALARM_Y_TICK_ON , 0 ) ;
if ( ret ! = 0 )
rtc_err ( rtc , " Failed to disable TICKS: %d \n " , ret ) ;
2016-04-14 14:04:54 +03:00
device_init_wakeup ( & pdev - > dev , true ) ;
2020-03-30 01:42:40 +03:00
rtc - > rtc = devm_rtc_allocate_device ( & pdev - > dev ) ;
2016-04-14 14:04:54 +03:00
if ( IS_ERR ( rtc - > rtc ) )
return PTR_ERR ( rtc - > rtc ) ;
2020-03-30 01:42:40 +03:00
rtc - > rtc - > ops = & da9052_rtc_ops ;
2020-03-06 10:35:46 +03:00
rtc - > rtc - > range_min = RTC_TIMESTAMP_BEGIN_2000 ;
rtc - > rtc - > range_max = RTC_TIMESTAMP_END_2063 ;
2020-03-30 01:42:40 +03:00
ret = rtc_register_device ( rtc - > rtc ) ;
if ( ret )
return ret ;
2014-04-04 01:49:52 +04:00
ret = da9052_request_irq ( rtc - > da9052 , DA9052_IRQ_ALARM , " ALM " ,
2013-03-23 02:04:44 +04:00
da9052_rtc_irq , rtc ) ;
2012-03-24 02:02:36 +04:00
if ( ret ! = 0 ) {
2014-06-07 01:35:54 +04:00
rtc_err ( rtc , " irq registration failed: %d \n " , ret ) ;
2012-07-31 01:41:54 +04:00
return ret ;
2012-03-24 02:02:36 +04:00
}
2016-04-14 14:04:54 +03:00
return 0 ;
2012-03-24 02:02:36 +04:00
}
static struct platform_driver da9052_rtc_driver = {
. probe = da9052_rtc_probe ,
. driver = {
. name = " da9052-rtc " ,
} ,
} ;
module_platform_driver ( da9052_rtc_driver ) ;
2014-06-07 01:35:54 +04:00
MODULE_AUTHOR ( " Anthony Olech <Anthony.Olech@diasemi.com> " ) ;
2012-03-24 02:02:36 +04:00
MODULE_DESCRIPTION ( " RTC driver for Dialog DA9052 PMIC " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:da9052-rtc " ) ;