2021-07-07 10:58:04 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-05-27 03:25:09 +04:00
/*
* drivers / rtc / rtc - spear . c
*
* Copyright ( C ) 2010 ST Microelectronics
* Rajeev Kumar < rajeev - dlh . kumar @ st . com >
*/
# include <linux/bcd.h>
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/module.h>
2012-05-30 02:07:35 +04:00
# include <linux/of.h>
2011-05-27 03:25:09 +04:00
# include <linux/platform_device.h>
# include <linux/rtc.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
/* RTC registers */
# define TIME_REG 0x00
# define DATE_REG 0x04
# define ALARM_TIME_REG 0x08
# define ALARM_DATE_REG 0x0C
# define CTRL_REG 0x10
# define STATUS_REG 0x14
/* TIME_REG & ALARM_TIME_REG */
# define SECONDS_UNITS (0xf<<0) /* seconds units position */
# define SECONDS_TENS (0x7<<4) /* seconds tens position */
# define MINUTES_UNITS (0xf<<8) /* minutes units position */
# define MINUTES_TENS (0x7<<12) /* minutes tens position */
# define HOURS_UNITS (0xf<<16) /* hours units position */
# define HOURS_TENS (0x3<<20) /* hours tens position */
/* DATE_REG & ALARM_DATE_REG */
# define DAYS_UNITS (0xf<<0) /* days units position */
# define DAYS_TENS (0x3<<4) /* days tens position */
# define MONTHS_UNITS (0xf<<8) /* months units position */
# define MONTHS_TENS (0x1<<12) /* months tens position */
# define YEARS_UNITS (0xf<<16) /* years units position */
# define YEARS_TENS (0xf<<20) /* years tens position */
# define YEARS_HUNDREDS (0xf<<24) /* years hundereds position */
# define YEARS_MILLENIUMS (0xf<<28) /* years millenium position */
/* MASK SHIFT TIME_REG & ALARM_TIME_REG*/
# define SECOND_SHIFT 0x00 /* seconds units */
# define MINUTE_SHIFT 0x08 /* minutes units position */
# define HOUR_SHIFT 0x10 /* hours units position */
# define MDAY_SHIFT 0x00 /* Month day shift */
# define MONTH_SHIFT 0x08 /* Month shift */
# define YEAR_SHIFT 0x10 /* Year shift */
# define SECOND_MASK 0x7F
# define MIN_MASK 0x7F
# define HOUR_MASK 0x3F
# define DAY_MASK 0x3F
# define MONTH_MASK 0x7F
# define YEAR_MASK 0xFFFF
/* date reg equal to time reg, for debug only */
# define TIME_BYP (1<<9)
# define INT_ENABLE (1<<31) /* interrupt enable */
/* STATUS_REG */
# define CLK_UNCONNECTED (1<<0)
# define PEND_WR_TIME (1<<2)
# define PEND_WR_DATE (1<<3)
# define LOST_WR_TIME (1<<4)
# define LOST_WR_DATE (1<<5)
# define RTC_INT_MASK (1<<31)
# define STATUS_BUSY (PEND_WR_TIME | PEND_WR_DATE)
# define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE)
struct spear_rtc_config {
2012-03-24 02:02:30 +04:00
struct rtc_device * rtc ;
2011-05-27 03:25:09 +04:00
struct clk * clk ;
spinlock_t lock ;
void __iomem * ioaddr ;
2012-03-24 02:02:29 +04:00
unsigned int irq_wake ;
2011-05-27 03:25:09 +04:00
} ;
static inline void spear_rtc_clear_interrupt ( struct spear_rtc_config * config )
{
unsigned int val ;
unsigned long flags ;
spin_lock_irqsave ( & config - > lock , flags ) ;
val = readl ( config - > ioaddr + STATUS_REG ) ;
val | = RTC_INT_MASK ;
writel ( val , config - > ioaddr + STATUS_REG ) ;
spin_unlock_irqrestore ( & config - > lock , flags ) ;
}
static inline void spear_rtc_enable_interrupt ( struct spear_rtc_config * config )
{
unsigned int val ;
val = readl ( config - > ioaddr + CTRL_REG ) ;
if ( ! ( val & INT_ENABLE ) ) {
spear_rtc_clear_interrupt ( config ) ;
val | = INT_ENABLE ;
writel ( val , config - > ioaddr + CTRL_REG ) ;
}
}
static inline void spear_rtc_disable_interrupt ( struct spear_rtc_config * config )
{
unsigned int val ;
val = readl ( config - > ioaddr + CTRL_REG ) ;
if ( val & INT_ENABLE ) {
val & = ~ INT_ENABLE ;
writel ( val , config - > ioaddr + CTRL_REG ) ;
}
}
static inline int is_write_complete ( struct spear_rtc_config * config )
{
int ret = 0 ;
unsigned long flags ;
spin_lock_irqsave ( & config - > lock , flags ) ;
if ( ( readl ( config - > ioaddr + STATUS_REG ) ) & STATUS_FAIL )
ret = - EIO ;
spin_unlock_irqrestore ( & config - > lock , flags ) ;
return ret ;
}
static void rtc_wait_not_busy ( struct spear_rtc_config * config )
{
int status , count = 0 ;
unsigned long flags ;
/* Assuming BUSY may stay active for 80 msec) */
for ( count = 0 ; count < 80 ; count + + ) {
spin_lock_irqsave ( & config - > lock , flags ) ;
status = readl ( config - > ioaddr + STATUS_REG ) ;
spin_unlock_irqrestore ( & config - > lock , flags ) ;
if ( ( status & STATUS_BUSY ) = = 0 )
break ;
/* check status busy, after each msec */
msleep ( 1 ) ;
}
}
static irqreturn_t spear_rtc_irq ( int irq , void * dev_id )
{
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = dev_id ;
2021-03-15 05:39:30 +03:00
unsigned long events = 0 ;
2011-05-27 03:25:09 +04:00
unsigned int irq_data ;
2021-03-15 05:39:30 +03:00
spin_lock ( & config - > lock ) ;
2011-05-27 03:25:09 +04:00
irq_data = readl ( config - > ioaddr + STATUS_REG ) ;
2021-03-15 05:39:30 +03:00
spin_unlock ( & config - > lock ) ;
2011-05-27 03:25:09 +04:00
if ( ( irq_data & RTC_INT_MASK ) ) {
spear_rtc_clear_interrupt ( config ) ;
events = RTC_IRQF | RTC_AF ;
2012-03-24 02:02:30 +04:00
rtc_update_irq ( config - > rtc , 1 , events ) ;
2011-05-27 03:25:09 +04:00
return IRQ_HANDLED ;
} else
return IRQ_NONE ;
}
2018-02-21 13:23:19 +03:00
static void tm2bcd ( struct rtc_time * tm )
2011-05-27 03:25:09 +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 ) ;
tm - > tm_mon = bin2bcd ( tm - > tm_mon + 1 ) ;
tm - > tm_year = bin2bcd ( tm - > tm_year ) ;
}
static void bcd2tm ( struct rtc_time * tm )
{
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 ;
/* epoch == 1900 */
tm - > tm_year = bcd2bin ( tm - > tm_year ) ;
}
/*
* spear_rtc_read_time - set the time
* @ dev : rtc device in use
* @ tm : holds date and time
*
* This function read time and date . On success it will return 0
* otherwise - ve error is returned .
*/
static int spear_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = dev_get_drvdata ( dev ) ;
2011-05-27 03:25:09 +04:00
unsigned int time , date ;
/* we don't report wday/yday/isdst ... */
rtc_wait_not_busy ( config ) ;
2022-03-09 19:22:52 +03:00
do {
time = readl ( config - > ioaddr + TIME_REG ) ;
date = readl ( config - > ioaddr + DATE_REG ) ;
} while ( time = = readl ( config - > ioaddr + TIME_REG ) ) ;
2011-05-27 03:25:09 +04:00
tm - > tm_sec = ( time > > SECOND_SHIFT ) & SECOND_MASK ;
tm - > tm_min = ( time > > MINUTE_SHIFT ) & MIN_MASK ;
tm - > tm_hour = ( time > > HOUR_SHIFT ) & HOUR_MASK ;
tm - > tm_mday = ( date > > MDAY_SHIFT ) & DAY_MASK ;
tm - > tm_mon = ( date > > MONTH_SHIFT ) & MONTH_MASK ;
tm - > tm_year = ( date > > YEAR_SHIFT ) & YEAR_MASK ;
bcd2tm ( tm ) ;
return 0 ;
}
/*
* spear_rtc_set_time - set the time
* @ dev : rtc device in use
* @ tm : holds date and time
*
* This function set time and date . On success it will return 0
* otherwise - ve error is returned .
*/
static int spear_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = dev_get_drvdata ( dev ) ;
2012-10-05 04:14:01 +04:00
unsigned int time , date ;
2011-05-27 03:25:09 +04:00
2018-02-21 13:23:19 +03:00
tm2bcd ( tm ) ;
2011-05-27 03:25:09 +04:00
rtc_wait_not_busy ( config ) ;
time = ( tm - > tm_sec < < SECOND_SHIFT ) | ( tm - > tm_min < < MINUTE_SHIFT ) |
( tm - > tm_hour < < HOUR_SHIFT ) ;
date = ( tm - > tm_mday < < MDAY_SHIFT ) | ( tm - > tm_mon < < MONTH_SHIFT ) |
( tm - > tm_year < < YEAR_SHIFT ) ;
writel ( time , config - > ioaddr + TIME_REG ) ;
writel ( date , config - > ioaddr + DATE_REG ) ;
2012-10-05 04:14:01 +04:00
return is_write_complete ( config ) ;
2011-05-27 03:25:09 +04:00
}
/*
* spear_rtc_read_alarm - read the alarm time
* @ dev : rtc device in use
* @ alm : holds alarm date and time
*
* This function read alarm time and date . On success it will return 0
* otherwise - ve error is returned .
*/
static int spear_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = dev_get_drvdata ( dev ) ;
2011-05-27 03:25:09 +04:00
unsigned int time , date ;
rtc_wait_not_busy ( config ) ;
time = readl ( config - > ioaddr + ALARM_TIME_REG ) ;
date = readl ( config - > ioaddr + ALARM_DATE_REG ) ;
alm - > time . tm_sec = ( time > > SECOND_SHIFT ) & SECOND_MASK ;
alm - > time . tm_min = ( time > > MINUTE_SHIFT ) & MIN_MASK ;
alm - > time . tm_hour = ( time > > HOUR_SHIFT ) & HOUR_MASK ;
alm - > time . tm_mday = ( date > > MDAY_SHIFT ) & DAY_MASK ;
alm - > time . tm_mon = ( date > > MONTH_SHIFT ) & MONTH_MASK ;
alm - > time . tm_year = ( date > > YEAR_SHIFT ) & YEAR_MASK ;
bcd2tm ( & alm - > time ) ;
alm - > enabled = readl ( config - > ioaddr + CTRL_REG ) & INT_ENABLE ;
return 0 ;
}
/*
* spear_rtc_set_alarm - set the alarm time
* @ dev : rtc device in use
* @ alm : holds alarm date and time
*
* This function set alarm time and date . On success it will return 0
* otherwise - ve error is returned .
*/
static int spear_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = dev_get_drvdata ( dev ) ;
2012-10-05 04:14:01 +04:00
unsigned int time , date ;
int err ;
2011-05-27 03:25:09 +04:00
2018-02-21 13:23:19 +03:00
tm2bcd ( & alm - > time ) ;
2011-05-27 03:25:09 +04:00
rtc_wait_not_busy ( config ) ;
time = ( alm - > time . tm_sec < < SECOND_SHIFT ) | ( alm - > time . tm_min < <
MINUTE_SHIFT ) | ( alm - > time . tm_hour < < HOUR_SHIFT ) ;
date = ( alm - > time . tm_mday < < MDAY_SHIFT ) | ( alm - > time . tm_mon < <
MONTH_SHIFT ) | ( alm - > time . tm_year < < YEAR_SHIFT ) ;
writel ( time , config - > ioaddr + ALARM_TIME_REG ) ;
writel ( date , config - > ioaddr + ALARM_DATE_REG ) ;
err = is_write_complete ( config ) ;
if ( err < 0 )
return err ;
if ( alm - > enabled )
spear_rtc_enable_interrupt ( config ) ;
else
spear_rtc_disable_interrupt ( config ) ;
return 0 ;
}
2012-03-24 02:02:29 +04:00
static int spear_alarm_irq_enable ( struct device * dev , unsigned int enabled )
{
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = dev_get_drvdata ( dev ) ;
2012-03-24 02:02:29 +04:00
int ret = 0 ;
spear_rtc_clear_interrupt ( config ) ;
switch ( enabled ) {
case 0 :
/* alarm off */
spear_rtc_disable_interrupt ( config ) ;
break ;
case 1 :
/* alarm on */
spear_rtc_enable_interrupt ( config ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
rtc: constify rtc_class_ops structures
Check for rtc_class_ops structures that are only passed to
devm_rtc_device_register, rtc_device_register,
platform_device_register_data, all of which declare the corresponding
parameter as const. Declare rtc_class_ops structures that have these
properties as const.
The semantic patch that makes this change is as follows:
(http://coccinelle.lip6.fr/)
// <smpl>
@r disable optional_qualifier@
identifier i;
position p;
@@
static struct rtc_class_ops i@p = { ... };
@ok@
identifier r.i;
expression e1,e2,e3,e4;
position p;
@@
(
devm_rtc_device_register(e1,e2,&i@p,e3)
|
rtc_device_register(e1,e2,&i@p,e3)
|
platform_device_register_data(e1,e2,e3,&i@p,e4)
)
@bad@
position p != {r.p,ok.p};
identifier r.i;
@@
i@p
@depends on !bad disable optional_qualifier@
identifier r.i;
@@
static
+const
struct rtc_class_ops i = { ... };
// </smpl>
Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
Acked-by: Baruch Siach <baruch@tkos.co.il>
Acked-by: Hans Ulli Kroll <ulli.kroll@googlemail.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
2016-08-31 11:05:25 +03:00
static const struct rtc_class_ops spear_rtc_ops = {
2011-05-27 03:25:09 +04:00
. read_time = spear_rtc_read_time ,
. set_time = spear_rtc_set_time ,
. read_alarm = spear_rtc_read_alarm ,
. set_alarm = spear_rtc_set_alarm ,
2012-03-24 02:02:29 +04:00
. alarm_irq_enable = spear_alarm_irq_enable ,
2011-05-27 03:25:09 +04:00
} ;
2012-12-22 01:09:38 +04:00
static int spear_rtc_probe ( struct platform_device * pdev )
2011-05-27 03:25:09 +04:00
{
struct spear_rtc_config * config ;
2012-10-05 04:14:01 +04:00
int status = 0 ;
2011-05-27 03:25:09 +04:00
int irq ;
2012-12-18 04:02:29 +04:00
config = devm_kzalloc ( & pdev - > dev , sizeof ( * config ) , GFP_KERNEL ) ;
2014-04-04 01:49:46 +04:00
if ( ! config )
2012-12-18 04:02:29 +04:00
return - ENOMEM ;
2011-05-27 03:25:09 +04:00
2022-03-09 19:22:49 +03:00
config - > rtc = devm_rtc_allocate_device ( & pdev - > dev ) ;
if ( IS_ERR ( config - > rtc ) )
return PTR_ERR ( config - > rtc ) ;
2012-12-18 04:02:29 +04:00
/* alarm irqs */
irq = platform_get_irq ( pdev , 0 ) ;
2019-07-30 21:15:39 +03:00
if ( irq < 0 )
2012-12-18 04:02:29 +04:00
return irq ;
2011-05-27 03:25:09 +04:00
2012-12-18 04:02:29 +04:00
status = devm_request_irq ( & pdev - > dev , irq , spear_rtc_irq , 0 , pdev - > name ,
config ) ;
if ( status ) {
dev_err ( & pdev - > dev , " Alarm interrupt IRQ%d already claimed \n " ,
irq ) ;
return status ;
}
2011-05-27 03:25:09 +04:00
2019-10-06 13:29:20 +03:00
config - > ioaddr = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-01-21 14:09:17 +04:00
if ( IS_ERR ( config - > ioaddr ) )
return PTR_ERR ( config - > ioaddr ) ;
2011-05-27 03:25:09 +04:00
2012-12-18 04:02:29 +04:00
config - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( config - > clk ) )
return PTR_ERR ( config - > clk ) ;
2012-12-18 04:02:32 +04:00
status = clk_prepare_enable ( config - > clk ) ;
2012-12-18 04:02:29 +04:00
if ( status < 0 )
return status ;
2011-05-27 03:25:09 +04:00
spin_lock_init ( & config - > lock ) ;
2012-03-24 02:02:30 +04:00
platform_set_drvdata ( pdev , config ) ;
2011-05-27 03:25:09 +04:00
2022-03-09 19:22:49 +03:00
config - > rtc - > ops = & spear_rtc_ops ;
2022-03-09 19:22:50 +03:00
config - > rtc - > range_min = RTC_TIMESTAMP_BEGIN_0000 ;
2022-07-28 13:01:01 +03:00
config - > rtc - > range_max = RTC_TIMESTAMP_END_9999 ;
2012-12-18 04:02:34 +04:00
2022-03-09 19:22:49 +03:00
status = devm_rtc_register_device ( config - > rtc ) ;
if ( status )
goto err_disable_clock ;
2011-05-27 03:25:09 +04:00
if ( ! device_can_wakeup ( & pdev - > dev ) )
device_init_wakeup ( & pdev - > dev , 1 ) ;
return 0 ;
err_disable_clock :
2012-12-18 04:02:32 +04:00
clk_disable_unprepare ( config - > clk ) ;
2011-05-27 03:25:09 +04:00
return status ;
}
2012-12-22 01:09:38 +04:00
static int spear_rtc_remove ( struct platform_device * pdev )
2011-05-27 03:25:09 +04:00
{
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = platform_get_drvdata ( pdev ) ;
2011-05-27 03:25:09 +04:00
spear_rtc_disable_interrupt ( config ) ;
2012-12-18 04:02:32 +04:00
clk_disable_unprepare ( config - > clk ) ;
2012-12-18 04:02:29 +04:00
device_init_wakeup ( & pdev - > dev , 0 ) ;
2011-05-27 03:25:09 +04:00
return 0 ;
}
2013-04-30 03:21:02 +04:00
# ifdef CONFIG_PM_SLEEP
static int spear_rtc_suspend ( struct device * dev )
2011-05-27 03:25:09 +04:00
{
2013-04-30 03:21:02 +04:00
struct platform_device * pdev = to_platform_device ( dev ) ;
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = platform_get_drvdata ( pdev ) ;
2011-05-27 03:25:09 +04:00
int irq ;
irq = platform_get_irq ( pdev , 0 ) ;
2012-03-24 02:02:29 +04:00
if ( device_may_wakeup ( & pdev - > dev ) ) {
if ( ! enable_irq_wake ( irq ) )
config - > irq_wake = 1 ;
} else {
2011-05-27 03:25:09 +04:00
spear_rtc_disable_interrupt ( config ) ;
clk_disable ( config - > clk ) ;
}
return 0 ;
}
2013-04-30 03:21:02 +04:00
static int spear_rtc_resume ( struct device * dev )
2011-05-27 03:25:09 +04:00
{
2013-04-30 03:21:02 +04:00
struct platform_device * pdev = to_platform_device ( dev ) ;
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = platform_get_drvdata ( pdev ) ;
2011-05-27 03:25:09 +04:00
int irq ;
irq = platform_get_irq ( pdev , 0 ) ;
2012-03-24 02:02:29 +04:00
if ( device_may_wakeup ( & pdev - > dev ) ) {
if ( config - > irq_wake ) {
disable_irq_wake ( irq ) ;
config - > irq_wake = 0 ;
}
} else {
2011-05-27 03:25:09 +04:00
clk_enable ( config - > clk ) ;
spear_rtc_enable_interrupt ( config ) ;
}
return 0 ;
}
# endif
2013-04-30 03:21:02 +04:00
static SIMPLE_DEV_PM_OPS ( spear_rtc_pm_ops , spear_rtc_suspend , spear_rtc_resume ) ;
2011-05-27 03:25:09 +04:00
static void spear_rtc_shutdown ( struct platform_device * pdev )
{
2012-03-24 02:02:30 +04:00
struct spear_rtc_config * config = platform_get_drvdata ( pdev ) ;
2011-05-27 03:25:09 +04:00
spear_rtc_disable_interrupt ( config ) ;
clk_disable ( config - > clk ) ;
}
2012-05-30 02:07:35 +04:00
# ifdef CONFIG_OF
static const struct of_device_id spear_rtc_id_table [ ] = {
{ . compatible = " st,spear600-rtc " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , spear_rtc_id_table ) ;
# endif
2011-05-27 03:25:09 +04:00
static struct platform_driver spear_rtc_driver = {
. probe = spear_rtc_probe ,
2012-12-22 01:09:38 +04:00
. remove = spear_rtc_remove ,
2011-05-27 03:25:09 +04:00
. shutdown = spear_rtc_shutdown ,
. driver = {
. name = " rtc-spear " ,
2013-04-30 03:21:02 +04:00
. pm = & spear_rtc_pm_ops ,
2012-05-30 02:07:35 +04:00
. of_match_table = of_match_ptr ( spear_rtc_id_table ) ,
2011-05-27 03:25:09 +04:00
} ,
} ;
2012-01-11 03:10:48 +04:00
module_platform_driver ( spear_rtc_driver ) ;
2011-05-27 03:25:09 +04:00
MODULE_ALIAS ( " platform:rtc-spear " ) ;
MODULE_AUTHOR ( " Rajeev Kumar <rajeev-dlh.kumar@st.com> " ) ;
MODULE_DESCRIPTION ( " ST SPEAr Realtime Clock Driver (RTC) " ) ;
MODULE_LICENSE ( " GPL " ) ;