2006-12-07 07:38:36 +03:00
/*
2014-12-11 02:53:19 +03:00
* TI OMAP Real Time Clock interface for Linux
2006-12-07 07:38:36 +03:00
*
* Copyright ( C ) 2003 MontaVista Software , Inc .
* Author : George G . Davis < gdavis @ mvista . com > or < source @ mvista . com >
*
* Copyright ( C ) 2006 David Brownell ( new RTC framework )
2014-12-11 02:53:22 +03:00
* Copyright ( C ) 2014 Johan Hovold < johan @ kernel . org >
2006-12-07 07:38:36 +03:00
*
* 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/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/rtc.h>
# include <linux/bcd.h>
# include <linux/platform_device.h>
2012-12-18 04:02:15 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2012-12-18 04:02:18 +04:00
# include <linux/pm_runtime.h>
2013-07-04 02:06:00 +04:00
# include <linux/io.h>
2006-12-07 07:38:36 +03:00
2014-12-11 02:53:19 +03:00
/*
* The OMAP RTC is a year / month / day / hours / minutes / seconds BCD clock
2006-12-07 07:38:36 +03:00
* with century - range alarm matching , driven by the 32 kHz clock .
*
* The main user - visible ways it differs from PC RTCs are by omitting
* " don't care " alarm fields and sub - second periodic IRQs , and having
* an autoadjust mechanism to calibrate to the true oscillator rate .
*
* Board - specific wiring options include using split power mode with
* RTC_OFF_NOFF used as the reset signal ( so the RTC won ' t be reset ) ,
* and wiring RTC_WAKE_INT ( so the RTC alarm can wake the system from
2010-10-28 02:33:05 +04:00
* low power modes ) for OMAP1 boards ( OMAP - L138 has this built into
* the SoC ) . See the BOARD - SPECIFIC CUSTOMIZATION comment .
2006-12-07 07:38:36 +03:00
*/
/* RTC registers */
# define OMAP_RTC_SECONDS_REG 0x00
# define OMAP_RTC_MINUTES_REG 0x04
# define OMAP_RTC_HOURS_REG 0x08
# define OMAP_RTC_DAYS_REG 0x0C
# define OMAP_RTC_MONTHS_REG 0x10
# define OMAP_RTC_YEARS_REG 0x14
# define OMAP_RTC_WEEKS_REG 0x18
# define OMAP_RTC_ALARM_SECONDS_REG 0x20
# define OMAP_RTC_ALARM_MINUTES_REG 0x24
# define OMAP_RTC_ALARM_HOURS_REG 0x28
# define OMAP_RTC_ALARM_DAYS_REG 0x2c
# define OMAP_RTC_ALARM_MONTHS_REG 0x30
# define OMAP_RTC_ALARM_YEARS_REG 0x34
# define OMAP_RTC_CTRL_REG 0x40
# define OMAP_RTC_STATUS_REG 0x44
# define OMAP_RTC_INTERRUPTS_REG 0x48
# define OMAP_RTC_COMP_LSB_REG 0x4c
# define OMAP_RTC_COMP_MSB_REG 0x50
# define OMAP_RTC_OSC_REG 0x54
2012-12-18 04:02:11 +04:00
# define OMAP_RTC_KICK0_REG 0x6c
# define OMAP_RTC_KICK1_REG 0x70
2013-09-12 01:24:18 +04:00
# define OMAP_RTC_IRQWAKEEN 0x7c
2014-12-11 02:53:13 +03:00
# define OMAP_RTC_ALARM2_SECONDS_REG 0x80
# define OMAP_RTC_ALARM2_MINUTES_REG 0x84
# define OMAP_RTC_ALARM2_HOURS_REG 0x88
# define OMAP_RTC_ALARM2_DAYS_REG 0x8c
# define OMAP_RTC_ALARM2_MONTHS_REG 0x90
# define OMAP_RTC_ALARM2_YEARS_REG 0x94
# define OMAP_RTC_PMIC_REG 0x98
2006-12-07 07:38:36 +03:00
/* OMAP_RTC_CTRL_REG bit fields: */
2014-06-07 01:36:05 +04:00
# define OMAP_RTC_CTRL_SPLIT BIT(7)
# define OMAP_RTC_CTRL_DISABLE BIT(6)
# define OMAP_RTC_CTRL_SET_32_COUNTER BIT(5)
# define OMAP_RTC_CTRL_TEST BIT(4)
# define OMAP_RTC_CTRL_MODE_12_24 BIT(3)
# define OMAP_RTC_CTRL_AUTO_COMP BIT(2)
# define OMAP_RTC_CTRL_ROUND_30S BIT(1)
# define OMAP_RTC_CTRL_STOP BIT(0)
2006-12-07 07:38:36 +03:00
/* OMAP_RTC_STATUS_REG bit fields: */
2014-06-07 01:36:05 +04:00
# define OMAP_RTC_STATUS_POWER_UP BIT(7)
2014-12-11 02:53:13 +03:00
# define OMAP_RTC_STATUS_ALARM2 BIT(7)
2014-06-07 01:36:05 +04:00
# define OMAP_RTC_STATUS_ALARM BIT(6)
# define OMAP_RTC_STATUS_1D_EVENT BIT(5)
# define OMAP_RTC_STATUS_1H_EVENT BIT(4)
# define OMAP_RTC_STATUS_1M_EVENT BIT(3)
# define OMAP_RTC_STATUS_1S_EVENT BIT(2)
# define OMAP_RTC_STATUS_RUN BIT(1)
# define OMAP_RTC_STATUS_BUSY BIT(0)
2006-12-07 07:38:36 +03:00
/* OMAP_RTC_INTERRUPTS_REG bit fields: */
2014-12-11 02:53:13 +03:00
# define OMAP_RTC_INTERRUPTS_IT_ALARM2 BIT(4)
2014-06-07 01:36:05 +04:00
# define OMAP_RTC_INTERRUPTS_IT_ALARM BIT(3)
# define OMAP_RTC_INTERRUPTS_IT_TIMER BIT(2)
2006-12-07 07:38:36 +03:00
2014-06-07 01:36:06 +04:00
/* OMAP_RTC_OSC_REG bit fields: */
# define OMAP_RTC_OSC_32KCLK_EN BIT(6)
2013-09-12 01:24:18 +04:00
/* OMAP_RTC_IRQWAKEEN bit fields: */
2014-06-07 01:36:05 +04:00
# define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1)
2013-09-12 01:24:18 +04:00
2014-12-11 02:53:13 +03:00
/* OMAP_RTC_PMIC bit fields: */
# define OMAP_RTC_PMIC_POWER_EN_EN BIT(16)
2012-12-18 04:02:11 +04:00
/* OMAP_RTC_KICKER values */
# define KICK0_VALUE 0x83e70b13
# define KICK1_VALUE 0x95a4f1e0
2014-12-11 02:53:01 +03:00
struct omap_rtc_device_type {
bool has_32kclk_en ;
bool has_kicker ;
bool has_irqwakeen ;
2014-12-11 02:53:13 +03:00
bool has_pmic_mode ;
2014-12-11 02:53:04 +03:00
bool has_power_up_reset ;
2014-12-11 02:53:01 +03:00
} ;
2014-06-07 01:36:06 +04:00
2014-12-11 02:52:55 +03:00
struct omap_rtc {
struct rtc_device * rtc ;
void __iomem * base ;
int irq_alarm ;
int irq_timer ;
u8 interrupts_reg ;
2014-12-11 02:53:13 +03:00
bool is_pmic_controller ;
2014-12-11 02:53:01 +03:00
const struct omap_rtc_device_type * type ;
2014-12-11 02:52:55 +03:00
} ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:52:55 +03:00
static inline u8 rtc_read ( struct omap_rtc * rtc , unsigned int reg )
{
return readb ( rtc - > base + reg ) ;
}
2012-12-18 04:02:11 +04:00
2014-12-11 02:53:10 +03:00
static inline u32 rtc_readl ( struct omap_rtc * rtc , unsigned int reg )
{
return readl ( rtc - > base + reg ) ;
}
2014-12-11 02:52:55 +03:00
static inline void rtc_write ( struct omap_rtc * rtc , unsigned int reg , u8 val )
{
writeb ( val , rtc - > base + reg ) ;
}
2006-12-07 07:38:36 +03:00
2014-12-11 02:52:55 +03:00
static inline void rtc_writel ( struct omap_rtc * rtc , unsigned int reg , u32 val )
{
writel ( val , rtc - > base + reg ) ;
}
2006-12-07 07:38:36 +03:00
2014-12-11 02:53:19 +03:00
/*
* We rely on the rtc framework to handle locking ( rtc - > ops_lock ) ,
2006-12-07 07:38:36 +03:00
* so the only other requirement is that register accesses which
* require BUSY to be clear are made with IRQs locally disabled
*/
2014-12-11 02:52:55 +03:00
static void rtc_wait_not_busy ( struct omap_rtc * rtc )
2006-12-07 07:38:36 +03:00
{
2014-12-11 02:53:19 +03:00
int count ;
u8 status ;
2006-12-07 07:38:36 +03:00
/* BUSY may stay active for 1/32768 second (~30 usec) */
for ( count = 0 ; count < 50 ; count + + ) {
2014-12-11 02:52:55 +03:00
status = rtc_read ( rtc , OMAP_RTC_STATUS_REG ) ;
2014-12-11 02:53:19 +03:00
if ( ! ( status & OMAP_RTC_STATUS_BUSY ) )
2006-12-07 07:38:36 +03:00
break ;
udelay ( 1 ) ;
}
/* now we have ~15 usec to read/write various registers */
}
2014-12-11 02:52:55 +03:00
static irqreturn_t rtc_irq ( int irq , void * dev_id )
2006-12-07 07:38:36 +03:00
{
2014-12-11 02:53:19 +03:00
struct omap_rtc * rtc = dev_id ;
unsigned long events = 0 ;
u8 irq_data ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:52:55 +03:00
irq_data = rtc_read ( rtc , OMAP_RTC_STATUS_REG ) ;
2006-12-07 07:38:36 +03:00
/* alarm irq? */
if ( irq_data & OMAP_RTC_STATUS_ALARM ) {
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_STATUS_REG , OMAP_RTC_STATUS_ALARM ) ;
2006-12-07 07:38:36 +03:00
events | = RTC_IRQF | RTC_AF ;
}
/* 1/sec periodic/update irq? */
if ( irq_data & OMAP_RTC_STATUS_1S_EVENT )
events | = RTC_IRQF | RTC_UF ;
2014-12-11 02:52:55 +03:00
rtc_update_irq ( rtc - > rtc , 1 , events ) ;
2006-12-07 07:38:36 +03:00
return IRQ_HANDLED ;
}
2011-02-03 04:02:41 +03:00
static int omap_rtc_alarm_irq_enable ( struct device * dev , unsigned int enabled )
{
2014-12-11 02:52:55 +03:00
struct omap_rtc * rtc = dev_get_drvdata ( dev ) ;
2014-06-07 01:36:12 +04:00
u8 reg , irqwake_reg = 0 ;
2011-02-03 04:02:41 +03:00
local_irq_disable ( ) ;
2014-12-11 02:52:55 +03:00
rtc_wait_not_busy ( rtc ) ;
reg = rtc_read ( rtc , OMAP_RTC_INTERRUPTS_REG ) ;
2014-12-11 02:53:01 +03:00
if ( rtc - > type - > has_irqwakeen )
2014-12-11 02:52:55 +03:00
irqwake_reg = rtc_read ( rtc , OMAP_RTC_IRQWAKEEN ) ;
2014-06-07 01:36:12 +04:00
if ( enabled ) {
2011-02-03 04:02:41 +03:00
reg | = OMAP_RTC_INTERRUPTS_IT_ALARM ;
2014-06-07 01:36:12 +04:00
irqwake_reg | = OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN ;
} else {
2011-02-03 04:02:41 +03:00
reg & = ~ OMAP_RTC_INTERRUPTS_IT_ALARM ;
2014-06-07 01:36:12 +04:00
irqwake_reg & = ~ OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN ;
}
2014-12-11 02:52:55 +03:00
rtc_wait_not_busy ( rtc ) ;
rtc_write ( rtc , OMAP_RTC_INTERRUPTS_REG , reg ) ;
2014-12-11 02:53:01 +03:00
if ( rtc - > type - > has_irqwakeen )
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_IRQWAKEEN , irqwake_reg ) ;
2011-02-03 04:02:41 +03:00
local_irq_enable ( ) ;
return 0 ;
}
2006-12-07 07:38:36 +03:00
/* this hardware doesn't support "don't care" alarm fields */
static int tm2bcd ( struct rtc_time * tm )
{
if ( rtc_valid_tm ( tm ) ! = 0 )
return - EINVAL ;
2008-10-19 07:28:41 +04:00
tm - > tm_sec = bin2bcd ( tm - > tm_sec ) ;
tm - > tm_min = bin2bcd ( tm - > tm_min ) ;
tm - > tm_hour = bin2bcd ( tm - > tm_hour ) ;
tm - > tm_mday = bin2bcd ( tm - > tm_mday ) ;
2006-12-07 07:38:36 +03:00
2008-10-19 07:28:41 +04:00
tm - > tm_mon = bin2bcd ( tm - > tm_mon + 1 ) ;
2006-12-07 07:38:36 +03:00
/* epoch == 1900 */
if ( tm - > tm_year < 100 | | tm - > tm_year > 199 )
return - EINVAL ;
2008-10-19 07:28:41 +04:00
tm - > tm_year = bin2bcd ( tm - > tm_year - 100 ) ;
2006-12-07 07:38:36 +03:00
return 0 ;
}
static void bcd2tm ( struct rtc_time * tm )
{
2008-10-19 07:28:41 +04:00
tm - > tm_sec = bcd2bin ( tm - > tm_sec ) ;
tm - > tm_min = bcd2bin ( tm - > tm_min ) ;
tm - > tm_hour = bcd2bin ( tm - > tm_hour ) ;
tm - > tm_mday = bcd2bin ( tm - > tm_mday ) ;
tm - > tm_mon = bcd2bin ( tm - > tm_mon ) - 1 ;
2006-12-07 07:38:36 +03:00
/* epoch == 1900 */
2008-10-19 07:28:41 +04:00
tm - > tm_year = bcd2bin ( tm - > tm_year ) + 100 ;
2006-12-07 07:38:36 +03:00
}
2014-12-11 02:53:07 +03:00
static void omap_rtc_read_time_raw ( struct omap_rtc * rtc , struct rtc_time * tm )
2006-12-07 07:38:36 +03:00
{
2014-12-11 02:52:55 +03:00
tm - > tm_sec = rtc_read ( rtc , OMAP_RTC_SECONDS_REG ) ;
tm - > tm_min = rtc_read ( rtc , OMAP_RTC_MINUTES_REG ) ;
tm - > tm_hour = rtc_read ( rtc , OMAP_RTC_HOURS_REG ) ;
tm - > tm_mday = rtc_read ( rtc , OMAP_RTC_DAYS_REG ) ;
tm - > tm_mon = rtc_read ( rtc , OMAP_RTC_MONTHS_REG ) ;
tm - > tm_year = rtc_read ( rtc , OMAP_RTC_YEARS_REG ) ;
2014-12-11 02:53:07 +03:00
}
static int omap_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
struct omap_rtc * rtc = dev_get_drvdata ( dev ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:53:07 +03:00
/* we don't report wday/yday/isdst ... */
local_irq_disable ( ) ;
rtc_wait_not_busy ( rtc ) ;
omap_rtc_read_time_raw ( rtc , tm ) ;
2006-12-07 07:38:36 +03:00
local_irq_enable ( ) ;
bcd2tm ( tm ) ;
2014-12-11 02:53:19 +03:00
2006-12-07 07:38:36 +03:00
return 0 ;
}
static int omap_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
2014-12-11 02:52:55 +03:00
struct omap_rtc * rtc = dev_get_drvdata ( dev ) ;
2006-12-07 07:38:36 +03:00
if ( tm2bcd ( tm ) < 0 )
return - EINVAL ;
2014-12-11 02:53:19 +03:00
2006-12-07 07:38:36 +03:00
local_irq_disable ( ) ;
2014-12-11 02:52:55 +03:00
rtc_wait_not_busy ( rtc ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_YEARS_REG , tm - > tm_year ) ;
rtc_write ( rtc , OMAP_RTC_MONTHS_REG , tm - > tm_mon ) ;
rtc_write ( rtc , OMAP_RTC_DAYS_REG , tm - > tm_mday ) ;
rtc_write ( rtc , OMAP_RTC_HOURS_REG , tm - > tm_hour ) ;
rtc_write ( rtc , OMAP_RTC_MINUTES_REG , tm - > tm_min ) ;
rtc_write ( rtc , OMAP_RTC_SECONDS_REG , tm - > tm_sec ) ;
2006-12-07 07:38:36 +03:00
local_irq_enable ( ) ;
return 0 ;
}
static int omap_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
2014-12-11 02:52:55 +03:00
struct omap_rtc * rtc = dev_get_drvdata ( dev ) ;
2014-12-11 02:53:19 +03:00
u8 interrupts ;
2014-12-11 02:52:55 +03:00
2006-12-07 07:38:36 +03:00
local_irq_disable ( ) ;
2014-12-11 02:52:55 +03:00
rtc_wait_not_busy ( rtc ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:52:55 +03:00
alm - > time . tm_sec = rtc_read ( rtc , OMAP_RTC_ALARM_SECONDS_REG ) ;
alm - > time . tm_min = rtc_read ( rtc , OMAP_RTC_ALARM_MINUTES_REG ) ;
alm - > time . tm_hour = rtc_read ( rtc , OMAP_RTC_ALARM_HOURS_REG ) ;
alm - > time . tm_mday = rtc_read ( rtc , OMAP_RTC_ALARM_DAYS_REG ) ;
alm - > time . tm_mon = rtc_read ( rtc , OMAP_RTC_ALARM_MONTHS_REG ) ;
alm - > time . tm_year = rtc_read ( rtc , OMAP_RTC_ALARM_YEARS_REG ) ;
2006-12-07 07:38:36 +03:00
local_irq_enable ( ) ;
bcd2tm ( & alm - > time ) ;
2014-12-11 02:53:19 +03:00
interrupts = rtc_read ( rtc , OMAP_RTC_INTERRUPTS_REG ) ;
alm - > enabled = ! ! ( interrupts & OMAP_RTC_INTERRUPTS_IT_ALARM ) ;
2006-12-07 07:38:36 +03:00
return 0 ;
}
static int omap_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
2014-12-11 02:52:55 +03:00
struct omap_rtc * rtc = dev_get_drvdata ( dev ) ;
2014-06-07 01:36:12 +04:00
u8 reg , irqwake_reg = 0 ;
2006-12-07 07:38:36 +03:00
if ( tm2bcd ( & alm - > time ) < 0 )
return - EINVAL ;
local_irq_disable ( ) ;
2014-12-11 02:52:55 +03:00
rtc_wait_not_busy ( rtc ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_ALARM_YEARS_REG , alm - > time . tm_year ) ;
rtc_write ( rtc , OMAP_RTC_ALARM_MONTHS_REG , alm - > time . tm_mon ) ;
rtc_write ( rtc , OMAP_RTC_ALARM_DAYS_REG , alm - > time . tm_mday ) ;
rtc_write ( rtc , OMAP_RTC_ALARM_HOURS_REG , alm - > time . tm_hour ) ;
rtc_write ( rtc , OMAP_RTC_ALARM_MINUTES_REG , alm - > time . tm_min ) ;
rtc_write ( rtc , OMAP_RTC_ALARM_SECONDS_REG , alm - > time . tm_sec ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:52:55 +03:00
reg = rtc_read ( rtc , OMAP_RTC_INTERRUPTS_REG ) ;
2014-12-11 02:53:01 +03:00
if ( rtc - > type - > has_irqwakeen )
2014-12-11 02:52:55 +03:00
irqwake_reg = rtc_read ( rtc , OMAP_RTC_IRQWAKEEN ) ;
2014-06-07 01:36:12 +04:00
if ( alm - > enabled ) {
2006-12-07 07:38:36 +03:00
reg | = OMAP_RTC_INTERRUPTS_IT_ALARM ;
2014-06-07 01:36:12 +04:00
irqwake_reg | = OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN ;
} else {
2006-12-07 07:38:36 +03:00
reg & = ~ OMAP_RTC_INTERRUPTS_IT_ALARM ;
2014-06-07 01:36:12 +04:00
irqwake_reg & = ~ OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN ;
}
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_INTERRUPTS_REG , reg ) ;
2014-12-11 02:53:01 +03:00
if ( rtc - > type - > has_irqwakeen )
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_IRQWAKEEN , irqwake_reg ) ;
2006-12-07 07:38:36 +03:00
local_irq_enable ( ) ;
return 0 ;
}
2014-12-11 02:53:13 +03:00
static struct omap_rtc * omap_rtc_power_off_rtc ;
/*
* omap_rtc_poweroff : RTC - controlled power off
*
* The RTC can be used to control an external PMIC via the pmic_power_en pin ,
* which can be configured to transition to OFF on ALARM2 events .
*
* Notes :
* The two - second alarm offset is the shortest offset possible as the alarm
* registers must be set before the next timer update and the offset
* calculation is too heavy for everything to be done within a single access
* period ( ~ 15 us ) .
*
* Called with local interrupts disabled .
*/
static void omap_rtc_power_off ( void )
{
struct omap_rtc * rtc = omap_rtc_power_off_rtc ;
struct rtc_time tm ;
unsigned long now ;
u32 val ;
/* enable pmic_power_en control */
val = rtc_readl ( rtc , OMAP_RTC_PMIC_REG ) ;
rtc_writel ( rtc , OMAP_RTC_PMIC_REG , val | OMAP_RTC_PMIC_POWER_EN_EN ) ;
/* set alarm two seconds from now */
omap_rtc_read_time_raw ( rtc , & tm ) ;
bcd2tm ( & tm ) ;
rtc_tm_to_time ( & tm , & now ) ;
rtc_time_to_tm ( now + 2 , & tm ) ;
if ( tm2bcd ( & tm ) < 0 ) {
dev_err ( & rtc - > rtc - > dev , " power off failed \n " ) ;
return ;
}
rtc_wait_not_busy ( rtc ) ;
rtc_write ( rtc , OMAP_RTC_ALARM2_SECONDS_REG , tm . tm_sec ) ;
rtc_write ( rtc , OMAP_RTC_ALARM2_MINUTES_REG , tm . tm_min ) ;
rtc_write ( rtc , OMAP_RTC_ALARM2_HOURS_REG , tm . tm_hour ) ;
rtc_write ( rtc , OMAP_RTC_ALARM2_DAYS_REG , tm . tm_mday ) ;
rtc_write ( rtc , OMAP_RTC_ALARM2_MONTHS_REG , tm . tm_mon ) ;
rtc_write ( rtc , OMAP_RTC_ALARM2_YEARS_REG , tm . tm_year ) ;
/*
* enable ALARM2 interrupt
*
* NOTE : this fails on AM3352 if rtc_write ( writeb ) is used
*/
val = rtc_read ( rtc , OMAP_RTC_INTERRUPTS_REG ) ;
rtc_writel ( rtc , OMAP_RTC_INTERRUPTS_REG ,
val | OMAP_RTC_INTERRUPTS_IT_ALARM2 ) ;
/*
* Wait for alarm to trigger ( within two seconds ) and external PMIC to
* power off the system . Add a 500 ms margin for external latencies
* ( e . g . debounce circuits ) .
*/
mdelay ( 2500 ) ;
}
2006-12-07 07:38:36 +03:00
static struct rtc_class_ops omap_rtc_ops = {
. read_time = omap_rtc_read_time ,
. set_time = omap_rtc_set_time ,
. read_alarm = omap_rtc_read_alarm ,
. set_alarm = omap_rtc_set_alarm ,
2011-02-03 04:02:41 +03:00
. alarm_irq_enable = omap_rtc_alarm_irq_enable ,
2006-12-07 07:38:36 +03:00
} ;
2014-12-11 02:53:01 +03:00
static const struct omap_rtc_device_type omap_rtc_default_type = {
2014-12-11 02:53:04 +03:00
. has_power_up_reset = true ,
2014-12-11 02:53:01 +03:00
} ;
static const struct omap_rtc_device_type omap_rtc_am3352_type = {
. has_32kclk_en = true ,
. has_kicker = true ,
. has_irqwakeen = true ,
2014-12-11 02:53:13 +03:00
. has_pmic_mode = true ,
2014-12-11 02:53:01 +03:00
} ;
static const struct omap_rtc_device_type omap_rtc_da830_type = {
. has_kicker = true ,
} ;
2012-12-18 04:02:15 +04:00
2014-12-11 02:53:01 +03:00
static const struct platform_device_id omap_rtc_id_table [ ] = {
2012-12-18 04:02:11 +04:00
{
2014-12-11 02:52:58 +03:00
. name = " omap_rtc " ,
2014-12-11 02:53:01 +03:00
. driver_data = ( kernel_ulong_t ) & omap_rtc_default_type ,
} , {
2013-09-12 01:24:18 +04:00
. name = " am3352-rtc " ,
2014-12-11 02:53:01 +03:00
. driver_data = ( kernel_ulong_t ) & omap_rtc_am3352_type ,
} , {
2012-12-18 04:02:11 +04:00
. name = " da830-rtc " ,
2014-12-11 02:53:01 +03:00
. driver_data = ( kernel_ulong_t ) & omap_rtc_da830_type ,
} , {
/* sentinel */
}
2012-12-18 04:02:11 +04:00
} ;
2014-12-11 02:53:01 +03:00
MODULE_DEVICE_TABLE ( platform , omap_rtc_id_table ) ;
2012-12-18 04:02:11 +04:00
2012-12-18 04:02:15 +04:00
static const struct of_device_id omap_rtc_of_match [ ] = {
2014-12-11 02:53:01 +03:00
{
. compatible = " ti,am3352-rtc " ,
. data = & omap_rtc_am3352_type ,
} , {
. compatible = " ti,da830-rtc " ,
. data = & omap_rtc_da830_type ,
} , {
/* sentinel */
}
2012-12-18 04:02:15 +04:00
} ;
MODULE_DEVICE_TABLE ( of , omap_rtc_of_match ) ;
2008-07-24 08:30:38 +04:00
static int __init omap_rtc_probe ( struct platform_device * pdev )
2006-12-07 07:38:36 +03:00
{
2014-12-11 02:53:19 +03:00
struct omap_rtc * rtc ;
struct resource * res ;
u8 reg , mask , new_ctrl ;
2012-12-18 04:02:11 +04:00
const struct platform_device_id * id_entry ;
2012-12-18 04:02:15 +04:00
const struct of_device_id * of_id ;
2014-12-11 02:52:40 +03:00
int ret ;
2012-12-18 04:02:15 +04:00
2014-12-11 02:52:55 +03:00
rtc = devm_kzalloc ( & pdev - > dev , sizeof ( * rtc ) , GFP_KERNEL ) ;
if ( ! rtc )
return - ENOMEM ;
2012-12-18 04:02:15 +04:00
of_id = of_match_device ( omap_rtc_of_match , & pdev - > dev ) ;
2014-12-11 02:53:01 +03:00
if ( of_id ) {
rtc - > type = of_id - > data ;
2014-12-11 02:53:13 +03:00
rtc - > is_pmic_controller = rtc - > type - > has_pmic_mode & &
of_property_read_bool ( pdev - > dev . of_node ,
" ti,system-power-controller " ) ;
2014-12-11 02:53:01 +03:00
} else {
id_entry = platform_get_device_id ( pdev ) ;
rtc - > type = ( void * ) id_entry - > driver_data ;
2014-06-07 01:36:04 +04:00
}
2014-12-11 02:52:55 +03:00
rtc - > irq_timer = platform_get_irq ( pdev , 0 ) ;
if ( rtc - > irq_timer < = 0 )
2006-12-07 07:38:36 +03:00
return - ENOENT ;
2014-12-11 02:52:55 +03:00
rtc - > irq_alarm = platform_get_irq ( pdev , 1 ) ;
if ( rtc - > irq_alarm < = 0 )
2006-12-07 07:38:36 +03:00
return - ENOENT ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2014-12-11 02:52:55 +03:00
rtc - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( rtc - > base ) )
return PTR_ERR ( rtc - > base ) ;
platform_set_drvdata ( pdev , rtc ) ;
2009-12-16 03:46:11 +03:00
2012-12-18 04:02:18 +04:00
/* Enable the clock/module so that we can access the registers */
pm_runtime_enable ( & pdev - > dev ) ;
pm_runtime_get_sync ( & pdev - > dev ) ;
2014-12-11 02:53:01 +03:00
if ( rtc - > type - > has_kicker ) {
2014-12-11 02:52:55 +03:00
rtc_writel ( rtc , OMAP_RTC_KICK0_REG , KICK0_VALUE ) ;
rtc_writel ( rtc , OMAP_RTC_KICK1_REG , KICK1_VALUE ) ;
2012-12-18 04:02:11 +04:00
}
2014-12-11 02:52:36 +03:00
/*
* disable interrupts
*
* NOTE : ALARM2 is not cleared on AM3352 if rtc_write ( writeb ) is used
2006-12-07 07:38:36 +03:00
*/
2014-12-11 02:52:55 +03:00
rtc_writel ( rtc , OMAP_RTC_INTERRUPTS_REG , 0 ) ;
2006-12-07 07:38:36 +03:00
2014-06-07 01:36:06 +04:00
/* enable RTC functional clock */
2014-12-11 02:53:01 +03:00
if ( rtc - > type - > has_32kclk_en ) {
2014-12-11 02:52:55 +03:00
reg = rtc_read ( rtc , OMAP_RTC_OSC_REG ) ;
rtc_writel ( rtc , OMAP_RTC_OSC_REG ,
reg | OMAP_RTC_OSC_32KCLK_EN ) ;
2014-12-11 02:52:30 +03:00
}
2014-06-07 01:36:06 +04:00
2006-12-07 07:38:36 +03:00
/* clear old status */
2014-12-11 02:52:55 +03:00
reg = rtc_read ( rtc , OMAP_RTC_STATUS_REG ) ;
2014-12-11 02:53:04 +03:00
mask = OMAP_RTC_STATUS_ALARM ;
2014-12-11 02:53:13 +03:00
if ( rtc - > type - > has_pmic_mode )
mask | = OMAP_RTC_STATUS_ALARM2 ;
2014-12-11 02:53:04 +03:00
if ( rtc - > type - > has_power_up_reset ) {
mask | = OMAP_RTC_STATUS_POWER_UP ;
if ( reg & OMAP_RTC_STATUS_POWER_UP )
dev_info ( & pdev - > dev , " RTC power up reset detected \n " ) ;
2006-12-07 07:38:36 +03:00
}
2014-12-11 02:53:04 +03:00
if ( reg & mask )
rtc_write ( rtc , OMAP_RTC_STATUS_REG , reg & mask ) ;
2006-12-07 07:38:36 +03:00
/* On boards with split power, RTC_ON_NOFF won't reset the RTC */
2014-12-11 02:52:55 +03:00
reg = rtc_read ( rtc , OMAP_RTC_CTRL_REG ) ;
2014-12-11 02:53:19 +03:00
if ( reg & OMAP_RTC_CTRL_STOP )
2014-12-11 02:52:49 +03:00
dev_info ( & pdev - > dev , " already running \n " ) ;
2006-12-07 07:38:36 +03:00
/* force to 24 hour mode */
2014-12-11 02:53:19 +03:00
new_ctrl = reg & ( OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP ) ;
2006-12-07 07:38:36 +03:00
new_ctrl | = OMAP_RTC_CTRL_STOP ;
2014-12-11 02:53:19 +03:00
/*
* BOARD - SPECIFIC CUSTOMIZATION CAN GO HERE :
2006-12-07 07:38:36 +03:00
*
2010-10-28 02:33:05 +04:00
* - Device wake - up capability setting should come through chip
* init logic . OMAP1 boards should initialize the " wakeup capable "
* flag in the platform device if the board is wired right for
* being woken up by RTC alarm . For OMAP - L138 , this capability
* is built into the SoC by the " Deep Sleep " capability .
2006-12-07 07:38:36 +03:00
*
* - Boards wired so RTC_ON_nOFF is used as the reset signal ,
* rather than nPWRON_RESET , should forcibly enable split
* power mode . ( Some chip errata report that RTC_CTRL_SPLIT
* is write - only , and always reads as zero . . . )
*/
2014-12-11 02:53:19 +03:00
if ( new_ctrl & OMAP_RTC_CTRL_SPLIT )
2014-12-11 02:52:49 +03:00
dev_info ( & pdev - > dev , " split power mode \n " ) ;
2006-12-07 07:38:36 +03:00
if ( reg ! = new_ctrl )
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_CTRL_REG , new_ctrl ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:52:43 +03:00
device_init_wakeup ( & pdev - > dev , true ) ;
2014-12-11 02:52:55 +03:00
rtc - > rtc = devm_rtc_device_register ( & pdev - > dev , pdev - > name ,
2014-12-11 02:52:43 +03:00
& omap_rtc_ops , THIS_MODULE ) ;
2014-12-11 02:52:55 +03:00
if ( IS_ERR ( rtc - > rtc ) ) {
ret = PTR_ERR ( rtc - > rtc ) ;
2014-12-11 02:52:43 +03:00
goto err ;
}
/* handle periodic and alarm irqs */
2014-12-11 02:52:55 +03:00
ret = devm_request_irq ( & pdev - > dev , rtc - > irq_timer , rtc_irq , 0 ,
dev_name ( & rtc - > rtc - > dev ) , rtc ) ;
2014-12-11 02:52:43 +03:00
if ( ret )
goto err ;
2014-12-11 02:52:55 +03:00
if ( rtc - > irq_timer ! = rtc - > irq_alarm ) {
ret = devm_request_irq ( & pdev - > dev , rtc - > irq_alarm , rtc_irq , 0 ,
dev_name ( & rtc - > rtc - > dev ) , rtc ) ;
2014-12-11 02:52:43 +03:00
if ( ret )
goto err ;
}
2014-12-11 02:53:13 +03:00
if ( rtc - > is_pmic_controller ) {
if ( ! pm_power_off ) {
omap_rtc_power_off_rtc = rtc ;
pm_power_off = omap_rtc_power_off ;
}
}
2006-12-07 07:38:36 +03:00
return 0 ;
2014-12-11 02:52:40 +03:00
err :
2014-12-11 02:52:33 +03:00
device_init_wakeup ( & pdev - > dev , false ) ;
2014-12-11 02:53:01 +03:00
if ( rtc - > type - > has_kicker )
2014-12-11 02:52:55 +03:00
rtc_writel ( rtc , OMAP_RTC_KICK0_REG , 0 ) ;
2012-12-18 04:02:18 +04:00
pm_runtime_put_sync ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2014-12-11 02:52:40 +03:00
return ret ;
2006-12-07 07:38:36 +03:00
}
2008-07-24 08:30:38 +04:00
static int __exit omap_rtc_remove ( struct platform_device * pdev )
2006-12-07 07:38:36 +03:00
{
2014-12-11 02:52:55 +03:00
struct omap_rtc * rtc = platform_get_drvdata ( pdev ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:53:13 +03:00
if ( pm_power_off = = omap_rtc_power_off & &
omap_rtc_power_off_rtc = = rtc ) {
pm_power_off = NULL ;
omap_rtc_power_off_rtc = NULL ;
}
2006-12-07 07:38:36 +03:00
device_init_wakeup ( & pdev - > dev , 0 ) ;
/* leave rtc running, but disable irqs */
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_INTERRUPTS_REG , 0 ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:53:01 +03:00
if ( rtc - > type - > has_kicker )
2014-12-11 02:52:55 +03:00
rtc_writel ( rtc , OMAP_RTC_KICK0_REG , 0 ) ;
2012-12-18 04:02:18 +04:00
/* Disable the clock/module */
pm_runtime_put_sync ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2006-12-07 07:38:36 +03:00
return 0 ;
}
2013-04-30 03:21:01 +04:00
# ifdef CONFIG_PM_SLEEP
static int omap_rtc_suspend ( struct device * dev )
2006-12-07 07:38:36 +03:00
{
2014-12-11 02:52:55 +03:00
struct omap_rtc * rtc = dev_get_drvdata ( dev ) ;
rtc - > interrupts_reg = rtc_read ( rtc , OMAP_RTC_INTERRUPTS_REG ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:53:19 +03:00
/*
* FIXME : the RTC alarm is not currently acting as a wakeup event
2013-09-12 01:24:18 +04:00
* source on some platforms , and in fact this enable ( ) call is just
* saving a flag that ' s never used . . .
2006-12-07 07:38:36 +03:00
*/
2014-06-07 01:36:12 +04:00
if ( device_may_wakeup ( dev ) )
2014-12-11 02:52:55 +03:00
enable_irq_wake ( rtc - > irq_alarm ) ;
2014-06-07 01:36:12 +04:00
else
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_INTERRUPTS_REG , 0 ) ;
2006-12-07 07:38:36 +03:00
2012-12-18 04:02:18 +04:00
/* Disable the clock/module */
2013-04-30 03:21:01 +04:00
pm_runtime_put_sync ( dev ) ;
2012-12-18 04:02:18 +04:00
2006-12-07 07:38:36 +03:00
return 0 ;
}
2013-04-30 03:21:01 +04:00
static int omap_rtc_resume ( struct device * dev )
2006-12-07 07:38:36 +03:00
{
2014-12-11 02:52:55 +03:00
struct omap_rtc * rtc = dev_get_drvdata ( dev ) ;
2012-12-18 04:02:18 +04:00
/* Enable the clock/module so that we can access the registers */
2013-04-30 03:21:01 +04:00
pm_runtime_get_sync ( dev ) ;
2012-12-18 04:02:18 +04:00
2014-06-07 01:36:12 +04:00
if ( device_may_wakeup ( dev ) )
2014-12-11 02:52:55 +03:00
disable_irq_wake ( rtc - > irq_alarm ) ;
2014-06-07 01:36:12 +04:00
else
2014-12-11 02:52:55 +03:00
rtc_write ( rtc , OMAP_RTC_INTERRUPTS_REG , rtc - > interrupts_reg ) ;
2014-06-07 01:36:12 +04:00
2006-12-07 07:38:36 +03:00
return 0 ;
}
# endif
2013-04-30 03:21:01 +04:00
static SIMPLE_DEV_PM_OPS ( omap_rtc_pm_ops , omap_rtc_suspend , omap_rtc_resume ) ;
2006-12-07 07:38:36 +03:00
static void omap_rtc_shutdown ( struct platform_device * pdev )
{
2014-12-11 02:52:55 +03:00
struct omap_rtc * rtc = platform_get_drvdata ( pdev ) ;
2014-12-11 02:53:16 +03:00
u8 mask ;
2014-12-11 02:52:55 +03:00
2014-12-11 02:53:16 +03:00
/*
* Keep the ALARM interrupt enabled to allow the system to power up on
* alarm events .
*/
mask = rtc_read ( rtc , OMAP_RTC_INTERRUPTS_REG ) ;
mask & = OMAP_RTC_INTERRUPTS_IT_ALARM ;
rtc_write ( rtc , OMAP_RTC_INTERRUPTS_REG , mask ) ;
2006-12-07 07:38:36 +03:00
}
static struct platform_driver omap_rtc_driver = {
2008-07-24 08:30:38 +04:00
. remove = __exit_p ( omap_rtc_remove ) ,
2006-12-07 07:38:36 +03:00
. shutdown = omap_rtc_shutdown ,
. driver = {
2014-12-11 02:52:58 +03:00
. name = " omap_rtc " ,
2006-12-07 07:38:36 +03:00
. owner = THIS_MODULE ,
2013-04-30 03:21:01 +04:00
. pm = & omap_rtc_pm_ops ,
2013-11-13 03:10:55 +04:00
. of_match_table = omap_rtc_of_match ,
2006-12-07 07:38:36 +03:00
} ,
2014-12-11 02:53:01 +03:00
. id_table = omap_rtc_id_table ,
2006-12-07 07:38:36 +03:00
} ;
2013-04-30 03:18:46 +04:00
module_platform_driver_probe ( omap_rtc_driver , omap_rtc_probe ) ;
2006-12-07 07:38:36 +03:00
2014-12-11 02:52:58 +03:00
MODULE_ALIAS ( " platform:omap_rtc " ) ;
2006-12-07 07:38:36 +03:00
MODULE_AUTHOR ( " George G. Davis (and others) " ) ;
MODULE_LICENSE ( " GPL " ) ;