2019-03-20 15:40:41 +03:00
// SPDX-License-Identifier: GPL-2.0+
2008-02-06 12:38:59 +03:00
/*
* " RTT as Real Time Clock " driver for AT91SAM9 SoC family
*
* ( C ) 2007 Michel Benoit
*
* Based on rtc - at91rm9200 . c by Rick Bronson
*/
2015-07-28 22:49:24 +03:00
# include <linux/clk.h>
2008-02-06 12:38:59 +03:00
# include <linux/interrupt.h>
# include <linux/ioctl.h>
2013-04-30 03:20:35 +04:00
# include <linux/io.h>
2015-07-28 22:49:24 +03:00
# include <linux/kernel.h>
2014-09-23 15:14:44 +04:00
# include <linux/mfd/syscon.h>
2015-07-28 22:49:24 +03:00
# include <linux/module.h>
2015-08-10 17:33:39 +03:00
# include <linux/of.h>
2015-07-28 22:49:24 +03:00
# include <linux/platform_device.h>
2014-09-23 15:14:44 +04:00
# include <linux/regmap.h>
2015-07-28 22:49:24 +03:00
# include <linux/rtc.h>
# include <linux/slab.h>
2015-03-02 12:18:14 +03:00
# include <linux/suspend.h>
2015-07-28 22:49:24 +03:00
# include <linux/time.h>
2008-02-06 12:38:59 +03:00
/*
* This driver uses two configurable hardware resources that live in the
* AT91SAM9 backup power domain ( intended to be powered at all times )
* to implement the Real Time Clock interfaces
*
* - A " Real-time Timer " ( RTT ) counts up in seconds from a base time .
* We can ' t assign the counter value ( CRTV ) . . . but we can reset it .
*
* - One of the " General Purpose Backup Registers " ( GPBRs ) holds the
* base time , normally an offset from the beginning of the POSIX
* epoch ( 1970 - Jan - 1 00 : 00 : 00 UTC ) . Some systems also include the
* local timezone ' s offset .
*
* The RTC ' s value is the RTT counter plus that offset . The RTC ' s alarm
* is likewise a base ( ALMV ) plus that offset .
*
* Not all RTTs will be used as RTCs ; some systems have multiple RTTs to
* choose from , or a " real " RTC module . All systems have multiple GPBR
* registers available , likewise usable for more than " RTC " support .
*/
2019-03-20 15:40:42 +03:00
# define AT91_RTT_MR 0x00 /* Real-time Mode Register */
# define AT91_RTT_RTPRES (0xffff << 0) /* Timer Prescaler Value */
# define AT91_RTT_ALMIEN BIT(16) /* Alarm Interrupt Enable */
# define AT91_RTT_RTTINCIEN BIT(17) /* Increment Interrupt Enable */
# define AT91_RTT_RTTRST BIT(18) /* Timer Restart */
2014-09-23 15:13:29 +04:00
2019-03-20 15:40:42 +03:00
# define AT91_RTT_AR 0x04 /* Real-time Alarm Register */
# define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */
2014-09-23 15:13:29 +04:00
2019-03-20 15:40:42 +03:00
# define AT91_RTT_VR 0x08 /* Real-time Value Register */
# define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */
2014-09-23 15:13:29 +04:00
2019-03-20 15:40:42 +03:00
# define AT91_RTT_SR 0x0c /* Real-time Status Register */
# define AT91_RTT_ALMS BIT(0) /* Alarm Status */
# define AT91_RTT_RTTINC BIT(1) /* Timer Increment */
2014-09-23 15:13:29 +04:00
2008-02-06 12:38:59 +03:00
/*
* We store ALARM_DISABLED in ALMV to record that no alarm is set .
* It ' s also the reset value for that field .
*/
# define ALARM_DISABLED ((u32)~0)
struct sam9_rtc {
void __iomem * rtt ;
struct rtc_device * rtcdev ;
u32 imr ;
2014-09-23 15:14:44 +04:00
struct regmap * gpbr ;
unsigned int gpbr_offset ;
2019-03-20 15:40:42 +03:00
int irq ;
2014-09-23 18:41:07 +04:00
struct clk * sclk ;
2015-03-02 12:18:14 +03:00
bool suspended ;
unsigned long events ;
spinlock_t lock ;
2008-02-06 12:38:59 +03:00
} ;
# define rtt_readl(rtc, field) \
2014-09-23 15:13:52 +04:00
readl ( ( rtc ) - > rtt + AT91_RTT_ # # field )
2008-02-06 12:38:59 +03:00
# define rtt_writel(rtc, field, val) \
2014-09-23 15:13:52 +04:00
writel ( ( val ) , ( rtc ) - > rtt + AT91_RTT_ # # field )
2008-02-06 12:38:59 +03:00
2014-09-23 15:14:44 +04:00
static inline unsigned int gpbr_readl ( struct sam9_rtc * rtc )
{
unsigned int val ;
regmap_read ( rtc - > gpbr , rtc - > gpbr_offset , & val ) ;
return val ;
}
static inline void gpbr_writel ( struct sam9_rtc * rtc , unsigned int val )
{
regmap_write ( rtc - > gpbr , rtc - > gpbr_offset , val ) ;
}
2008-02-06 12:38:59 +03:00
/*
* Read current time and date in RTC
*/
static int at91_rtc_readtime ( struct device * dev , struct rtc_time * tm )
{
struct sam9_rtc * rtc = dev_get_drvdata ( dev ) ;
u32 secs , secs2 ;
u32 offset ;
/* read current time offset */
offset = gpbr_readl ( rtc ) ;
if ( offset = = 0 )
return - EILSEQ ;
/* reread the counter to help sync the two clock domains */
secs = rtt_readl ( rtc , VR ) ;
secs2 = rtt_readl ( rtc , VR ) ;
if ( secs ! = secs2 )
secs = rtt_readl ( rtc , VR ) ;
2019-03-20 15:40:40 +03:00
rtc_time64_to_tm ( offset + secs , tm ) ;
2008-02-06 12:38:59 +03:00
2018-12-05 00:23:14 +03:00
dev_dbg ( dev , " %s: %ptR \n " , __func__ , tm ) ;
2008-02-06 12:38:59 +03:00
return 0 ;
}
/*
* Set current time and date in RTC
*/
static int at91_rtc_settime ( struct device * dev , struct rtc_time * tm )
{
struct sam9_rtc * rtc = dev_get_drvdata ( dev ) ;
u32 offset , alarm , mr ;
unsigned long secs ;
2018-12-05 00:23:14 +03:00
dev_dbg ( dev , " %s: %ptR \n " , __func__ , tm ) ;
2008-02-06 12:38:59 +03:00
2019-03-20 15:40:40 +03:00
secs = rtc_tm_to_time64 ( tm ) ;
2008-02-06 12:38:59 +03:00
mr = rtt_readl ( rtc , MR ) ;
/* disable interrupts */
rtt_writel ( rtc , MR , mr & ~ ( AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN ) ) ;
/* read current time offset */
offset = gpbr_readl ( rtc ) ;
/* store the new base time in a battery backup register */
secs + = 1 ;
gpbr_writel ( rtc , secs ) ;
/* adjust the alarm time for the new base */
alarm = rtt_readl ( rtc , AR ) ;
if ( alarm ! = ALARM_DISABLED ) {
if ( offset > secs ) {
/* time jumped backwards, increase time until alarm */
alarm + = ( offset - secs ) ;
} else if ( ( alarm + offset ) > secs ) {
/* time jumped forwards, decrease time until alarm */
alarm - = ( secs - offset ) ;
} else {
/* time jumped past the alarm, disable alarm */
alarm = ALARM_DISABLED ;
mr & = ~ AT91_RTT_ALMIEN ;
}
rtt_writel ( rtc , AR , alarm ) ;
}
/* reset the timer, and re-enable interrupts */
rtt_writel ( rtc , MR , mr | AT91_RTT_RTTRST ) ;
return 0 ;
}
static int at91_rtc_readalarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct sam9_rtc * rtc = dev_get_drvdata ( dev ) ;
struct rtc_time * tm = & alrm - > time ;
u32 alarm = rtt_readl ( rtc , AR ) ;
u32 offset ;
offset = gpbr_readl ( rtc ) ;
if ( offset = = 0 )
return - EILSEQ ;
2010-03-06 00:44:23 +03:00
memset ( alrm , 0 , sizeof ( * alrm ) ) ;
2021-07-08 08:13:40 +03:00
if ( alarm ! = ALARM_DISABLED ) {
2019-03-20 15:40:40 +03:00
rtc_time64_to_tm ( offset + alarm , tm ) ;
2008-02-06 12:38:59 +03:00
2018-12-05 00:23:14 +03:00
dev_dbg ( dev , " %s: %ptR \n " , __func__ , tm ) ;
2008-02-06 12:38:59 +03:00
if ( rtt_readl ( rtc , MR ) & AT91_RTT_ALMIEN )
alrm - > enabled = 1 ;
}
return 0 ;
}
static int at91_rtc_setalarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct sam9_rtc * rtc = dev_get_drvdata ( dev ) ;
struct rtc_time * tm = & alrm - > time ;
unsigned long secs ;
u32 offset ;
u32 mr ;
2019-03-20 15:40:40 +03:00
secs = rtc_tm_to_time64 ( tm ) ;
2008-02-06 12:38:59 +03:00
offset = gpbr_readl ( rtc ) ;
if ( offset = = 0 ) {
/* time is not set */
return - EILSEQ ;
}
mr = rtt_readl ( rtc , MR ) ;
rtt_writel ( rtc , MR , mr & ~ AT91_RTT_ALMIEN ) ;
/* alarm in the past? finish and leave disabled */
if ( secs < = offset ) {
rtt_writel ( rtc , AR , ALARM_DISABLED ) ;
return 0 ;
}
/* else set alarm and maybe enable it */
rtt_writel ( rtc , AR , secs - offset ) ;
if ( alrm - > enabled )
rtt_writel ( rtc , MR , mr | AT91_RTT_ALMIEN ) ;
2018-12-05 00:23:14 +03:00
dev_dbg ( dev , " %s: %ptR \n " , __func__ , tm ) ;
2008-02-06 12:38:59 +03:00
return 0 ;
}
2011-02-03 04:02:41 +03:00
static int at91_rtc_alarm_irq_enable ( struct device * dev , unsigned int enabled )
{
struct sam9_rtc * rtc = dev_get_drvdata ( dev ) ;
u32 mr = rtt_readl ( rtc , MR ) ;
dev_dbg ( dev , " alarm_irq_enable: enabled=%08x, mr %08x \n " , enabled , mr ) ;
if ( enabled )
rtt_writel ( rtc , MR , mr | AT91_RTT_ALMIEN ) ;
else
rtt_writel ( rtc , MR , mr & ~ AT91_RTT_ALMIEN ) ;
return 0 ;
}
2008-02-06 12:38:59 +03:00
/*
* Provide additional RTC information in / proc / driver / rtc
*/
static int at91_rtc_proc ( struct device * dev , struct seq_file * seq )
{
struct sam9_rtc * rtc = dev_get_drvdata ( dev ) ;
2016-03-28 14:24:00 +03:00
u32 mr = rtt_readl ( rtc , MR ) ;
2008-02-06 12:38:59 +03:00
seq_printf ( seq , " update_IRQ \t : %s \n " ,
2019-03-20 15:40:42 +03:00
( mr & AT91_RTT_RTTINCIEN ) ? " yes " : " no " ) ;
2008-02-06 12:38:59 +03:00
return 0 ;
}
2015-03-02 12:18:14 +03:00
static irqreturn_t at91_rtc_cache_events ( struct sam9_rtc * rtc )
2008-02-06 12:38:59 +03:00
{
u32 sr , mr ;
/* Shared interrupt may be for another device. Note: reading
* SR clears it , so we must only read it in this irq handler !
*/
mr = rtt_readl ( rtc , MR ) & ( AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN ) ;
2008-03-20 03:01:09 +03:00
sr = rtt_readl ( rtc , SR ) & ( mr > > 16 ) ;
2008-02-06 12:38:59 +03:00
if ( ! sr )
return IRQ_NONE ;
/* alarm status */
if ( sr & AT91_RTT_ALMS )
2015-03-02 12:18:14 +03:00
rtc - > events | = ( RTC_AF | RTC_IRQF ) ;
2008-02-06 12:38:59 +03:00
/* timer update/increment */
if ( sr & AT91_RTT_RTTINC )
2015-03-02 12:18:14 +03:00
rtc - > events | = ( RTC_UF | RTC_IRQF ) ;
return IRQ_HANDLED ;
}
static void at91_rtc_flush_events ( struct sam9_rtc * rtc )
{
if ( ! rtc - > events )
return ;
2008-02-06 12:38:59 +03:00
2015-03-02 12:18:14 +03:00
rtc_update_irq ( rtc - > rtcdev , 1 , rtc - > events ) ;
rtc - > events = 0 ;
2008-02-06 12:38:59 +03:00
2008-04-28 13:12:00 +04:00
pr_debug ( " %s: num=%ld, events=0x%02lx \n " , __func__ ,
2019-03-20 15:40:42 +03:00
rtc - > events > > 8 , rtc - > events & 0x000000FF ) ;
2015-03-02 12:18:14 +03:00
}
2008-02-06 12:38:59 +03:00
2015-03-02 12:18:14 +03:00
/*
* IRQ handler for the RTC
*/
static irqreturn_t at91_rtc_interrupt ( int irq , void * _rtc )
{
struct sam9_rtc * rtc = _rtc ;
int ret ;
spin_lock ( & rtc - > lock ) ;
ret = at91_rtc_cache_events ( rtc ) ;
/* We're called in suspended state */
if ( rtc - > suspended ) {
/* Mask irqs coming from this peripheral */
rtt_writel ( rtc , MR ,
rtt_readl ( rtc , MR ) &
~ ( AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN ) ) ;
/* Trigger a system wakeup */
pm_system_wakeup ( ) ;
} else {
at91_rtc_flush_events ( rtc ) ;
}
spin_unlock ( & rtc - > lock ) ;
return ret ;
2008-02-06 12:38:59 +03:00
}
static const struct rtc_class_ops at91_rtc_ops = {
. read_time = at91_rtc_readtime ,
. set_time = at91_rtc_settime ,
. read_alarm = at91_rtc_readalarm ,
. set_alarm = at91_rtc_setalarm ,
. proc = at91_rtc_proc ,
2011-02-25 22:13:55 +03:00
. alarm_irq_enable = at91_rtc_alarm_irq_enable ,
2008-02-06 12:38:59 +03:00
} ;
/*
* Initialize and install RTC driver
*/
2012-12-22 01:09:38 +04:00
static int at91_rtc_probe ( struct platform_device * pdev )
2008-02-06 12:38:59 +03:00
{
struct sam9_rtc * rtc ;
2012-08-14 13:19:22 +04:00
int ret , irq ;
2008-02-06 12:38:59 +03:00
u32 mr ;
2014-09-23 18:41:07 +04:00
unsigned int sclk_rate ;
2019-03-20 15:40:37 +03:00
struct of_phandle_args args ;
2008-02-06 12:38:59 +03:00
2012-08-14 13:19:22 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
2019-07-30 21:15:39 +03:00
if ( irq < 0 )
2012-08-14 13:19:22 +04:00
return irq ;
2013-04-30 03:20:35 +04:00
rtc = devm_kzalloc ( & pdev - > dev , sizeof ( * rtc ) , GFP_KERNEL ) ;
2008-02-06 12:38:59 +03:00
if ( ! rtc )
return - ENOMEM ;
2016-07-25 10:05:11 +03:00
spin_lock_init ( & rtc - > lock ) ;
2012-08-14 13:19:22 +04:00
rtc - > irq = irq ;
2008-03-20 03:01:09 +03:00
/* platform setup code should have handled this; sigh */
if ( ! device_can_wakeup ( & pdev - > dev ) )
device_init_wakeup ( & pdev - > dev , 1 ) ;
2008-02-06 12:38:59 +03:00
platform_set_drvdata ( pdev , rtc ) ;
2019-10-06 13:29:20 +03:00
rtc - > rtt = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-09-23 15:14:09 +04:00
if ( IS_ERR ( rtc - > rtt ) )
return PTR_ERR ( rtc - > rtt ) ;
2019-03-20 15:40:37 +03:00
ret = of_parse_phandle_with_fixed_args ( pdev - > dev . of_node ,
2019-03-20 15:40:42 +03:00
" atmel,rtt-rtc-time-reg " , 1 , 0 ,
& args ) ;
2019-03-20 15:40:37 +03:00
if ( ret )
return ret ;
2014-09-23 15:14:44 +04:00
2019-03-20 15:40:37 +03:00
rtc - > gpbr = syscon_node_to_regmap ( args . np ) ;
rtc - > gpbr_offset = args . args [ 0 ] ;
2014-09-23 15:14:44 +04:00
if ( IS_ERR ( rtc - > gpbr ) ) {
dev_err ( & pdev - > dev , " failed to retrieve gpbr regmap, aborting. \n " ) ;
return - ENOMEM ;
}
2012-02-15 17:24:46 +04:00
2014-09-23 18:41:07 +04:00
rtc - > sclk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( rtc - > sclk ) )
return PTR_ERR ( rtc - > sclk ) ;
ret = clk_prepare_enable ( rtc - > sclk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Could not enable slow clock \n " ) ;
return ret ;
}
2015-07-28 22:51:10 +03:00
sclk_rate = clk_get_rate ( rtc - > sclk ) ;
if ( ! sclk_rate | | sclk_rate > AT91_RTT_RTPRES ) {
dev_err ( & pdev - > dev , " Invalid slow clock rate \n " ) ;
ret = - EINVAL ;
goto err_clk ;
}
2008-02-06 12:38:59 +03:00
mr = rtt_readl ( rtc , MR ) ;
/* unless RTT is counting at 1 Hz, re-initialize it */
2014-09-23 18:41:07 +04:00
if ( ( mr & AT91_RTT_RTPRES ) ! = sclk_rate ) {
mr = AT91_RTT_RTTRST | ( sclk_rate & AT91_RTT_RTPRES ) ;
2008-02-06 12:38:59 +03:00
gpbr_writel ( rtc , 0 ) ;
}
/* disable all interrupts (same as on shutdown path) */
mr & = ~ ( AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN ) ;
rtt_writel ( rtc , MR , mr ) ;
2019-03-20 15:40:38 +03:00
rtc - > rtcdev = devm_rtc_allocate_device ( & pdev - > dev ) ;
2015-07-28 22:46:15 +03:00
if ( IS_ERR ( rtc - > rtcdev ) ) {
ret = PTR_ERR ( rtc - > rtcdev ) ;
goto err_clk ;
}
2008-02-06 12:38:59 +03:00
2019-03-20 15:40:38 +03:00
rtc - > rtcdev - > ops = & at91_rtc_ops ;
2019-03-20 15:40:39 +03:00
rtc - > rtcdev - > range_max = U32_MAX ;
2019-03-20 15:40:38 +03:00
2008-02-06 12:38:59 +03:00
/* register irq handler after we know what name we'll use */
2013-04-30 03:20:35 +04:00
ret = devm_request_irq ( & pdev - > dev , rtc - > irq , at91_rtc_interrupt ,
2015-03-02 12:18:14 +03:00
IRQF_SHARED | IRQF_COND_SUSPEND ,
dev_name ( & rtc - > rtcdev - > dev ) , rtc ) ;
2008-02-06 12:38:59 +03:00
if ( ret ) {
2012-08-14 13:19:22 +04:00
dev_dbg ( & pdev - > dev , " can't share IRQ %d? \n " , rtc - > irq ) ;
2015-07-28 22:46:15 +03:00
goto err_clk ;
2008-02-06 12:38:59 +03:00
}
/* NOTE: sam9260 rev A silicon has a ROM bug which resets the
* RTT on at least some reboots . If you have that chip , you must
* initialize the time from some external source like a GPS , wall
* clock , discrete RTC , etc
*/
if ( gpbr_readl ( rtc ) = = 0 )
dev_warn ( & pdev - > dev , " %s: SET TIME! \n " ,
2019-03-20 15:40:42 +03:00
dev_name ( & rtc - > rtcdev - > dev ) ) ;
2008-02-06 12:38:59 +03:00
2020-11-09 19:34:08 +03:00
return devm_rtc_register_device ( rtc - > rtcdev ) ;
2015-07-28 22:46:15 +03:00
err_clk :
clk_disable_unprepare ( rtc - > sclk ) ;
return ret ;
2008-02-06 12:38:59 +03:00
}
/*
* Disable and remove the RTC driver
*/
2012-12-22 01:09:38 +04:00
static int at91_rtc_remove ( struct platform_device * pdev )
2008-02-06 12:38:59 +03:00
{
struct sam9_rtc * rtc = platform_get_drvdata ( pdev ) ;
u32 mr = rtt_readl ( rtc , MR ) ;
/* disable all interrupts */
rtt_writel ( rtc , MR , mr & ~ ( AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN ) ) ;
2015-07-28 22:47:57 +03:00
clk_disable_unprepare ( rtc - > sclk ) ;
2014-09-23 18:41:07 +04:00
2008-02-06 12:38:59 +03:00
return 0 ;
}
static void at91_rtc_shutdown ( struct platform_device * pdev )
{
struct sam9_rtc * rtc = platform_get_drvdata ( pdev ) ;
u32 mr = rtt_readl ( rtc , MR ) ;
rtc - > imr = mr & ( AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN ) ;
rtt_writel ( rtc , MR , mr & ~ rtc - > imr ) ;
}
2013-04-30 03:20:58 +04:00
# ifdef CONFIG_PM_SLEEP
2008-02-06 12:38:59 +03:00
/* AT91SAM9 RTC Power management control */
2013-04-30 03:20:58 +04:00
static int at91_rtc_suspend ( struct device * dev )
2008-02-06 12:38:59 +03:00
{
2013-04-30 03:20:58 +04:00
struct sam9_rtc * rtc = dev_get_drvdata ( dev ) ;
2008-02-06 12:38:59 +03:00
u32 mr = rtt_readl ( rtc , MR ) ;
/*
* This IRQ is shared with DBGU and other hardware which isn ' t
* necessarily a wakeup event source .
*/
rtc - > imr = mr & ( AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN ) ;
if ( rtc - > imr ) {
2013-04-30 03:20:58 +04:00
if ( device_may_wakeup ( dev ) & & ( mr & AT91_RTT_ALMIEN ) ) {
2015-03-02 12:18:14 +03:00
unsigned long flags ;
2012-08-14 13:19:22 +04:00
enable_irq_wake ( rtc - > irq ) ;
2015-03-02 12:18:14 +03:00
spin_lock_irqsave ( & rtc - > lock , flags ) ;
rtc - > suspended = true ;
spin_unlock_irqrestore ( & rtc - > lock , flags ) ;
2008-02-06 12:38:59 +03:00
/* don't let RTTINC cause wakeups */
if ( mr & AT91_RTT_RTTINCIEN )
rtt_writel ( rtc , MR , mr & ~ AT91_RTT_RTTINCIEN ) ;
2019-03-20 15:40:42 +03:00
} else {
2008-02-06 12:38:59 +03:00
rtt_writel ( rtc , MR , mr & ~ rtc - > imr ) ;
2019-03-20 15:40:42 +03:00
}
2008-02-06 12:38:59 +03:00
}
return 0 ;
}
2013-04-30 03:20:58 +04:00
static int at91_rtc_resume ( struct device * dev )
2008-02-06 12:38:59 +03:00
{
2013-04-30 03:20:58 +04:00
struct sam9_rtc * rtc = dev_get_drvdata ( dev ) ;
2008-02-06 12:38:59 +03:00
u32 mr ;
if ( rtc - > imr ) {
2015-03-02 12:18:14 +03:00
unsigned long flags ;
2013-04-30 03:20:58 +04:00
if ( device_may_wakeup ( dev ) )
2012-08-14 13:19:22 +04:00
disable_irq_wake ( rtc - > irq ) ;
2008-02-06 12:38:59 +03:00
mr = rtt_readl ( rtc , MR ) ;
rtt_writel ( rtc , MR , mr | rtc - > imr ) ;
2015-03-02 12:18:14 +03:00
spin_lock_irqsave ( & rtc - > lock , flags ) ;
rtc - > suspended = false ;
at91_rtc_cache_events ( rtc ) ;
at91_rtc_flush_events ( rtc ) ;
spin_unlock_irqrestore ( & rtc - > lock , flags ) ;
2008-02-06 12:38:59 +03:00
}
return 0 ;
}
# endif
2013-04-30 03:20:58 +04:00
static SIMPLE_DEV_PM_OPS ( at91_rtc_pm_ops , at91_rtc_suspend , at91_rtc_resume ) ;
2014-09-23 15:14:24 +04:00
static const struct of_device_id at91_rtc_dt_ids [ ] = {
{ . compatible = " atmel,at91sam9260-rtt " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , at91_rtc_dt_ids ) ;
2008-02-06 12:38:59 +03:00
static struct platform_driver at91_rtc_driver = {
2012-02-15 16:51:37 +04:00
. probe = at91_rtc_probe ,
2012-12-22 01:09:38 +04:00
. remove = at91_rtc_remove ,
2008-02-06 12:38:59 +03:00
. shutdown = at91_rtc_shutdown ,
2012-02-15 16:51:37 +04:00
. driver = {
. name = " rtc-at91sam9 " ,
2013-04-30 03:20:58 +04:00
. pm = & at91_rtc_pm_ops ,
2014-09-23 15:14:24 +04:00
. of_match_table = of_match_ptr ( at91_rtc_dt_ids ) ,
2012-02-15 16:51:37 +04:00
} ,
2008-02-06 12:38:59 +03:00
} ;
2012-10-05 04:13:54 +04:00
module_platform_driver ( at91_rtc_driver ) ;
2008-02-06 12:38:59 +03:00
MODULE_AUTHOR ( " Michel Benoit " ) ;
MODULE_DESCRIPTION ( " RTC driver for Atmel AT91SAM9x " ) ;
MODULE_LICENSE ( " GPL " ) ;