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>
# include <linux/mfd/da9052/da9052.h>
# include <linux/mfd/da9052/reg.h>
# define rtc_err(da9052, fmt, ...) \
dev_err ( da9052 - > dev , " %s: " fmt , __func__ , # # __VA_ARGS__ )
struct da9052_rtc {
struct rtc_device * rtc ;
struct da9052 * da9052 ;
int irq ;
} ;
static int da9052_rtc_enable_alarm ( struct da9052 * da9052 , bool enable )
{
int ret ;
if ( enable ) {
ret = da9052_reg_update ( da9052 , DA9052_ALARM_Y_REG ,
DA9052_ALARM_Y_ALARM_ON ,
DA9052_ALARM_Y_ALARM_ON ) ;
if ( ret ! = 0 )
rtc_err ( da9052 , " Failed to enable ALM: %d \n " , ret ) ;
} else {
ret = da9052_reg_update ( da9052 , DA9052_ALARM_Y_REG ,
DA9052_ALARM_Y_ALARM_ON , 0 ) ;
if ( ret ! = 0 )
rtc_err ( da9052 , " Write error: %d \n " , ret ) ;
}
return ret ;
}
static irqreturn_t da9052_rtc_irq ( int irq , void * data )
{
struct da9052_rtc * rtc = data ;
int ret ;
ret = da9052_reg_read ( rtc - > da9052 , DA9052_ALARM_MI_REG ) ;
if ( ret < 0 ) {
rtc_err ( rtc - > da9052 , " Read error: %d \n " , ret ) ;
return IRQ_NONE ;
}
if ( ret & DA9052_ALARMMI_ALARMTYPE ) {
da9052_rtc_enable_alarm ( rtc - > da9052 , 0 ) ;
rtc_update_irq ( rtc - > rtc , 1 , RTC_IRQF | RTC_AF ) ;
} else
rtc_update_irq ( rtc - > rtc , 1 , RTC_IRQF | RTC_PF ) ;
return IRQ_HANDLED ;
}
static int da9052_read_alarm ( struct da9052 * da9052 , struct rtc_time * rtc_tm )
{
int ret ;
uint8_t v [ 5 ] ;
ret = da9052_group_read ( da9052 , DA9052_ALARM_MI_REG , 5 , v ) ;
if ( ret ! = 0 ) {
rtc_err ( da9052 , " Failed to group read ALM: %d \n " , ret ) ;
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 ) ;
if ( ret ! = 0 )
return ret ;
return ret ;
}
static int da9052_set_alarm ( struct da9052 * da9052 , struct rtc_time * rtc_tm )
{
int ret ;
uint8_t v [ 3 ] ;
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 ) {
rtc_err ( da9052 , " Failed to write ALRM MIN: %d \n " , ret ) ;
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 )
rtc_err ( da9052 , " Failed to write ALRM YEAR: %d \n " , ret ) ;
return ret ;
}
static int da9052_rtc_get_alarm_status ( struct da9052 * da9052 )
{
int ret ;
ret = da9052_reg_read ( da9052 , DA9052_ALARM_Y_REG ) ;
if ( ret < 0 ) {
rtc_err ( da9052 , " Failed to read ALM: %d \n " , ret ) ;
return ret ;
}
ret & = DA9052_ALARM_Y_ALARM_ON ;
return ( ret > 0 ) ? 1 : 0 ;
}
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 ) {
rtc_err ( rtc - > da9052 , " Failed to read RTC time : %d \n " , ret ) ;
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 ) ;
if ( ret ! = 0 ) {
rtc_err ( rtc - > da9052 , " rtc_valid_tm failed: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static int da9052_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
struct da9052_rtc * rtc ;
uint8_t v [ 6 ] ;
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 ;
return da9052_group_write ( rtc - > da9052 , DA9052_COUNT_S_REG , 6 , v ) ;
}
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 ) ;
ret = da9052_read_alarm ( rtc - > da9052 , tm ) ;
if ( ret )
return ret ;
alrm - > enabled = da9052_rtc_get_alarm_status ( rtc - > da9052 ) ;
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 ) ;
ret = da9052_rtc_enable_alarm ( rtc - > da9052 , 0 ) ;
if ( ret < 0 )
return ret ;
ret = da9052_set_alarm ( rtc - > da9052 , tm ) ;
if ( ret )
return ret ;
ret = da9052_rtc_enable_alarm ( rtc - > da9052 , 1 ) ;
return ret ;
}
static int da9052_rtc_alarm_irq_enable ( struct device * dev , unsigned int enabled )
{
struct da9052_rtc * rtc = dev_get_drvdata ( dev ) ;
return da9052_rtc_enable_alarm ( rtc - > da9052 , enabled ) ;
}
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 ,
} ;
static int __devinit da9052_rtc_probe ( struct platform_device * pdev )
{
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 ) ;
rtc - > irq = platform_get_irq_byname ( pdev , " ALM " ) ;
ret = request_threaded_irq ( rtc - > irq , NULL , da9052_rtc_irq ,
IRQF_TRIGGER_LOW | IRQF_ONESHOT ,
" ALM " , rtc ) ;
if ( ret ! = 0 ) {
rtc_err ( rtc - > da9052 , " irq registration failed: %d \n " , ret ) ;
2012-07-31 01:41:54 +04:00
return ret ;
2012-03-24 02:02:36 +04:00
}
rtc - > rtc = rtc_device_register ( pdev - > name , & pdev - > dev ,
& da9052_rtc_ops , THIS_MODULE ) ;
if ( IS_ERR ( rtc - > rtc ) ) {
ret = PTR_ERR ( rtc - > rtc ) ;
goto err_free_irq ;
}
return 0 ;
err_free_irq :
free_irq ( rtc - > irq , rtc ) ;
return ret ;
}
static int __devexit da9052_rtc_remove ( struct platform_device * pdev )
{
struct da9052_rtc * rtc = pdev - > dev . platform_data ;
rtc_device_unregister ( rtc - > rtc ) ;
free_irq ( rtc - > irq , rtc ) ;
platform_set_drvdata ( pdev , NULL ) ;
return 0 ;
}
static struct platform_driver da9052_rtc_driver = {
. probe = da9052_rtc_probe ,
. remove = __devexit_p ( da9052_rtc_remove ) ,
. driver = {
. name = " da9052-rtc " ,
. owner = THIS_MODULE ,
} ,
} ;
module_platform_driver ( da9052_rtc_driver ) ;
MODULE_AUTHOR ( " David Dajun Chen <dchen@diasemi.com> " ) ;
MODULE_DESCRIPTION ( " RTC driver for Dialog DA9052 PMIC " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:da9052-rtc " ) ;