2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-05-06 10:23:41 +03:00
/*
* Copyright ( c ) 2014 - 2015 MediaTek Inc .
* Author : Tianping . Fang < tianping . fang @ mediatek . com >
*/
2019-09-10 10:04:42 +03:00
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/mfd/mt6397/core.h>
2015-05-06 10:23:41 +03:00
# include <linux/module.h>
2019-09-10 10:04:42 +03:00
# include <linux/mutex.h>
2020-04-21 06:00:11 +03:00
# include <linux/of_device.h>
2019-09-10 10:04:42 +03:00
# include <linux/platform_device.h>
2015-05-06 10:23:41 +03:00
# include <linux/regmap.h>
# include <linux/rtc.h>
2019-09-10 10:04:41 +03:00
# include <linux/mfd/mt6397/rtc.h>
2019-09-10 10:04:42 +03:00
# include <linux/mod_devicetable.h>
2015-05-06 10:23:41 +03:00
static int mtk_rtc_write_trigger ( struct mt6397_rtc * rtc )
{
int ret ;
u32 data ;
2020-04-21 06:00:11 +03:00
ret = regmap_write ( rtc - > regmap , rtc - > addr_base + rtc - > data - > wrtgr , 1 ) ;
2015-05-06 10:23:41 +03:00
if ( ret < 0 )
return ret ;
2019-09-10 10:04:42 +03:00
ret = regmap_read_poll_timeout ( rtc - > regmap ,
rtc - > addr_base + RTC_BBPU , data ,
! ( data & RTC_BBPU_CBUSY ) ,
MTK_RTC_POLL_DELAY_US ,
MTK_RTC_POLL_TIMEOUT ) ;
if ( ret < 0 )
dev_err ( rtc - > dev , " failed to write WRTGE: %d \n " , ret ) ;
2015-05-06 10:23:41 +03:00
return ret ;
}
static irqreturn_t mtk_rtc_irq_handler_thread ( int irq , void * data )
{
struct mt6397_rtc * rtc = data ;
u32 irqsta , irqen ;
int ret ;
ret = regmap_read ( rtc - > regmap , rtc - > addr_base + RTC_IRQ_STA , & irqsta ) ;
if ( ( ret > = 0 ) & & ( irqsta & RTC_IRQ_STA_AL ) ) {
rtc_update_irq ( rtc - > rtc_dev , 1 , RTC_IRQF | RTC_AF ) ;
irqen = irqsta & ~ RTC_IRQ_EN_AL ;
mutex_lock ( & rtc - > lock ) ;
if ( regmap_write ( rtc - > regmap , rtc - > addr_base + RTC_IRQ_EN ,
2019-12-11 12:43:54 +03:00
irqen ) = = 0 )
2015-05-06 10:23:41 +03:00
mtk_rtc_write_trigger ( rtc ) ;
mutex_unlock ( & rtc - > lock ) ;
return IRQ_HANDLED ;
}
return IRQ_NONE ;
}
static int __mtk_rtc_read_time ( struct mt6397_rtc * rtc ,
struct rtc_time * tm , int * sec )
{
int ret ;
u16 data [ RTC_OFFSET_COUNT ] ;
mutex_lock ( & rtc - > lock ) ;
ret = regmap_bulk_read ( rtc - > regmap , rtc - > addr_base + RTC_TC_SEC ,
data , RTC_OFFSET_COUNT ) ;
if ( ret < 0 )
goto exit ;
tm - > tm_sec = data [ RTC_OFFSET_SEC ] ;
tm - > tm_min = data [ RTC_OFFSET_MIN ] ;
tm - > tm_hour = data [ RTC_OFFSET_HOUR ] ;
tm - > tm_mday = data [ RTC_OFFSET_DOM ] ;
tm - > tm_mon = data [ RTC_OFFSET_MTH ] ;
tm - > tm_year = data [ RTC_OFFSET_YEAR ] ;
ret = regmap_read ( rtc - > regmap , rtc - > addr_base + RTC_TC_SEC , sec ) ;
exit :
mutex_unlock ( & rtc - > lock ) ;
return ret ;
}
static int mtk_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
time64_t time ;
struct mt6397_rtc * rtc = dev_get_drvdata ( dev ) ;
2015-05-14 23:39:06 +03:00
int days , sec , ret ;
2015-05-06 10:23:41 +03:00
do {
ret = __mtk_rtc_read_time ( rtc , tm , & sec ) ;
if ( ret < 0 )
goto exit ;
} while ( sec < tm - > tm_sec ) ;
/* HW register use 7 bits to store year data, minus
* RTC_MIN_YEAR_OFFSET before write year data to register , and plus
* RTC_MIN_YEAR_OFFSET back after read year from register
*/
tm - > tm_year + = RTC_MIN_YEAR_OFFSET ;
/* HW register start mon from one, but tm_mon start from zero. */
tm - > tm_mon - - ;
time = rtc_tm_to_time64 ( tm ) ;
/* rtc_tm_to_time64 covert Gregorian date to seconds since
* 01 - 01 - 1970 00 : 00 : 00 , and this date is Thursday .
*/
2015-05-14 23:39:06 +03:00
days = div_s64 ( time , 86400 ) ;
tm - > tm_wday = ( days + 4 ) % 7 ;
2015-05-06 10:23:41 +03:00
exit :
return ret ;
}
static int mtk_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
struct mt6397_rtc * rtc = dev_get_drvdata ( dev ) ;
int ret ;
u16 data [ RTC_OFFSET_COUNT ] ;
tm - > tm_year - = RTC_MIN_YEAR_OFFSET ;
tm - > tm_mon + + ;
data [ RTC_OFFSET_SEC ] = tm - > tm_sec ;
data [ RTC_OFFSET_MIN ] = tm - > tm_min ;
data [ RTC_OFFSET_HOUR ] = tm - > tm_hour ;
data [ RTC_OFFSET_DOM ] = tm - > tm_mday ;
data [ RTC_OFFSET_MTH ] = tm - > tm_mon ;
data [ RTC_OFFSET_YEAR ] = tm - > tm_year ;
mutex_lock ( & rtc - > lock ) ;
ret = regmap_bulk_write ( rtc - > regmap , rtc - > addr_base + RTC_TC_SEC ,
data , RTC_OFFSET_COUNT ) ;
if ( ret < 0 )
goto exit ;
/* Time register write to hardware after call trigger function */
ret = mtk_rtc_write_trigger ( rtc ) ;
exit :
mutex_unlock ( & rtc - > lock ) ;
return ret ;
}
static int mtk_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
struct rtc_time * tm = & alm - > time ;
struct mt6397_rtc * rtc = dev_get_drvdata ( dev ) ;
u32 irqen , pdn2 ;
int ret ;
u16 data [ RTC_OFFSET_COUNT ] ;
mutex_lock ( & rtc - > lock ) ;
ret = regmap_read ( rtc - > regmap , rtc - > addr_base + RTC_IRQ_EN , & irqen ) ;
if ( ret < 0 )
goto err_exit ;
ret = regmap_read ( rtc - > regmap , rtc - > addr_base + RTC_PDN2 , & pdn2 ) ;
if ( ret < 0 )
goto err_exit ;
ret = regmap_bulk_read ( rtc - > regmap , rtc - > addr_base + RTC_AL_SEC ,
data , RTC_OFFSET_COUNT ) ;
if ( ret < 0 )
goto err_exit ;
alm - > enabled = ! ! ( irqen & RTC_IRQ_EN_AL ) ;
alm - > pending = ! ! ( pdn2 & RTC_PDN2_PWRON_ALARM ) ;
mutex_unlock ( & rtc - > lock ) ;
2019-12-11 12:43:54 +03:00
tm - > tm_sec = data [ RTC_OFFSET_SEC ] & RTC_AL_SEC_MASK ;
tm - > tm_min = data [ RTC_OFFSET_MIN ] & RTC_AL_MIN_MASK ;
tm - > tm_hour = data [ RTC_OFFSET_HOUR ] & RTC_AL_HOU_MASK ;
tm - > tm_mday = data [ RTC_OFFSET_DOM ] & RTC_AL_DOM_MASK ;
tm - > tm_mon = data [ RTC_OFFSET_MTH ] & RTC_AL_MTH_MASK ;
tm - > tm_year = data [ RTC_OFFSET_YEAR ] & RTC_AL_YEA_MASK ;
2015-05-06 10:23:41 +03:00
tm - > tm_year + = RTC_MIN_YEAR_OFFSET ;
tm - > tm_mon - - ;
return 0 ;
err_exit :
mutex_unlock ( & rtc - > lock ) ;
return ret ;
}
static int mtk_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
struct rtc_time * tm = & alm - > time ;
struct mt6397_rtc * rtc = dev_get_drvdata ( dev ) ;
int ret ;
u16 data [ RTC_OFFSET_COUNT ] ;
tm - > tm_year - = RTC_MIN_YEAR_OFFSET ;
tm - > tm_mon + + ;
mutex_lock ( & rtc - > lock ) ;
2019-12-11 12:43:54 +03:00
ret = regmap_bulk_read ( rtc - > regmap , rtc - > addr_base + RTC_AL_SEC ,
data , RTC_OFFSET_COUNT ) ;
if ( ret < 0 )
goto exit ;
data [ RTC_OFFSET_SEC ] = ( ( data [ RTC_OFFSET_SEC ] & ~ ( RTC_AL_SEC_MASK ) ) |
( tm - > tm_sec & RTC_AL_SEC_MASK ) ) ;
data [ RTC_OFFSET_MIN ] = ( ( data [ RTC_OFFSET_MIN ] & ~ ( RTC_AL_MIN_MASK ) ) |
( tm - > tm_min & RTC_AL_MIN_MASK ) ) ;
data [ RTC_OFFSET_HOUR ] = ( ( data [ RTC_OFFSET_HOUR ] & ~ ( RTC_AL_HOU_MASK ) ) |
( tm - > tm_hour & RTC_AL_HOU_MASK ) ) ;
data [ RTC_OFFSET_DOM ] = ( ( data [ RTC_OFFSET_DOM ] & ~ ( RTC_AL_DOM_MASK ) ) |
( tm - > tm_mday & RTC_AL_DOM_MASK ) ) ;
data [ RTC_OFFSET_MTH ] = ( ( data [ RTC_OFFSET_MTH ] & ~ ( RTC_AL_MTH_MASK ) ) |
( tm - > tm_mon & RTC_AL_MTH_MASK ) ) ;
data [ RTC_OFFSET_YEAR ] = ( ( data [ RTC_OFFSET_YEAR ] & ~ ( RTC_AL_YEA_MASK ) ) |
( tm - > tm_year & RTC_AL_YEA_MASK ) ) ;
2015-05-06 10:23:41 +03:00
if ( alm - > enabled ) {
ret = regmap_bulk_write ( rtc - > regmap ,
rtc - > addr_base + RTC_AL_SEC ,
data , RTC_OFFSET_COUNT ) ;
if ( ret < 0 )
goto exit ;
ret = regmap_write ( rtc - > regmap , rtc - > addr_base + RTC_AL_MASK ,
RTC_AL_MASK_DOW ) ;
if ( ret < 0 )
goto exit ;
ret = regmap_update_bits ( rtc - > regmap ,
rtc - > addr_base + RTC_IRQ_EN ,
RTC_IRQ_EN_ONESHOT_AL ,
RTC_IRQ_EN_ONESHOT_AL ) ;
if ( ret < 0 )
goto exit ;
} else {
ret = regmap_update_bits ( rtc - > regmap ,
rtc - > addr_base + RTC_IRQ_EN ,
RTC_IRQ_EN_ONESHOT_AL , 0 ) ;
if ( ret < 0 )
goto exit ;
}
/* All alarm time register write to hardware after calling
* mtk_rtc_write_trigger . This can avoid race condition if alarm
* occur happen during writing alarm time register .
*/
ret = mtk_rtc_write_trigger ( rtc ) ;
exit :
mutex_unlock ( & rtc - > lock ) ;
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 mtk_rtc_ops = {
2015-05-06 10:23:41 +03:00
. read_time = mtk_rtc_read_time ,
. set_time = mtk_rtc_set_time ,
. read_alarm = mtk_rtc_read_alarm ,
. set_alarm = mtk_rtc_set_alarm ,
} ;
static int mtk_rtc_probe ( struct platform_device * pdev )
{
struct resource * res ;
struct mt6397_chip * mt6397_chip = dev_get_drvdata ( pdev - > dev . parent ) ;
struct mt6397_rtc * rtc ;
int ret ;
rtc = devm_kzalloc ( & pdev - > dev , sizeof ( struct mt6397_rtc ) , GFP_KERNEL ) ;
if ( ! rtc )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
rtc - > addr_base = res - > start ;
2020-04-21 06:00:11 +03:00
rtc - > data = of_device_get_match_data ( & pdev - > dev ) ;
2017-10-25 16:15:59 +03:00
rtc - > irq = platform_get_irq ( pdev , 0 ) ;
if ( rtc - > irq < 0 )
return rtc - > irq ;
2015-05-06 10:23:41 +03:00
rtc - > regmap = mt6397_chip - > regmap ;
mutex_init ( & rtc - > lock ) ;
platform_set_drvdata ( pdev , rtc ) ;
2019-09-10 10:04:42 +03:00
rtc - > rtc_dev = devm_rtc_allocate_device ( & pdev - > dev ) ;
2018-09-09 23:38:46 +03:00
if ( IS_ERR ( rtc - > rtc_dev ) )
return PTR_ERR ( rtc - > rtc_dev ) ;
2019-09-10 10:04:42 +03:00
ret = devm_request_threaded_irq ( & pdev - > dev , rtc - > irq , NULL ,
mtk_rtc_irq_handler_thread ,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH ,
" mt6397-rtc " , rtc ) ;
2015-05-06 10:23:41 +03:00
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to request alarm IRQ: %d: %d \n " ,
rtc - > irq , ret ) ;
2019-03-11 10:55:40 +03:00
return ret ;
2015-05-06 10:23:41 +03:00
}
2015-07-02 11:36:56 +03:00
device_init_wakeup ( & pdev - > dev , 1 ) ;
2018-09-09 23:38:46 +03:00
rtc - > rtc_dev - > ops = & mtk_rtc_ops ;
2019-11-13 05:17:20 +03:00
return rtc_register_device ( rtc - > rtc_dev ) ;
2015-05-06 10:23:41 +03:00
}
2015-07-30 17:53:14 +03:00
# ifdef CONFIG_PM_SLEEP
static int mt6397_rtc_suspend ( struct device * dev )
{
struct mt6397_rtc * rtc = dev_get_drvdata ( dev ) ;
if ( device_may_wakeup ( dev ) )
enable_irq_wake ( rtc - > irq ) ;
return 0 ;
}
static int mt6397_rtc_resume ( struct device * dev )
{
struct mt6397_rtc * rtc = dev_get_drvdata ( dev ) ;
if ( device_may_wakeup ( dev ) )
disable_irq_wake ( rtc - > irq ) ;
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( mt6397_pm_ops , mt6397_rtc_suspend ,
mt6397_rtc_resume ) ;
2020-04-21 06:00:11 +03:00
static const struct mtk_rtc_data mt6358_rtc_data = {
. wrtgr = RTC_WRTGR_MT6358 ,
} ;
static const struct mtk_rtc_data mt6397_rtc_data = {
. wrtgr = RTC_WRTGR_MT6397 ,
} ;
2015-05-06 10:23:41 +03:00
static const struct of_device_id mt6397_rtc_of_match [ ] = {
2020-04-21 06:00:11 +03:00
{ . compatible = " mediatek,mt6323-rtc " , . data = & mt6397_rtc_data } ,
{ . compatible = " mediatek,mt6358-rtc " , . data = & mt6358_rtc_data } ,
{ . compatible = " mediatek,mt6397-rtc " , . data = & mt6397_rtc_data } ,
2015-05-06 10:23:41 +03:00
{ }
} ;
2015-08-27 14:52:02 +03:00
MODULE_DEVICE_TABLE ( of , mt6397_rtc_of_match ) ;
2015-05-06 10:23:41 +03:00
static struct platform_driver mtk_rtc_driver = {
. driver = {
. name = " mt6397-rtc " ,
. of_match_table = mt6397_rtc_of_match ,
2015-07-30 17:53:14 +03:00
. pm = & mt6397_pm_ops ,
2015-05-06 10:23:41 +03:00
} ,
. probe = mtk_rtc_probe ,
} ;
module_platform_driver ( mtk_rtc_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Tianping Fang <tianping.fang@mediatek.com> " ) ;
MODULE_DESCRIPTION ( " RTC Driver for MediaTek MT6397 PMIC " ) ;