2010-10-28 14:30:53 +04:00
/*
* Real Time Clock driver for Freescale MC13XXX PMIC
*
* ( C ) 2009 Sascha Hauer , Pengutronix
* ( C ) 2009 Uwe Kleine - Koenig , Pengutronix
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/mfd/mc13xxx.h>
# include <linux/platform_device.h>
# include <linux/kernel.h>
# include <linux/module.h>
2018-06-20 08:47:28 +03:00
# include <linux/mod_devicetable.h>
2010-10-28 14:30:53 +04:00
# include <linux/slab.h>
# include <linux/rtc.h>
# define DRIVER_NAME "mc13xxx-rtc"
# define MC13XXX_RTCTOD 20
# define MC13XXX_RTCTODA 21
# define MC13XXX_RTCDAY 22
# define MC13XXX_RTCDAYA 23
2014-04-04 01:50:01 +04:00
# define SEC_PER_DAY (24 * 60 * 60)
2010-10-28 14:30:53 +04:00
struct mc13xxx_rtc {
struct rtc_device * rtc ;
struct mc13xxx * mc13xxx ;
int valid ;
} ;
static int mc13xxx_rtc_irq_enable_unlocked ( struct device * dev ,
unsigned int enabled , int irq )
{
struct mc13xxx_rtc * priv = dev_get_drvdata ( dev ) ;
int ( * func ) ( struct mc13xxx * mc13xxx , int irq ) ;
if ( ! priv - > valid )
return - ENODATA ;
func = enabled ? mc13xxx_irq_unmask : mc13xxx_irq_mask ;
return func ( priv - > mc13xxx , irq ) ;
}
2014-04-04 01:49:58 +04:00
static int mc13xxx_rtc_alarm_irq_enable ( struct device * dev ,
unsigned int enabled )
2010-10-28 14:30:53 +04:00
{
struct mc13xxx_rtc * priv = dev_get_drvdata ( dev ) ;
int ret ;
mc13xxx_lock ( priv - > mc13xxx ) ;
2014-04-04 01:49:58 +04:00
ret = mc13xxx_rtc_irq_enable_unlocked ( dev , enabled , MC13XXX_IRQ_TODA ) ;
2010-10-28 14:30:53 +04:00
mc13xxx_unlock ( priv - > mc13xxx ) ;
return ret ;
}
static int mc13xxx_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
struct mc13xxx_rtc * priv = dev_get_drvdata ( dev ) ;
unsigned int seconds , days1 , days2 ;
2014-04-04 01:50:00 +04:00
if ( ! priv - > valid )
return - ENODATA ;
2010-10-28 14:30:53 +04:00
2014-04-04 01:50:01 +04:00
do {
int ret ;
2010-10-28 14:30:53 +04:00
2014-04-04 01:50:01 +04:00
ret = mc13xxx_reg_read ( priv - > mc13xxx , MC13XXX_RTCDAY , & days1 ) ;
if ( ret )
return ret ;
2010-10-28 14:30:53 +04:00
2014-04-04 01:50:01 +04:00
ret = mc13xxx_reg_read ( priv - > mc13xxx , MC13XXX_RTCTOD , & seconds ) ;
if ( ret )
return ret ;
2010-10-28 14:30:53 +04:00
2014-04-04 01:50:01 +04:00
ret = mc13xxx_reg_read ( priv - > mc13xxx , MC13XXX_RTCDAY , & days2 ) ;
if ( ret )
return ret ;
} while ( days1 ! = days2 ) ;
2010-10-28 14:30:53 +04:00
2015-04-02 06:34:30 +03:00
rtc_time64_to_tm ( ( time64_t ) days1 * SEC_PER_DAY + seconds , tm ) ;
2010-10-28 14:30:53 +04:00
2018-02-19 18:23:55 +03:00
return 0 ;
2010-10-28 14:30:53 +04:00
}
2015-04-02 06:34:30 +03:00
static int mc13xxx_rtc_set_mmss ( struct device * dev , time64_t secs )
2010-10-28 14:30:53 +04:00
{
struct mc13xxx_rtc * priv = dev_get_drvdata ( dev ) ;
unsigned int seconds , days ;
unsigned int alarmseconds ;
int ret ;
2015-04-02 06:34:30 +03:00
days = div_s64_rem ( secs , SEC_PER_DAY , & seconds ) ;
2010-10-28 14:30:53 +04:00
mc13xxx_lock ( priv - > mc13xxx ) ;
/*
* temporarily invalidate alarm to prevent triggering it when the day is
* already updated while the time isn ' t yet .
*/
ret = mc13xxx_reg_read ( priv - > mc13xxx , MC13XXX_RTCTODA , & alarmseconds ) ;
if ( unlikely ( ret ) )
goto out ;
2014-04-04 01:50:01 +04:00
if ( alarmseconds < SEC_PER_DAY ) {
2010-10-28 14:30:53 +04:00
ret = mc13xxx_reg_write ( priv - > mc13xxx ,
MC13XXX_RTCTODA , 0x1ffff ) ;
if ( unlikely ( ret ) )
goto out ;
}
/*
* write seconds = 0 to prevent a day switch between writing days
* and seconds below
*/
ret = mc13xxx_reg_write ( priv - > mc13xxx , MC13XXX_RTCTOD , 0 ) ;
if ( unlikely ( ret ) )
goto out ;
ret = mc13xxx_reg_write ( priv - > mc13xxx , MC13XXX_RTCDAY , days ) ;
if ( unlikely ( ret ) )
goto out ;
ret = mc13xxx_reg_write ( priv - > mc13xxx , MC13XXX_RTCTOD , seconds ) ;
if ( unlikely ( ret ) )
goto out ;
/* restore alarm */
2014-04-04 01:50:01 +04:00
if ( alarmseconds < SEC_PER_DAY ) {
2010-10-28 14:30:53 +04:00
ret = mc13xxx_reg_write ( priv - > mc13xxx ,
MC13XXX_RTCTODA , alarmseconds ) ;
if ( unlikely ( ret ) )
goto out ;
}
2014-04-04 01:50:00 +04:00
if ( ! priv - > valid ) {
ret = mc13xxx_irq_ack ( priv - > mc13xxx , MC13XXX_IRQ_RTCRST ) ;
if ( unlikely ( ret ) )
goto out ;
ret = mc13xxx_irq_unmask ( priv - > mc13xxx , MC13XXX_IRQ_RTCRST ) ;
}
2010-10-28 14:30:53 +04:00
out :
priv - > valid = ! ret ;
mc13xxx_unlock ( priv - > mc13xxx ) ;
return ret ;
}
static int mc13xxx_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alarm )
{
struct mc13xxx_rtc * priv = dev_get_drvdata ( dev ) ;
unsigned seconds , days ;
2015-04-02 06:34:30 +03:00
time64_t s1970 ;
2010-10-28 14:30:53 +04:00
int enabled , pending ;
int ret ;
mc13xxx_lock ( priv - > mc13xxx ) ;
ret = mc13xxx_reg_read ( priv - > mc13xxx , MC13XXX_RTCTODA , & seconds ) ;
if ( unlikely ( ret ) )
goto out ;
2014-04-04 01:50:01 +04:00
if ( seconds > = SEC_PER_DAY ) {
2010-10-28 14:30:53 +04:00
ret = - ENODATA ;
goto out ;
}
ret = mc13xxx_reg_read ( priv - > mc13xxx , MC13XXX_RTCDAY , & days ) ;
if ( unlikely ( ret ) )
goto out ;
ret = mc13xxx_irq_status ( priv - > mc13xxx , MC13XXX_IRQ_TODA ,
& enabled , & pending ) ;
out :
mc13xxx_unlock ( priv - > mc13xxx ) ;
if ( ret )
return ret ;
alarm - > enabled = enabled ;
alarm - > pending = pending ;
2015-04-02 06:34:30 +03:00
s1970 = ( time64_t ) days * SEC_PER_DAY + seconds ;
2010-10-28 14:30:53 +04:00
2015-04-02 06:34:30 +03:00
rtc_time64_to_tm ( s1970 , & alarm - > time ) ;
dev_dbg ( dev , " %s: %lld \n " , __func__ , ( long long ) s1970 ) ;
2010-10-28 14:30:53 +04:00
return 0 ;
}
static int mc13xxx_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alarm )
{
struct mc13xxx_rtc * priv = dev_get_drvdata ( dev ) ;
2015-04-02 06:34:30 +03:00
time64_t s1970 ;
u32 seconds , days ;
2010-10-28 14:30:53 +04:00
int ret ;
mc13xxx_lock ( priv - > mc13xxx ) ;
/* disable alarm to prevent false triggering */
ret = mc13xxx_reg_write ( priv - > mc13xxx , MC13XXX_RTCTODA , 0x1ffff ) ;
if ( unlikely ( ret ) )
goto out ;
ret = mc13xxx_irq_ack ( priv - > mc13xxx , MC13XXX_IRQ_TODA ) ;
if ( unlikely ( ret ) )
goto out ;
2015-04-02 06:34:30 +03:00
s1970 = rtc_tm_to_time64 ( & alarm - > time ) ;
2010-10-28 14:30:53 +04:00
2015-02-20 16:47:30 +03:00
dev_dbg ( dev , " %s: %s %lld \n " , __func__ , alarm - > enabled ? " on " : " off " ,
2015-04-02 06:34:30 +03:00
( long long ) s1970 ) ;
2010-10-28 14:30:53 +04:00
ret = mc13xxx_rtc_irq_enable_unlocked ( dev , alarm - > enabled ,
MC13XXX_IRQ_TODA ) ;
if ( unlikely ( ret ) )
goto out ;
2015-04-02 06:34:30 +03:00
days = div_s64_rem ( s1970 , SEC_PER_DAY , & seconds ) ;
2010-10-28 14:30:53 +04:00
ret = mc13xxx_reg_write ( priv - > mc13xxx , MC13XXX_RTCDAYA , days ) ;
if ( unlikely ( ret ) )
goto out ;
ret = mc13xxx_reg_write ( priv - > mc13xxx , MC13XXX_RTCTODA , seconds ) ;
out :
mc13xxx_unlock ( priv - > mc13xxx ) ;
return ret ;
}
static irqreturn_t mc13xxx_rtc_alarm_handler ( int irq , void * dev )
{
struct mc13xxx_rtc * priv = dev ;
struct mc13xxx * mc13xxx = priv - > mc13xxx ;
rtc_update_irq ( priv - > rtc , 1 , RTC_IRQF | RTC_AF ) ;
mc13xxx_irq_ack ( mc13xxx , irq ) ;
return IRQ_HANDLED ;
}
static const struct rtc_class_ops mc13xxx_rtc_ops = {
. read_time = mc13xxx_rtc_read_time ,
2015-04-02 06:34:30 +03:00
. set_mmss64 = mc13xxx_rtc_set_mmss ,
2010-10-28 14:30:53 +04:00
. read_alarm = mc13xxx_rtc_read_alarm ,
. set_alarm = mc13xxx_rtc_set_alarm ,
. alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable ,
} ;
static irqreturn_t mc13xxx_rtc_reset_handler ( int irq , void * dev )
{
struct mc13xxx_rtc * priv = dev ;
struct mc13xxx * mc13xxx = priv - > mc13xxx ;
priv - > valid = 0 ;
mc13xxx_irq_mask ( mc13xxx , irq ) ;
return IRQ_HANDLED ;
}
2011-11-03 00:37:56 +04:00
static int __init mc13xxx_rtc_probe ( struct platform_device * pdev )
2010-10-28 14:30:53 +04:00
{
int ret ;
struct mc13xxx_rtc * priv ;
struct mc13xxx * mc13xxx ;
2013-04-30 03:20:44 +04:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2010-10-28 14:30:53 +04:00
if ( ! priv )
return - ENOMEM ;
mc13xxx = dev_get_drvdata ( pdev - > dev . parent ) ;
priv - > mc13xxx = mc13xxx ;
2014-04-04 01:50:00 +04:00
priv - > valid = 1 ;
2010-10-28 14:30:53 +04:00
platform_set_drvdata ( pdev , priv ) ;
mc13xxx_lock ( mc13xxx ) ;
2014-04-04 01:50:00 +04:00
mc13xxx_irq_ack ( mc13xxx , MC13XXX_IRQ_RTCRST ) ;
2010-10-28 14:30:53 +04:00
ret = mc13xxx_irq_request ( mc13xxx , MC13XXX_IRQ_RTCRST ,
mc13xxx_rtc_reset_handler , DRIVER_NAME , priv ) ;
if ( ret )
2014-04-04 01:50:15 +04:00
goto err_irq_request ;
2010-10-28 14:30:53 +04:00
ret = mc13xxx_irq_request_nounmask ( mc13xxx , MC13XXX_IRQ_TODA ,
mc13xxx_rtc_alarm_handler , DRIVER_NAME , priv ) ;
2014-04-04 01:50:15 +04:00
if ( ret )
goto err_irq_request ;
2010-10-28 14:30:53 +04:00
2014-04-04 01:50:15 +04:00
mc13xxx_unlock ( mc13xxx ) ;
priv - > rtc = devm_rtc_device_register ( & pdev - > dev , pdev - > name ,
& mc13xxx_rtc_ops , THIS_MODULE ) ;
2010-10-28 14:30:53 +04:00
2014-04-04 01:50:15 +04:00
return 0 ;
err_irq_request :
mc13xxx_irq_free ( mc13xxx , MC13XXX_IRQ_TODA , priv ) ;
2014-04-04 01:49:57 +04:00
mc13xxx_irq_free ( mc13xxx , MC13XXX_IRQ_RTCRST , priv ) ;
2010-10-28 14:30:53 +04:00
2014-04-04 01:49:57 +04:00
mc13xxx_unlock ( mc13xxx ) ;
2010-10-28 14:30:53 +04:00
return ret ;
}
2014-04-04 01:49:56 +04:00
static int mc13xxx_rtc_remove ( struct platform_device * pdev )
2010-10-28 14:30:53 +04:00
{
struct mc13xxx_rtc * priv = platform_get_drvdata ( pdev ) ;
mc13xxx_lock ( priv - > mc13xxx ) ;
mc13xxx_irq_free ( priv - > mc13xxx , MC13XXX_IRQ_TODA , priv ) ;
mc13xxx_irq_free ( priv - > mc13xxx , MC13XXX_IRQ_RTCRST , priv ) ;
mc13xxx_unlock ( priv - > mc13xxx ) ;
return 0 ;
}
2012-01-11 03:10:52 +04:00
static const struct platform_device_id mc13xxx_rtc_idtable [ ] = {
2010-10-28 14:30:53 +04:00
{
. name = " mc13783-rtc " ,
} , {
. name = " mc13892-rtc " ,
2012-07-31 01:41:52 +04:00
} , {
. name = " mc34708-rtc " ,
2010-10-28 14:30:53 +04:00
} ,
2012-07-31 01:41:50 +04:00
{ /* sentinel */ }
2010-10-28 14:30:53 +04:00
} ;
2012-07-31 01:41:50 +04:00
MODULE_DEVICE_TABLE ( platform , mc13xxx_rtc_idtable ) ;
2010-10-28 14:30:53 +04:00
static struct platform_driver mc13xxx_rtc_driver = {
. id_table = mc13xxx_rtc_idtable ,
2014-04-04 01:49:56 +04:00
. remove = mc13xxx_rtc_remove ,
2010-10-28 14:30:53 +04:00
. driver = {
. name = DRIVER_NAME ,
} ,
} ;
2013-04-30 03:18:43 +04:00
module_platform_driver_probe ( mc13xxx_rtc_driver , & mc13xxx_rtc_probe ) ;
2010-10-28 14:30:53 +04:00
MODULE_AUTHOR ( " Sascha Hauer <s.hauer@pengutronix.de> " ) ;
MODULE_DESCRIPTION ( " RTC driver for Freescale MC13XXX PMIC " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;