2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-02-02 22:56:10 +03:00
/*
* Copyright ( C ) 2016 Oleksij Rempel < linux @ rempel - privat . de >
*/
# include <linux/clk.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/rtc.h>
/* Miscellaneous registers */
/* Interrupt Location Register */
# define HW_ILR 0x00
# define BM_RTCALF BIT(1)
# define BM_RTCCIF BIT(0)
/* Clock Control Register */
# define HW_CCR 0x08
/* Calibration counter disable */
# define BM_CCALOFF BIT(4)
/* Reset internal oscillator divider */
# define BM_CTCRST BIT(1)
/* Clock Enable */
# define BM_CLKEN BIT(0)
/* Counter Increment Interrupt Register */
# define HW_CIIR 0x0C
# define BM_CIIR_IMYEAR BIT(7)
# define BM_CIIR_IMMON BIT(6)
# define BM_CIIR_IMDOY BIT(5)
# define BM_CIIR_IMDOW BIT(4)
# define BM_CIIR_IMDOM BIT(3)
# define BM_CIIR_IMHOUR BIT(2)
# define BM_CIIR_IMMIN BIT(1)
# define BM_CIIR_IMSEC BIT(0)
/* Alarm Mask Register */
# define HW_AMR 0x10
# define BM_AMR_IMYEAR BIT(7)
# define BM_AMR_IMMON BIT(6)
# define BM_AMR_IMDOY BIT(5)
# define BM_AMR_IMDOW BIT(4)
# define BM_AMR_IMDOM BIT(3)
# define BM_AMR_IMHOUR BIT(2)
# define BM_AMR_IMMIN BIT(1)
# define BM_AMR_IMSEC BIT(0)
# define BM_AMR_OFF 0xff
/* Consolidated time registers */
# define HW_CTIME0 0x14
# define BM_CTIME0_DOW_S 24
# define BM_CTIME0_DOW_M 0x7
# define BM_CTIME0_HOUR_S 16
# define BM_CTIME0_HOUR_M 0x1f
# define BM_CTIME0_MIN_S 8
# define BM_CTIME0_MIN_M 0x3f
# define BM_CTIME0_SEC_S 0
# define BM_CTIME0_SEC_M 0x3f
# define HW_CTIME1 0x18
# define BM_CTIME1_YEAR_S 16
# define BM_CTIME1_YEAR_M 0xfff
# define BM_CTIME1_MON_S 8
# define BM_CTIME1_MON_M 0xf
# define BM_CTIME1_DOM_S 0
# define BM_CTIME1_DOM_M 0x1f
# define HW_CTIME2 0x1C
# define BM_CTIME2_DOY_S 0
# define BM_CTIME2_DOY_M 0xfff
/* Time counter registers */
# define HW_SEC 0x20
# define HW_MIN 0x24
# define HW_HOUR 0x28
# define HW_DOM 0x2C
# define HW_DOW 0x30
# define HW_DOY 0x34
# define HW_MONTH 0x38
# define HW_YEAR 0x3C
# define HW_CALIBRATION 0x40
# define BM_CALDIR_BACK BIT(17)
# define BM_CALVAL_M 0x1ffff
/* General purpose registers */
# define HW_GPREG0 0x44
# define HW_GPREG1 0x48
# define HW_GPREG2 0x4C
# define HW_GPREG3 0x50
# define HW_GPREG4 0x54
/* Alarm register group */
# define HW_ALSEC 0x60
# define HW_ALMIN 0x64
# define HW_ALHOUR 0x68
# define HW_ALDOM 0x6C
# define HW_ALDOW 0x70
# define HW_ALDOY 0x74
# define HW_ALMON 0x78
# define HW_ALYEAR 0x7C
struct asm9260_rtc_priv {
struct device * dev ;
void __iomem * iobase ;
struct rtc_device * rtc ;
struct clk * clk ;
} ;
static irqreturn_t asm9260_rtc_irq ( int irq , void * dev_id )
{
struct asm9260_rtc_priv * priv = dev_id ;
u32 isr ;
unsigned long events = 0 ;
2021-01-20 01:06:40 +03:00
rtc_lock ( priv - > rtc ) ;
2016-02-02 22:56:10 +03:00
isr = ioread32 ( priv - > iobase + HW_CIIR ) ;
2016-08-31 19:15:28 +03:00
if ( ! isr ) {
2021-01-20 01:06:40 +03:00
rtc_unlock ( priv - > rtc ) ;
2016-02-02 22:56:10 +03:00
return IRQ_NONE ;
2016-08-31 19:15:28 +03:00
}
2016-02-02 22:56:10 +03:00
iowrite32 ( 0 , priv - > iobase + HW_CIIR ) ;
2021-01-20 01:06:40 +03:00
rtc_unlock ( priv - > rtc ) ;
2016-02-02 22:56:10 +03:00
events | = RTC_AF | RTC_IRQF ;
rtc_update_irq ( priv - > rtc , 1 , events ) ;
return IRQ_HANDLED ;
}
static int asm9260_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
struct asm9260_rtc_priv * priv = dev_get_drvdata ( dev ) ;
u32 ctime0 , ctime1 , ctime2 ;
ctime0 = ioread32 ( priv - > iobase + HW_CTIME0 ) ;
ctime1 = ioread32 ( priv - > iobase + HW_CTIME1 ) ;
ctime2 = ioread32 ( priv - > iobase + HW_CTIME2 ) ;
if ( ctime1 ! = ioread32 ( priv - > iobase + HW_CTIME1 ) ) {
/*
* woops , counter flipped right now . Now we are safe
* to reread .
*/
ctime0 = ioread32 ( priv - > iobase + HW_CTIME0 ) ;
ctime1 = ioread32 ( priv - > iobase + HW_CTIME1 ) ;
ctime2 = ioread32 ( priv - > iobase + HW_CTIME2 ) ;
}
tm - > tm_sec = ( ctime0 > > BM_CTIME0_SEC_S ) & BM_CTIME0_SEC_M ;
tm - > tm_min = ( ctime0 > > BM_CTIME0_MIN_S ) & BM_CTIME0_MIN_M ;
tm - > tm_hour = ( ctime0 > > BM_CTIME0_HOUR_S ) & BM_CTIME0_HOUR_M ;
tm - > tm_wday = ( ctime0 > > BM_CTIME0_DOW_S ) & BM_CTIME0_DOW_M ;
tm - > tm_mday = ( ctime1 > > BM_CTIME1_DOM_S ) & BM_CTIME1_DOM_M ;
tm - > tm_mon = ( ctime1 > > BM_CTIME1_MON_S ) & BM_CTIME1_MON_M ;
tm - > tm_year = ( ctime1 > > BM_CTIME1_YEAR_S ) & BM_CTIME1_YEAR_M ;
tm - > tm_yday = ( ctime2 > > BM_CTIME2_DOY_S ) & BM_CTIME2_DOY_M ;
return 0 ;
}
static int asm9260_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
struct asm9260_rtc_priv * priv = dev_get_drvdata ( dev ) ;
/*
* make sure SEC counter will not flip other counter on write time ,
* real value will be written at the enf of sequence .
*/
iowrite32 ( 0 , priv - > iobase + HW_SEC ) ;
iowrite32 ( tm - > tm_year , priv - > iobase + HW_YEAR ) ;
iowrite32 ( tm - > tm_mon , priv - > iobase + HW_MONTH ) ;
iowrite32 ( tm - > tm_mday , priv - > iobase + HW_DOM ) ;
iowrite32 ( tm - > tm_wday , priv - > iobase + HW_DOW ) ;
iowrite32 ( tm - > tm_yday , priv - > iobase + HW_DOY ) ;
iowrite32 ( tm - > tm_hour , priv - > iobase + HW_HOUR ) ;
iowrite32 ( tm - > tm_min , priv - > iobase + HW_MIN ) ;
iowrite32 ( tm - > tm_sec , priv - > iobase + HW_SEC ) ;
return 0 ;
}
static int asm9260_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct asm9260_rtc_priv * priv = dev_get_drvdata ( dev ) ;
alrm - > time . tm_year = ioread32 ( priv - > iobase + HW_ALYEAR ) ;
alrm - > time . tm_mon = ioread32 ( priv - > iobase + HW_ALMON ) ;
alrm - > time . tm_mday = ioread32 ( priv - > iobase + HW_ALDOM ) ;
alrm - > time . tm_wday = ioread32 ( priv - > iobase + HW_ALDOW ) ;
alrm - > time . tm_yday = ioread32 ( priv - > iobase + HW_ALDOY ) ;
alrm - > time . tm_hour = ioread32 ( priv - > iobase + HW_ALHOUR ) ;
alrm - > time . tm_min = ioread32 ( priv - > iobase + HW_ALMIN ) ;
alrm - > time . tm_sec = ioread32 ( priv - > iobase + HW_ALSEC ) ;
alrm - > enabled = ioread32 ( priv - > iobase + HW_AMR ) ? 1 : 0 ;
alrm - > pending = ioread32 ( priv - > iobase + HW_CIIR ) ? 1 : 0 ;
return rtc_valid_tm ( & alrm - > time ) ;
}
static int asm9260_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct asm9260_rtc_priv * priv = dev_get_drvdata ( dev ) ;
iowrite32 ( alrm - > time . tm_year , priv - > iobase + HW_ALYEAR ) ;
iowrite32 ( alrm - > time . tm_mon , priv - > iobase + HW_ALMON ) ;
iowrite32 ( alrm - > time . tm_mday , priv - > iobase + HW_ALDOM ) ;
iowrite32 ( alrm - > time . tm_wday , priv - > iobase + HW_ALDOW ) ;
iowrite32 ( alrm - > time . tm_yday , priv - > iobase + HW_ALDOY ) ;
iowrite32 ( alrm - > time . tm_hour , priv - > iobase + HW_ALHOUR ) ;
iowrite32 ( alrm - > time . tm_min , priv - > iobase + HW_ALMIN ) ;
iowrite32 ( alrm - > time . tm_sec , priv - > iobase + HW_ALSEC ) ;
iowrite32 ( alrm - > enabled ? 0 : BM_AMR_OFF , priv - > iobase + HW_AMR ) ;
return 0 ;
}
static int asm9260_alarm_irq_enable ( struct device * dev , unsigned int enabled )
{
struct asm9260_rtc_priv * priv = dev_get_drvdata ( dev ) ;
iowrite32 ( enabled ? 0 : BM_AMR_OFF , priv - > iobase + HW_AMR ) ;
return 0 ;
}
static const struct rtc_class_ops asm9260_rtc_ops = {
. read_time = asm9260_rtc_read_time ,
. set_time = asm9260_rtc_set_time ,
. read_alarm = asm9260_rtc_read_alarm ,
. set_alarm = asm9260_rtc_set_alarm ,
. alarm_irq_enable = asm9260_alarm_irq_enable ,
} ;
2016-03-16 00:44:31 +03:00
static int asm9260_rtc_probe ( struct platform_device * pdev )
2016-02-02 22:56:10 +03:00
{
struct asm9260_rtc_priv * priv ;
struct device * dev = & pdev - > dev ;
int irq_alarm , ret ;
u32 ccr ;
priv = devm_kzalloc ( dev , sizeof ( struct asm9260_rtc_priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , priv ) ;
irq_alarm = platform_get_irq ( pdev , 0 ) ;
2019-07-30 21:15:39 +03:00
if ( irq_alarm < 0 )
2016-02-02 22:56:10 +03:00
return irq_alarm ;
2019-10-06 13:29:20 +03:00
priv - > iobase = devm_platform_ioremap_resource ( pdev , 0 ) ;
2016-02-02 22:56:10 +03:00
if ( IS_ERR ( priv - > iobase ) )
return PTR_ERR ( priv - > iobase ) ;
priv - > clk = devm_clk_get ( dev , " ahb " ) ;
2019-12-14 10:45:28 +03:00
if ( IS_ERR ( priv - > clk ) )
return PTR_ERR ( priv - > clk ) ;
2016-02-02 22:56:10 +03:00
ret = clk_prepare_enable ( priv - > clk ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable clk! \n " ) ;
return ret ;
}
ccr = ioread32 ( priv - > iobase + HW_CCR ) ;
/* if dev is not enabled, reset it */
if ( ( ccr & ( BM_CLKEN | BM_CTCRST ) ) ! = BM_CLKEN ) {
iowrite32 ( BM_CTCRST , priv - > iobase + HW_CCR ) ;
ccr = 0 ;
}
iowrite32 ( BM_CLKEN | ccr , priv - > iobase + HW_CCR ) ;
iowrite32 ( 0 , priv - > iobase + HW_CIIR ) ;
iowrite32 ( BM_AMR_OFF , priv - > iobase + HW_AMR ) ;
priv - > rtc = devm_rtc_device_register ( dev , dev_name ( dev ) ,
& asm9260_rtc_ops , THIS_MODULE ) ;
if ( IS_ERR ( priv - > rtc ) ) {
ret = PTR_ERR ( priv - > rtc ) ;
dev_err ( dev , " Failed to register RTC device: %d \n " , ret ) ;
goto err_return ;
}
ret = devm_request_threaded_irq ( dev , irq_alarm , NULL ,
asm9260_rtc_irq , IRQF_ONESHOT ,
dev_name ( dev ) , priv ) ;
if ( ret < 0 ) {
dev_err ( dev , " can't get irq %i, err %d \n " ,
irq_alarm , ret ) ;
goto err_return ;
}
return 0 ;
err_return :
clk_disable_unprepare ( priv - > clk ) ;
return ret ;
}
2016-03-16 00:44:31 +03:00
static int asm9260_rtc_remove ( struct platform_device * pdev )
2016-02-02 22:56:10 +03:00
{
struct asm9260_rtc_priv * priv = platform_get_drvdata ( pdev ) ;
/* Disable alarm matching */
iowrite32 ( BM_AMR_OFF , priv - > iobase + HW_AMR ) ;
clk_disable_unprepare ( priv - > clk ) ;
return 0 ;
}
static const struct of_device_id asm9260_dt_ids [ ] = {
{ . compatible = " alphascale,asm9260-rtc " , } ,
{ }
} ;
2016-10-14 16:15:35 +03:00
MODULE_DEVICE_TABLE ( of , asm9260_dt_ids ) ;
2016-02-02 22:56:10 +03:00
static struct platform_driver asm9260_rtc_driver = {
. probe = asm9260_rtc_probe ,
. remove = asm9260_rtc_remove ,
. driver = {
. name = " asm9260-rtc " ,
. of_match_table = asm9260_dt_ids ,
} ,
} ;
module_platform_driver ( asm9260_rtc_driver ) ;
MODULE_AUTHOR ( " Oleksij Rempel <linux@rempel-privat.de> " ) ;
MODULE_DESCRIPTION ( " Alphascale asm9260 SoC Realtime Clock Driver (RTC) " ) ;
MODULE_LICENSE ( " GPL " ) ;