2019-04-30 12:28:21 +03:00
// SPDX-License-Identifier: GPL-2.0+
2010-06-19 22:29:50 +04:00
/*
* Copyright ( C ) 2009 - 2010 , Lars - Peter Clausen < lars @ metafoo . de >
2010-10-28 02:33:12 +04:00
* Copyright ( C ) 2010 , Paul Cercueil < paul @ crapouillou . net >
2010-06-19 22:29:50 +04:00
* JZ4740 SoC RTC driver
*/
2016-10-31 23:39:48 +03:00
# include <linux/clk.h>
2013-07-04 02:07:06 +04:00
# include <linux/io.h>
2010-06-19 22:29:50 +04:00
# include <linux/kernel.h>
2017-01-25 02:44:16 +03:00
# include <linux/module.h>
2016-10-31 23:39:47 +03:00
# include <linux/of_device.h>
2010-06-19 22:29:50 +04:00
# include <linux/platform_device.h>
2019-04-30 12:28:19 +03:00
# include <linux/pm_wakeirq.h>
2016-10-31 23:39:48 +03:00
# include <linux/reboot.h>
2010-06-19 22:29:50 +04:00
# include <linux/rtc.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# define JZ_REG_RTC_CTRL 0x00
# define JZ_REG_RTC_SEC 0x04
# define JZ_REG_RTC_SEC_ALARM 0x08
# define JZ_REG_RTC_REGULATOR 0x0C
# define JZ_REG_RTC_HIBERNATE 0x20
2016-10-31 23:39:48 +03:00
# define JZ_REG_RTC_WAKEUP_FILTER 0x24
# define JZ_REG_RTC_RESET_COUNTER 0x28
2010-06-19 22:29:50 +04:00
# define JZ_REG_RTC_SCRATCHPAD 0x34
2016-10-31 23:39:45 +03:00
/* The following are present on the jz4780 */
# define JZ_REG_RTC_WENR 0x3C
# define JZ_RTC_WENR_WEN BIT(31)
2010-06-19 22:29:50 +04:00
# define JZ_RTC_CTRL_WRDY BIT(7)
# define JZ_RTC_CTRL_1HZ BIT(6)
# define JZ_RTC_CTRL_1HZ_IRQ BIT(5)
# define JZ_RTC_CTRL_AF BIT(4)
# define JZ_RTC_CTRL_AF_IRQ BIT(3)
# define JZ_RTC_CTRL_AE BIT(2)
# define JZ_RTC_CTRL_ENABLE BIT(0)
2016-10-31 23:39:45 +03:00
/* Magic value to enable writes on jz4780 */
# define JZ_RTC_WENR_MAGIC 0xA55A
2016-10-31 23:39:48 +03:00
# define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0
# define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0
2016-10-31 23:39:45 +03:00
enum jz4740_rtc_type {
ID_JZ4740 ,
2020-03-11 21:23:16 +03:00
ID_JZ4760 ,
2016-10-31 23:39:45 +03:00
ID_JZ4780 ,
} ;
2010-06-19 22:29:50 +04:00
struct jz4740_rtc {
void __iomem * base ;
2016-10-31 23:39:45 +03:00
enum jz4740_rtc_type type ;
2010-06-19 22:29:50 +04:00
struct rtc_device * rtc ;
spinlock_t lock ;
} ;
2016-10-31 23:39:48 +03:00
static struct device * dev_for_power_off ;
2010-06-19 22:29:50 +04:00
static inline uint32_t jz4740_rtc_reg_read ( struct jz4740_rtc * rtc , size_t reg )
{
return readl ( rtc - > base + reg ) ;
}
static int jz4740_rtc_wait_write_ready ( struct jz4740_rtc * rtc )
{
uint32_t ctrl ;
2017-09-18 22:10:13 +03:00
int timeout = 10000 ;
2010-06-19 22:29:50 +04:00
do {
ctrl = jz4740_rtc_reg_read ( rtc , JZ_REG_RTC_CTRL ) ;
} while ( ! ( ctrl & JZ_RTC_CTRL_WRDY ) & & - - timeout ) ;
return timeout ? 0 : - EIO ;
}
2016-10-31 23:39:45 +03:00
static inline int jz4780_rtc_enable_write ( struct jz4740_rtc * rtc )
{
uint32_t ctrl ;
2017-09-18 22:10:13 +03:00
int ret , timeout = 10000 ;
2016-10-31 23:39:45 +03:00
ret = jz4740_rtc_wait_write_ready ( rtc ) ;
if ( ret ! = 0 )
return ret ;
writel ( JZ_RTC_WENR_MAGIC , rtc - > base + JZ_REG_RTC_WENR ) ;
do {
ctrl = readl ( rtc - > base + JZ_REG_RTC_WENR ) ;
} while ( ! ( ctrl & JZ_RTC_WENR_WEN ) & & - - timeout ) ;
return timeout ? 0 : - EIO ;
}
2010-06-19 22:29:50 +04:00
static inline int jz4740_rtc_reg_write ( struct jz4740_rtc * rtc , size_t reg ,
uint32_t val )
{
2016-10-31 23:39:45 +03:00
int ret = 0 ;
2020-03-11 21:23:16 +03:00
if ( rtc - > type > = ID_JZ4760 )
2016-10-31 23:39:45 +03:00
ret = jz4780_rtc_enable_write ( rtc ) ;
if ( ret = = 0 )
ret = jz4740_rtc_wait_write_ready ( rtc ) ;
2010-06-19 22:29:50 +04:00
if ( ret = = 0 )
writel ( val , rtc - > base + reg ) ;
return ret ;
}
static int jz4740_rtc_ctrl_set_bits ( struct jz4740_rtc * rtc , uint32_t mask ,
bool set )
{
int ret ;
unsigned long flags ;
uint32_t ctrl ;
spin_lock_irqsave ( & rtc - > lock , flags ) ;
ctrl = jz4740_rtc_reg_read ( rtc , JZ_REG_RTC_CTRL ) ;
/* Don't clear interrupt flags by accident */
ctrl | = JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF ;
if ( set )
ctrl | = mask ;
else
ctrl & = ~ mask ;
ret = jz4740_rtc_reg_write ( rtc , JZ_REG_RTC_CTRL , ctrl ) ;
spin_unlock_irqrestore ( & rtc - > lock , flags ) ;
return ret ;
}
static int jz4740_rtc_read_time ( struct device * dev , struct rtc_time * time )
{
struct jz4740_rtc * rtc = dev_get_drvdata ( dev ) ;
uint32_t secs , secs2 ;
int timeout = 5 ;
2019-04-30 12:28:20 +03:00
if ( jz4740_rtc_reg_read ( rtc , JZ_REG_RTC_SCRATCHPAD ) ! = 0x12345678 )
return - EINVAL ;
2010-06-19 22:29:50 +04:00
/* If the seconds register is read while it is updated, it can contain a
* bogus value . This can be avoided by making sure that two consecutive
* reads have the same value .
*/
secs = jz4740_rtc_reg_read ( rtc , JZ_REG_RTC_SEC ) ;
secs2 = jz4740_rtc_reg_read ( rtc , JZ_REG_RTC_SEC ) ;
while ( secs ! = secs2 & & - - timeout ) {
secs = secs2 ;
secs2 = jz4740_rtc_reg_read ( rtc , JZ_REG_RTC_SEC ) ;
}
if ( timeout = = 0 )
return - EIO ;
2019-04-30 12:28:16 +03:00
rtc_time64_to_tm ( secs , time ) ;
2010-06-19 22:29:50 +04:00
2018-02-19 18:23:55 +03:00
return 0 ;
2010-06-19 22:29:50 +04:00
}
2019-04-30 12:28:18 +03:00
static int jz4740_rtc_set_time ( struct device * dev , struct rtc_time * time )
2010-06-19 22:29:50 +04:00
{
struct jz4740_rtc * rtc = dev_get_drvdata ( dev ) ;
2019-04-30 12:28:20 +03:00
int ret ;
ret = jz4740_rtc_reg_write ( rtc , JZ_REG_RTC_SEC , rtc_tm_to_time64 ( time ) ) ;
if ( ret )
return ret ;
2010-06-19 22:29:50 +04:00
2019-04-30 12:28:20 +03:00
return jz4740_rtc_reg_write ( rtc , JZ_REG_RTC_SCRATCHPAD , 0x12345678 ) ;
2010-06-19 22:29:50 +04:00
}
static int jz4740_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct jz4740_rtc * rtc = dev_get_drvdata ( dev ) ;
uint32_t secs ;
uint32_t ctrl ;
secs = jz4740_rtc_reg_read ( rtc , JZ_REG_RTC_SEC_ALARM ) ;
ctrl = jz4740_rtc_reg_read ( rtc , JZ_REG_RTC_CTRL ) ;
alrm - > enabled = ! ! ( ctrl & JZ_RTC_CTRL_AE ) ;
alrm - > pending = ! ! ( ctrl & JZ_RTC_CTRL_AF ) ;
2019-04-30 12:28:16 +03:00
rtc_time64_to_tm ( secs , & alrm - > time ) ;
2010-06-19 22:29:50 +04:00
2019-04-30 12:28:17 +03:00
return 0 ;
2010-06-19 22:29:50 +04:00
}
static int jz4740_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
int ret ;
struct jz4740_rtc * rtc = dev_get_drvdata ( dev ) ;
2019-04-30 12:28:16 +03:00
uint32_t secs = lower_32_bits ( rtc_tm_to_time64 ( & alrm - > time ) ) ;
2010-06-19 22:29:50 +04:00
ret = jz4740_rtc_reg_write ( rtc , JZ_REG_RTC_SEC_ALARM , secs ) ;
if ( ! ret )
2010-10-28 02:33:12 +04:00
ret = jz4740_rtc_ctrl_set_bits ( rtc ,
JZ_RTC_CTRL_AE | JZ_RTC_CTRL_AF_IRQ , alrm - > enabled ) ;
2010-06-19 22:29:50 +04:00
return ret ;
}
static int jz4740_rtc_alarm_irq_enable ( struct device * dev , unsigned int enable )
{
struct jz4740_rtc * rtc = dev_get_drvdata ( dev ) ;
return jz4740_rtc_ctrl_set_bits ( rtc , JZ_RTC_CTRL_AF_IRQ , enable ) ;
}
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 jz4740_rtc_ops = {
2010-06-19 22:29:50 +04:00
. read_time = jz4740_rtc_read_time ,
2019-04-30 12:28:18 +03:00
. set_time = jz4740_rtc_set_time ,
2010-06-19 22:29:50 +04:00
. read_alarm = jz4740_rtc_read_alarm ,
. set_alarm = jz4740_rtc_set_alarm ,
. alarm_irq_enable = jz4740_rtc_alarm_irq_enable ,
} ;
static irqreturn_t jz4740_rtc_irq ( int irq , void * data )
{
struct jz4740_rtc * rtc = data ;
uint32_t ctrl ;
unsigned long events = 0 ;
ctrl = jz4740_rtc_reg_read ( rtc , JZ_REG_RTC_CTRL ) ;
if ( ctrl & JZ_RTC_CTRL_1HZ )
events | = ( RTC_UF | RTC_IRQF ) ;
if ( ctrl & JZ_RTC_CTRL_AF )
events | = ( RTC_AF | RTC_IRQF ) ;
rtc_update_irq ( rtc - > rtc , 1 , events ) ;
jz4740_rtc_ctrl_set_bits ( rtc , JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF , false ) ;
return IRQ_HANDLED ;
}
2016-11-09 00:20:37 +03:00
static void jz4740_rtc_poweroff ( struct device * dev )
2010-06-19 22:29:50 +04:00
{
struct jz4740_rtc * rtc = dev_get_drvdata ( dev ) ;
jz4740_rtc_reg_write ( rtc , JZ_REG_RTC_HIBERNATE , 1 ) ;
}
2016-10-31 23:39:48 +03:00
static void jz4740_rtc_power_off ( void )
{
jz4740_rtc_poweroff ( dev_for_power_off ) ;
2017-01-25 02:44:16 +03:00
kernel_halt ( ) ;
2016-10-31 23:39:48 +03:00
}
2020-05-06 01:13:32 +03:00
static void jz4740_rtc_clk_disable ( void * data )
{
clk_disable_unprepare ( data ) ;
}
2016-10-31 23:39:47 +03:00
static const struct of_device_id jz4740_rtc_of_match [ ] = {
{ . compatible = " ingenic,jz4740-rtc " , . data = ( void * ) ID_JZ4740 } ,
2020-03-11 21:23:16 +03:00
{ . compatible = " ingenic,jz4760-rtc " , . data = ( void * ) ID_JZ4760 } ,
2016-10-31 23:39:47 +03:00
{ . compatible = " ingenic,jz4780-rtc " , . data = ( void * ) ID_JZ4780 } ,
{ } ,
} ;
2017-01-25 02:44:16 +03:00
MODULE_DEVICE_TABLE ( of , jz4740_rtc_of_match ) ;
2016-10-31 23:39:47 +03:00
2020-05-06 01:13:33 +03:00
static void jz4740_rtc_set_wakeup_params ( struct jz4740_rtc * rtc ,
struct device_node * np ,
unsigned long rate )
{
unsigned long wakeup_ticks , reset_ticks ;
unsigned int min_wakeup_pin_assert_time = 60 ; /* Default: 60ms */
unsigned int reset_pin_assert_time = 100 ; /* Default: 100ms */
of_property_read_u32 ( np , " ingenic,reset-pin-assert-time-ms " ,
& reset_pin_assert_time ) ;
of_property_read_u32 ( np , " ingenic,min-wakeup-pin-assert-time-ms " ,
& min_wakeup_pin_assert_time ) ;
/*
* Set minimum wakeup pin assertion time : 100 ms .
* Range is 0 to 2 sec if RTC is clocked at 32 kHz .
*/
wakeup_ticks = ( min_wakeup_pin_assert_time * rate ) / 1000 ;
if ( wakeup_ticks < JZ_RTC_WAKEUP_FILTER_MASK )
wakeup_ticks & = JZ_RTC_WAKEUP_FILTER_MASK ;
else
wakeup_ticks = JZ_RTC_WAKEUP_FILTER_MASK ;
jz4740_rtc_reg_write ( rtc , JZ_REG_RTC_WAKEUP_FILTER , wakeup_ticks ) ;
/*
* Set reset pin low - level assertion time after wakeup : 60 ms .
* Range is 0 to 125 ms if RTC is clocked at 32 kHz .
*/
reset_ticks = ( reset_pin_assert_time * rate ) / 1000 ;
if ( reset_ticks < JZ_RTC_RESET_COUNTER_MASK )
reset_ticks & = JZ_RTC_RESET_COUNTER_MASK ;
else
reset_ticks = JZ_RTC_RESET_COUNTER_MASK ;
jz4740_rtc_reg_write ( rtc , JZ_REG_RTC_RESET_COUNTER , reset_ticks ) ;
}
2012-12-22 01:09:38 +04:00
static int jz4740_rtc_probe ( struct platform_device * pdev )
2010-06-19 22:29:50 +04:00
{
2020-05-06 01:13:31 +03:00
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
2010-06-19 22:29:50 +04:00
struct jz4740_rtc * rtc ;
2020-05-06 01:13:33 +03:00
unsigned long rate ;
2020-05-06 01:13:34 +03:00
struct clk * clk ;
int ret , irq ;
2010-06-19 22:29:50 +04:00
2020-05-06 01:13:31 +03:00
rtc = devm_kzalloc ( dev , sizeof ( * rtc ) , GFP_KERNEL ) ;
2010-06-19 22:29:50 +04:00
if ( ! rtc )
return - ENOMEM ;
2020-05-06 01:13:30 +03:00
rtc - > type = ( enum jz4740_rtc_type ) device_get_match_data ( dev ) ;
2016-10-31 23:39:45 +03:00
2020-05-06 01:13:34 +03:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
2020-05-06 01:13:35 +03:00
return irq ;
2010-06-19 22:29:50 +04:00
2019-10-06 13:29:20 +03:00
rtc - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-04-04 01:49:50 +04:00
if ( IS_ERR ( rtc - > base ) )
return PTR_ERR ( rtc - > base ) ;
2010-06-19 22:29:50 +04:00
2020-05-06 01:13:34 +03:00
clk = devm_clk_get ( dev , " rtc " ) ;
if ( IS_ERR ( clk ) ) {
2020-05-06 01:13:31 +03:00
dev_err ( dev , " Failed to get RTC clock \n " ) ;
2020-05-06 01:13:34 +03:00
return PTR_ERR ( clk ) ;
2016-10-31 23:39:48 +03:00
}
2020-05-06 01:13:34 +03:00
ret = clk_prepare_enable ( clk ) ;
2020-05-06 01:13:32 +03:00
if ( ret ) {
dev_err ( dev , " Failed to enable clock \n " ) ;
return ret ;
}
2020-05-06 01:13:34 +03:00
ret = devm_add_action_or_reset ( dev , jz4740_rtc_clk_disable , clk ) ;
2020-05-06 01:13:32 +03:00
if ( ret ) {
dev_err ( dev , " Failed to register devm action \n " ) ;
return ret ;
}
2010-06-19 22:29:50 +04:00
spin_lock_init ( & rtc - > lock ) ;
platform_set_drvdata ( pdev , rtc ) ;
2020-05-06 01:13:31 +03:00
device_init_wakeup ( dev , 1 ) ;
2010-10-28 02:33:12 +04:00
2020-05-06 01:13:34 +03:00
ret = dev_pm_set_wake_irq ( dev , irq ) ;
2019-04-30 12:28:19 +03:00
if ( ret ) {
2020-05-06 01:13:31 +03:00
dev_err ( dev , " Failed to set wake irq: %d \n " , ret ) ;
2019-04-30 12:28:19 +03:00
return ret ;
}
2020-05-06 01:13:31 +03:00
rtc - > rtc = devm_rtc_allocate_device ( dev ) ;
2010-06-19 22:29:50 +04:00
if ( IS_ERR ( rtc - > rtc ) ) {
ret = PTR_ERR ( rtc - > rtc ) ;
2020-05-06 01:13:31 +03:00
dev_err ( dev , " Failed to allocate rtc device: %d \n " , ret ) ;
2019-04-30 12:28:15 +03:00
return ret ;
}
rtc - > rtc - > ops = & jz4740_rtc_ops ;
rtc - > rtc - > range_max = U32_MAX ;
2020-05-06 01:13:34 +03:00
rate = clk_get_rate ( clk ) ;
2020-05-06 01:13:33 +03:00
jz4740_rtc_set_wakeup_params ( rtc , np , rate ) ;
2020-05-06 01:13:36 +03:00
/* Each 1 Hz pulse should happen after (rate) ticks */
jz4740_rtc_reg_write ( rtc , JZ_REG_RTC_REGULATOR , rate - 1 ) ;
2020-11-09 19:34:08 +03:00
ret = devm_rtc_register_device ( rtc - > rtc ) ;
2019-08-19 01:00:41 +03:00
if ( ret )
2013-07-04 02:07:06 +04:00
return ret ;
2010-06-19 22:29:50 +04:00
2020-05-06 01:13:34 +03:00
ret = devm_request_irq ( dev , irq , jz4740_rtc_irq , 0 ,
2020-05-06 01:13:31 +03:00
pdev - > name , rtc ) ;
2010-06-19 22:29:50 +04:00
if ( ret ) {
2020-05-06 01:13:31 +03:00
dev_err ( dev , " Failed to request rtc irq: %d \n " , ret ) ;
2013-07-04 02:07:06 +04:00
return ret ;
2010-06-19 22:29:50 +04:00
}
2020-05-06 01:13:30 +03:00
if ( of_device_is_system_power_controller ( np ) ) {
2020-05-06 01:13:33 +03:00
dev_for_power_off = dev ;
if ( ! pm_power_off )
2016-10-31 23:39:48 +03:00
pm_power_off = jz4740_rtc_power_off ;
2020-05-06 01:13:33 +03:00
else
2020-05-06 01:13:31 +03:00
dev_warn ( dev , " Poweroff handler already present! \n " ) ;
2016-10-31 23:39:48 +03:00
}
2010-06-19 22:29:50 +04:00
return 0 ;
}
2012-01-11 03:10:55 +04:00
static struct platform_driver jz4740_rtc_driver = {
2010-10-28 02:33:12 +04:00
. probe = jz4740_rtc_probe ,
. driver = {
. name = " jz4740-rtc " ,
2020-05-06 01:13:30 +03:00
. of_match_table = jz4740_rtc_of_match ,
2010-06-19 22:29:50 +04:00
} ,
} ;
2017-01-25 02:44:16 +03:00
module_platform_driver ( jz4740_rtc_driver ) ;
MODULE_AUTHOR ( " Lars-Peter Clausen <lars@metafoo.de> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " RTC driver for the JZ4740 SoC \n " ) ;
MODULE_ALIAS ( " platform:jz4740-rtc " ) ;