2006-12-07 07:38:36 +03:00
/*
* TI OMAP1 Real Time Clock interface for Linux
*
* 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 )
*
* 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
/* The OMAP1 RTC is a year/month/day/hours/minutes/seconds BCD clock
* 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
*/
2012-12-18 04:02:11 +04:00
# define DRIVER_NAME "omap_rtc"
2006-12-07 07:38:36 +03:00
# define OMAP_RTC_BASE 0xfffb4800
/* 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
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)
# 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-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
2012-12-18 04:02:11 +04:00
/* OMAP_RTC_KICKER values */
# define KICK0_VALUE 0x83e70b13
# define KICK1_VALUE 0x95a4f1e0
2014-06-07 01:36:05 +04:00
# define OMAP_RTC_HAS_KICKER BIT(0)
2012-12-18 04:02:11 +04:00
2013-09-12 01:24:18 +04:00
/*
* Few RTC IP revisions has special WAKE - EN Register to enable Wakeup
* generation for event Alarm .
*/
2014-06-07 01:36:05 +04:00
# define OMAP_RTC_HAS_IRQWAKEEN BIT(1)
2013-09-12 01:24:18 +04:00
2014-06-07 01:36:06 +04:00
/*
* Some RTC IP revisions ( like those in AM335x and DRA7x ) need
* the 32 KHz clock to be explicitly enabled .
*/
# define OMAP_RTC_HAS_32KCLK_EN BIT(2)
2009-12-16 03:46:11 +03:00
static void __iomem * rtc_base ;
2006-12-07 07:38:36 +03:00
2012-12-18 04:02:11 +04:00
# define rtc_read(addr) readb(rtc_base + (addr))
# define rtc_write(val, addr) writeb(val, rtc_base + (addr))
# define rtc_writel(val, addr) writel(val, rtc_base + (addr))
2006-12-07 07:38:36 +03:00
/* we rely on the rtc framework to handle locking (rtc->ops_lock),
* so the only other requirement is that register accesses which
* require BUSY to be clear are made with IRQs locally disabled
*/
static void rtc_wait_not_busy ( void )
{
int count = 0 ;
u8 status ;
/* BUSY may stay active for 1/32768 second (~30 usec) */
for ( count = 0 ; count < 50 ; count + + ) {
status = rtc_read ( OMAP_RTC_STATUS_REG ) ;
if ( ( status & ( u8 ) OMAP_RTC_STATUS_BUSY ) = = 0 )
break ;
udelay ( 1 ) ;
}
/* now we have ~15 usec to read/write various registers */
}
2007-05-08 11:33:30 +04:00
static irqreturn_t rtc_irq ( int irq , void * rtc )
2006-12-07 07:38:36 +03:00
{
unsigned long events = 0 ;
u8 irq_data ;
irq_data = rtc_read ( OMAP_RTC_STATUS_REG ) ;
/* alarm irq? */
if ( irq_data & OMAP_RTC_STATUS_ALARM ) {
rtc_write ( OMAP_RTC_STATUS_ALARM , OMAP_RTC_STATUS_REG ) ;
events | = RTC_IRQF | RTC_AF ;
}
/* 1/sec periodic/update irq? */
if ( irq_data & OMAP_RTC_STATUS_1S_EVENT )
events | = RTC_IRQF | RTC_UF ;
2007-05-08 11:33:30 +04:00
rtc_update_irq ( 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-06-07 01:36:12 +04:00
u8 reg , irqwake_reg = 0 ;
struct platform_device * pdev = to_platform_device ( dev ) ;
const struct platform_device_id * id_entry =
platform_get_device_id ( pdev ) ;
2011-02-03 04:02:41 +03:00
local_irq_disable ( ) ;
rtc_wait_not_busy ( ) ;
reg = rtc_read ( OMAP_RTC_INTERRUPTS_REG ) ;
2014-06-07 01:36:12 +04:00
if ( id_entry - > driver_data & OMAP_RTC_HAS_IRQWAKEEN )
irqwake_reg = rtc_read ( OMAP_RTC_IRQWAKEEN ) ;
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 ;
}
2011-02-03 04:02:41 +03:00
rtc_wait_not_busy ( ) ;
rtc_write ( reg , OMAP_RTC_INTERRUPTS_REG ) ;
2014-06-07 01:36:12 +04:00
if ( id_entry - > driver_data & OMAP_RTC_HAS_IRQWAKEEN )
rtc_write ( irqwake_reg , OMAP_RTC_IRQWAKEEN ) ;
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
}
static int omap_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
/* we don't report wday/yday/isdst ... */
local_irq_disable ( ) ;
rtc_wait_not_busy ( ) ;
tm - > tm_sec = rtc_read ( OMAP_RTC_SECONDS_REG ) ;
tm - > tm_min = rtc_read ( OMAP_RTC_MINUTES_REG ) ;
tm - > tm_hour = rtc_read ( OMAP_RTC_HOURS_REG ) ;
tm - > tm_mday = rtc_read ( OMAP_RTC_DAYS_REG ) ;
tm - > tm_mon = rtc_read ( OMAP_RTC_MONTHS_REG ) ;
tm - > tm_year = rtc_read ( OMAP_RTC_YEARS_REG ) ;
local_irq_enable ( ) ;
bcd2tm ( tm ) ;
return 0 ;
}
static int omap_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
if ( tm2bcd ( tm ) < 0 )
return - EINVAL ;
local_irq_disable ( ) ;
rtc_wait_not_busy ( ) ;
rtc_write ( tm - > tm_year , OMAP_RTC_YEARS_REG ) ;
rtc_write ( tm - > tm_mon , OMAP_RTC_MONTHS_REG ) ;
rtc_write ( tm - > tm_mday , OMAP_RTC_DAYS_REG ) ;
rtc_write ( tm - > tm_hour , OMAP_RTC_HOURS_REG ) ;
rtc_write ( tm - > tm_min , OMAP_RTC_MINUTES_REG ) ;
rtc_write ( tm - > tm_sec , OMAP_RTC_SECONDS_REG ) ;
local_irq_enable ( ) ;
return 0 ;
}
static int omap_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
local_irq_disable ( ) ;
rtc_wait_not_busy ( ) ;
alm - > time . tm_sec = rtc_read ( OMAP_RTC_ALARM_SECONDS_REG ) ;
alm - > time . tm_min = rtc_read ( OMAP_RTC_ALARM_MINUTES_REG ) ;
alm - > time . tm_hour = rtc_read ( OMAP_RTC_ALARM_HOURS_REG ) ;
alm - > time . tm_mday = rtc_read ( OMAP_RTC_ALARM_DAYS_REG ) ;
alm - > time . tm_mon = rtc_read ( OMAP_RTC_ALARM_MONTHS_REG ) ;
alm - > time . tm_year = rtc_read ( OMAP_RTC_ALARM_YEARS_REG ) ;
local_irq_enable ( ) ;
bcd2tm ( & alm - > time ) ;
2006-12-13 11:35:08 +03:00
alm - > enabled = ! ! ( rtc_read ( OMAP_RTC_INTERRUPTS_REG )
2006-12-07 07:38:36 +03:00
& OMAP_RTC_INTERRUPTS_IT_ALARM ) ;
return 0 ;
}
static int omap_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
2014-06-07 01:36:12 +04:00
u8 reg , irqwake_reg = 0 ;
struct platform_device * pdev = to_platform_device ( dev ) ;
const struct platform_device_id * id_entry =
platform_get_device_id ( pdev ) ;
2006-12-07 07:38:36 +03:00
if ( tm2bcd ( & alm - > time ) < 0 )
return - EINVAL ;
local_irq_disable ( ) ;
rtc_wait_not_busy ( ) ;
rtc_write ( alm - > time . tm_year , OMAP_RTC_ALARM_YEARS_REG ) ;
rtc_write ( alm - > time . tm_mon , OMAP_RTC_ALARM_MONTHS_REG ) ;
rtc_write ( alm - > time . tm_mday , OMAP_RTC_ALARM_DAYS_REG ) ;
rtc_write ( alm - > time . tm_hour , OMAP_RTC_ALARM_HOURS_REG ) ;
rtc_write ( alm - > time . tm_min , OMAP_RTC_ALARM_MINUTES_REG ) ;
rtc_write ( alm - > time . tm_sec , OMAP_RTC_ALARM_SECONDS_REG ) ;
reg = rtc_read ( OMAP_RTC_INTERRUPTS_REG ) ;
2014-06-07 01:36:12 +04:00
if ( id_entry - > driver_data & OMAP_RTC_HAS_IRQWAKEEN )
irqwake_reg = rtc_read ( OMAP_RTC_IRQWAKEEN ) ;
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 ;
}
2006-12-07 07:38:36 +03:00
rtc_write ( reg , OMAP_RTC_INTERRUPTS_REG ) ;
2014-06-07 01:36:12 +04:00
if ( id_entry - > driver_data & OMAP_RTC_HAS_IRQWAKEEN )
rtc_write ( irqwake_reg , OMAP_RTC_IRQWAKEEN ) ;
2006-12-07 07:38:36 +03:00
local_irq_enable ( ) ;
return 0 ;
}
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
} ;
static int omap_rtc_alarm ;
static int omap_rtc_timer ;
2013-09-12 01:24:18 +04:00
# define OMAP_RTC_DATA_AM3352_IDX 1
# define OMAP_RTC_DATA_DA830_IDX 2
2012-12-18 04:02:15 +04:00
2012-12-18 04:02:11 +04:00
static struct platform_device_id omap_rtc_devtype [ ] = {
{
. name = DRIVER_NAME ,
2013-09-12 01:24:18 +04:00
} ,
[ OMAP_RTC_DATA_AM3352_IDX ] = {
. name = " am3352-rtc " ,
2014-06-07 01:36:06 +04:00
. driver_data = OMAP_RTC_HAS_KICKER | OMAP_RTC_HAS_IRQWAKEEN |
OMAP_RTC_HAS_32KCLK_EN ,
2013-09-12 01:24:18 +04:00
} ,
[ OMAP_RTC_DATA_DA830_IDX ] = {
2012-12-18 04:02:11 +04:00
. name = " da830-rtc " ,
. driver_data = OMAP_RTC_HAS_KICKER ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , omap_rtc_devtype ) ;
2012-12-18 04:02:15 +04:00
static const struct of_device_id omap_rtc_of_match [ ] = {
{ . compatible = " ti,da830-rtc " ,
. data = & omap_rtc_devtype [ OMAP_RTC_DATA_DA830_IDX ] ,
} ,
2013-09-12 01:24:18 +04:00
{ . compatible = " ti,am3352-rtc " ,
. data = & omap_rtc_devtype [ OMAP_RTC_DATA_AM3352_IDX ] ,
} ,
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
{
2013-04-30 03:20:04 +04:00
struct resource * res ;
2006-12-07 07:38:36 +03:00
struct rtc_device * rtc ;
u8 reg , 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 ;
of_id = of_match_device ( omap_rtc_of_match , & pdev - > dev ) ;
if ( of_id )
pdev - > id_entry = of_id - > data ;
2006-12-07 07:38:36 +03:00
2014-06-07 01:36:04 +04:00
id_entry = platform_get_device_id ( pdev ) ;
if ( ! id_entry ) {
dev_err ( & pdev - > dev , " no matching device entry \n " ) ;
return - ENODEV ;
}
2006-12-07 07:38:36 +03:00
omap_rtc_timer = platform_get_irq ( pdev , 0 ) ;
if ( omap_rtc_timer < = 0 ) {
pr_debug ( " %s: no update irq? \n " , pdev - > name ) ;
return - ENOENT ;
}
omap_rtc_alarm = platform_get_irq ( pdev , 1 ) ;
if ( omap_rtc_alarm < = 0 ) {
pr_debug ( " %s: no alarm irq? \n " , pdev - > name ) ;
return - ENOENT ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-04-30 03:20:04 +04:00
rtc_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( rtc_base ) )
return PTR_ERR ( rtc_base ) ;
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-06-07 01:36:04 +04:00
if ( id_entry - > driver_data & OMAP_RTC_HAS_KICKER ) {
2012-12-18 04:02:11 +04:00
rtc_writel ( KICK0_VALUE , OMAP_RTC_KICK0_REG ) ;
rtc_writel ( KICK1_VALUE , OMAP_RTC_KICK1_REG ) ;
}
2013-04-30 03:20:04 +04:00
rtc = devm_rtc_device_register ( & pdev - > dev , pdev - > name ,
2006-12-07 07:38:36 +03:00
& omap_rtc_ops , THIS_MODULE ) ;
if ( IS_ERR ( rtc ) ) {
pr_debug ( " %s: can't register RTC device, err %ld \n " ,
pdev - > name , PTR_ERR ( rtc ) ) ;
2009-12-16 03:46:11 +03:00
goto fail0 ;
2006-12-07 07:38:36 +03:00
}
platform_set_drvdata ( pdev , rtc ) ;
/* clear pending irqs, and set 1/second periodic,
* which we ' ll use instead of update irqs
*/
rtc_write ( 0 , OMAP_RTC_INTERRUPTS_REG ) ;
2014-06-07 01:36:06 +04:00
/* enable RTC functional clock */
if ( id_entry - > driver_data & OMAP_RTC_HAS_32KCLK_EN )
rtc_writel ( OMAP_RTC_OSC_32KCLK_EN , OMAP_RTC_OSC_REG ) ;
2006-12-07 07:38:36 +03:00
/* clear old status */
reg = rtc_read ( OMAP_RTC_STATUS_REG ) ;
if ( reg & ( u8 ) OMAP_RTC_STATUS_POWER_UP ) {
pr_info ( " %s: RTC power up reset detected \n " ,
pdev - > name ) ;
rtc_write ( OMAP_RTC_STATUS_POWER_UP , OMAP_RTC_STATUS_REG ) ;
}
if ( reg & ( u8 ) OMAP_RTC_STATUS_ALARM )
rtc_write ( OMAP_RTC_STATUS_ALARM , OMAP_RTC_STATUS_REG ) ;
/* handle periodic and alarm irqs */
2013-04-30 03:20:04 +04:00
if ( devm_request_irq ( & pdev - > dev , omap_rtc_timer , rtc_irq , 0 ,
2009-03-25 02:38:22 +03:00
dev_name ( & rtc - > dev ) , rtc ) ) {
2006-12-07 07:38:36 +03:00
pr_debug ( " %s: RTC timer interrupt IRQ%d already claimed \n " ,
pdev - > name , omap_rtc_timer ) ;
2013-04-30 03:20:04 +04:00
goto fail0 ;
2006-12-07 07:38:36 +03:00
}
2009-12-16 03:46:11 +03:00
if ( ( omap_rtc_timer ! = omap_rtc_alarm ) & &
2013-04-30 03:20:04 +04:00
( devm_request_irq ( & pdev - > dev , omap_rtc_alarm , rtc_irq , 0 ,
2009-12-16 03:46:11 +03:00
dev_name ( & rtc - > dev ) , rtc ) ) ) {
2006-12-07 07:38:36 +03:00
pr_debug ( " %s: RTC alarm interrupt IRQ%d already claimed \n " ,
pdev - > name , omap_rtc_alarm ) ;
2013-04-30 03:20:04 +04:00
goto fail0 ;
2006-12-07 07:38:36 +03:00
}
/* On boards with split power, RTC_ON_NOFF won't reset the RTC */
reg = rtc_read ( OMAP_RTC_CTRL_REG ) ;
if ( reg & ( u8 ) OMAP_RTC_CTRL_STOP )
pr_info ( " %s: already running \n " , pdev - > name ) ;
/* force to 24 hour mode */
2011-08-04 03:21:02 +04: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 ;
/* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
*
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 . . . )
*/
2013-07-04 02:08:02 +04:00
device_init_wakeup ( & pdev - > dev , true ) ;
2006-12-07 07:38:36 +03:00
if ( new_ctrl & ( u8 ) OMAP_RTC_CTRL_SPLIT )
pr_info ( " %s: split power mode \n " , pdev - > name ) ;
if ( reg ! = new_ctrl )
rtc_write ( new_ctrl , OMAP_RTC_CTRL_REG ) ;
return 0 ;
2009-12-16 03:46:11 +03:00
fail0 :
2014-06-07 01:36:04 +04:00
if ( id_entry - > driver_data & OMAP_RTC_HAS_KICKER )
2012-12-18 04:02:11 +04:00
rtc_writel ( 0 , OMAP_RTC_KICK0_REG ) ;
2012-12-18 04:02:18 +04:00
pm_runtime_put_sync ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2006-12-07 07:38:36 +03:00
return - EIO ;
}
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
{
2012-12-18 04:02:11 +04:00
const struct platform_device_id * id_entry =
platform_get_device_id ( pdev ) ;
2006-12-07 07:38:36 +03:00
device_init_wakeup ( & pdev - > dev , 0 ) ;
/* leave rtc running, but disable irqs */
rtc_write ( 0 , OMAP_RTC_INTERRUPTS_REG ) ;
2014-06-07 01:36:04 +04:00
if ( id_entry - > driver_data & OMAP_RTC_HAS_KICKER )
2012-12-18 04:02:11 +04:00
rtc_writel ( 0 , OMAP_RTC_KICK0_REG ) ;
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
2006-12-07 07:38:36 +03:00
static u8 irqstat ;
2013-04-30 03:21:01 +04:00
static int omap_rtc_suspend ( struct device * dev )
2006-12-07 07:38:36 +03:00
{
irqstat = rtc_read ( OMAP_RTC_INTERRUPTS_REG ) ;
/* 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 ) )
2006-12-07 07:38:36 +03:00
enable_irq_wake ( omap_rtc_alarm ) ;
2014-06-07 01:36:12 +04:00
else
2006-12-07 07:38:36 +03:00
rtc_write ( 0 , OMAP_RTC_INTERRUPTS_REG ) ;
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
{
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 ) )
2006-12-07 07:38:36 +03:00
disable_irq_wake ( omap_rtc_alarm ) ;
2014-06-07 01:36:12 +04:00
else
2006-12-07 07:38:36 +03:00
rtc_write ( irqstat , OMAP_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 )
{
rtc_write ( 0 , OMAP_RTC_INTERRUPTS_REG ) ;
}
2008-04-11 08:29:25 +04:00
MODULE_ALIAS ( " platform:omap_rtc " ) ;
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 = {
2012-12-18 04:02:11 +04:00
. name = DRIVER_NAME ,
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
} ,
2012-12-18 04:02:11 +04:00
. id_table = omap_rtc_devtype ,
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
MODULE_AUTHOR ( " George G. Davis (and others) " ) ;
MODULE_LICENSE ( " GPL " ) ;