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 >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
*/
# 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>
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
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 ;
uint8_t v [ 5 ] ;
2014-06-07 01:35:54 +04:00
ret = da9052_group_read ( rtc - > da9052 , DA9052_ALARM_MI_REG , 5 , v ) ;
2012-03-24 02:02:36 +04:00
if ( ret ! = 0 ) {
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 ;
}
rtc_tm - > tm_year = ( v [ 4 ] & DA9052_RTC_YEAR ) + 100 ;
rtc_tm - > tm_mon = ( v [ 3 ] & DA9052_RTC_MONTH ) - 1 ;
rtc_tm - > tm_mday = v [ 2 ] & DA9052_RTC_DAY ;
rtc_tm - > tm_hour = v [ 1 ] & DA9052_RTC_HOUR ;
rtc_tm - > tm_min = v [ 0 ] & DA9052_RTC_MIN ;
ret = rtc_valid_tm ( rtc_tm ) ;
return ret ;
}
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 ] ;
2014-06-07 01:35:54 +04:00
ret = rtc_tm_to_time ( rtc_tm , & alm_time ) ;
if ( ret ! = 0 )
return ret ;
if ( rtc_tm - > tm_sec > 0 ) {
alm_time + = 60 - rtc_tm - > tm_sec ;
rtc_time_to_tm ( alm_time , rtc_tm ) ;
}
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 ) ;
uint8_t v [ 6 ] ;
int ret ;
ret = da9052_group_read ( rtc - > da9052 , DA9052_COUNT_S_REG , 6 , v ) ;
if ( ret < 0 ) {
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 ;
}
rtc_tm - > tm_year = ( v [ 5 ] & DA9052_RTC_YEAR ) + 100 ;
rtc_tm - > tm_mon = ( v [ 4 ] & DA9052_RTC_MONTH ) - 1 ;
rtc_tm - > tm_mday = v [ 3 ] & DA9052_RTC_DAY ;
rtc_tm - > tm_hour = v [ 2 ] & DA9052_RTC_HOUR ;
rtc_tm - > tm_min = v [ 1 ] & DA9052_RTC_MIN ;
rtc_tm - > tm_sec = v [ 0 ] & DA9052_RTC_SEC ;
ret = rtc_valid_tm ( rtc_tm ) ;
2014-06-07 01:35:54 +04:00
return ret ;
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
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 ) ;
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 ) ;
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
}
2013-04-30 03:18:57 +04:00
rtc - > rtc = devm_rtc_device_register ( & pdev - > dev , pdev - > name ,
2012-03-24 02:02:36 +04:00
& da9052_rtc_ops , THIS_MODULE ) ;
2013-07-15 14:13:32 +04:00
return PTR_ERR_OR_ZERO ( rtc - > rtc ) ;
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 " ) ;