2019-03-07 14:04:08 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-08-19 12:53:22 +03:00
/*
* Xilinx Zynq Ultrascale + MPSoC Real Time Clock Driver
*
* Copyright ( C ) 2015 Xilinx , Inc .
*
*/
2022-06-26 10:08:17 +03:00
# include <linux/clk.h>
2015-08-19 12:53:22 +03:00
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/rtc.h>
/* RTC Registers */
# define RTC_SET_TM_WR 0x00
# define RTC_SET_TM_RD 0x04
# define RTC_CALIB_WR 0x08
# define RTC_CALIB_RD 0x0C
# define RTC_CUR_TM 0x10
# define RTC_CUR_TICK 0x14
# define RTC_ALRM 0x18
# define RTC_INT_STS 0x20
# define RTC_INT_MASK 0x24
# define RTC_INT_EN 0x28
# define RTC_INT_DIS 0x2C
# define RTC_CTRL 0x40
# define RTC_FR_EN BIT(20)
# define RTC_FR_DATSHIFT 16
# define RTC_TICK_MASK 0xFFFF
# define RTC_INT_SEC BIT(0)
# define RTC_INT_ALRM BIT(1)
# define RTC_OSC_EN BIT(24)
2016-04-12 15:15:44 +03:00
# define RTC_BATT_EN BIT(31)
2015-08-19 12:53:22 +03:00
2022-06-26 10:08:16 +03:00
# define RTC_CALIB_DEF 0x7FFF
2015-08-19 12:53:22 +03:00
# define RTC_CALIB_MASK 0x1FFFFF
2020-02-12 13:24:39 +03:00
# define RTC_ALRM_MASK BIT(1)
# define RTC_MSEC 1000
2022-06-26 10:08:17 +03:00
# define RTC_FR_MASK 0xF0000
# define RTC_FR_MAX_TICKS 16
# define RTC_PPB 1000000000LL
# define RTC_MIN_OFFSET -32768000
# define RTC_MAX_OFFSET 32767000
2015-08-19 12:53:22 +03:00
struct xlnx_rtc_dev {
struct rtc_device * rtc ;
void __iomem * reg_base ;
int alarm_irq ;
int sec_irq ;
2022-06-26 10:08:17 +03:00
struct clk * rtc_clk ;
unsigned int freq ;
2015-08-19 12:53:22 +03:00
} ;
static int xlnx_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
struct xlnx_rtc_dev * xrtcdev = dev_get_drvdata ( dev ) ;
unsigned long new_time ;
2016-04-20 18:47:35 +03:00
/*
* The value written will be updated after 1 sec into the
* seconds read register , so we need to program time + 1 sec
* to get the correct time on read .
*/
new_time = rtc_tm_to_time64 ( tm ) + 1 ;
2015-08-19 12:53:22 +03:00
writel ( new_time , xrtcdev - > reg_base + RTC_SET_TM_WR ) ;
2016-04-20 18:47:35 +03:00
/*
* Clear the rtc interrupt status register after setting the
* time . During a read_time function , the code should read the
* RTC_INT_STATUS register and if bit 0 is still 0 , it means
* that one second has not elapsed yet since RTC was set and
* the current time should be read from SET_TIME_READ register ;
* otherwise , CURRENT_TIME register is read to report the time
*/
writel ( RTC_INT_SEC , xrtcdev - > reg_base + RTC_INT_STS ) ;
2015-08-19 12:53:22 +03:00
return 0 ;
}
static int xlnx_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
2016-04-20 18:47:35 +03:00
u32 status ;
unsigned long read_time ;
2015-08-19 12:53:22 +03:00
struct xlnx_rtc_dev * xrtcdev = dev_get_drvdata ( dev ) ;
2016-04-20 18:47:35 +03:00
status = readl ( xrtcdev - > reg_base + RTC_INT_STS ) ;
if ( status & RTC_INT_SEC ) {
/*
* RTC has updated the CURRENT_TIME with the time written into
* SET_TIME_WRITE register .
*/
2019-11-28 04:56:12 +03:00
read_time = readl ( xrtcdev - > reg_base + RTC_CUR_TM ) ;
2016-04-20 18:47:35 +03:00
} else {
/*
* Time written in SET_TIME_WRITE has not yet updated into
* the seconds read register , so read the time from the
* SET_TIME_WRITE instead of CURRENT_TIME register .
* Since we add + 1 sec while writing , we need to - 1 sec while
* reading .
*/
read_time = readl ( xrtcdev - > reg_base + RTC_SET_TM_RD ) - 1 ;
}
2019-11-28 04:56:12 +03:00
rtc_time64_to_tm ( read_time , tm ) ;
2015-08-19 12:53:22 +03:00
2018-02-19 18:23:55 +03:00
return 0 ;
2015-08-19 12:53:22 +03:00
}
static int xlnx_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct xlnx_rtc_dev * xrtcdev = dev_get_drvdata ( dev ) ;
rtc_time64_to_tm ( readl ( xrtcdev - > reg_base + RTC_ALRM ) , & alrm - > time ) ;
alrm - > enabled = readl ( xrtcdev - > reg_base + RTC_INT_MASK ) & RTC_INT_ALRM ;
return 0 ;
}
static int xlnx_rtc_alarm_irq_enable ( struct device * dev , u32 enabled )
{
struct xlnx_rtc_dev * xrtcdev = dev_get_drvdata ( dev ) ;
2020-02-12 13:24:39 +03:00
unsigned int status ;
ulong timeout ;
timeout = jiffies + msecs_to_jiffies ( RTC_MSEC ) ;
if ( enabled ) {
while ( 1 ) {
status = readl ( xrtcdev - > reg_base + RTC_INT_STS ) ;
if ( ! ( ( status & RTC_ALRM_MASK ) = = RTC_ALRM_MASK ) )
break ;
if ( time_after_eq ( jiffies , timeout ) ) {
dev_err ( dev , " Time out occur, while clearing alarm status bit \n " ) ;
return - ETIMEDOUT ;
}
writel ( RTC_INT_ALRM , xrtcdev - > reg_base + RTC_INT_STS ) ;
}
2015-08-19 12:53:22 +03:00
writel ( RTC_INT_ALRM , xrtcdev - > reg_base + RTC_INT_EN ) ;
2020-02-12 13:24:39 +03:00
} else {
2015-08-19 12:53:22 +03:00
writel ( RTC_INT_ALRM , xrtcdev - > reg_base + RTC_INT_DIS ) ;
2020-02-12 13:24:39 +03:00
}
2015-08-19 12:53:22 +03:00
return 0 ;
}
static int xlnx_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct xlnx_rtc_dev * xrtcdev = dev_get_drvdata ( dev ) ;
unsigned long alarm_time ;
alarm_time = rtc_tm_to_time64 ( & alrm - > time ) ;
writel ( ( u32 ) alarm_time , ( xrtcdev - > reg_base + RTC_ALRM ) ) ;
xlnx_rtc_alarm_irq_enable ( dev , alrm - > enabled ) ;
return 0 ;
}
2016-04-12 15:15:45 +03:00
static void xlnx_init_rtc ( struct xlnx_rtc_dev * xrtcdev )
2015-08-19 12:53:22 +03:00
{
2016-04-12 15:15:44 +03:00
u32 rtc_ctrl ;
/* Enable RTC switch to battery when VCC_PSAUX is not available */
rtc_ctrl = readl ( xrtcdev - > reg_base + RTC_CTRL ) ;
rtc_ctrl | = RTC_BATT_EN ;
writel ( rtc_ctrl , xrtcdev - > reg_base + RTC_CTRL ) ;
2022-06-26 10:08:17 +03:00
}
2016-04-12 15:15:44 +03:00
2022-06-26 10:08:17 +03:00
static int xlnx_rtc_read_offset ( struct device * dev , long * offset )
{
struct xlnx_rtc_dev * xrtcdev = dev_get_drvdata ( dev ) ;
unsigned long long rtc_ppb = RTC_PPB ;
unsigned int tick_mult = do_div ( rtc_ppb , xrtcdev - > freq ) ;
unsigned int calibval ;
long offset_val ;
calibval = readl ( xrtcdev - > reg_base + RTC_CALIB_RD ) ;
/* Offset with seconds ticks */
offset_val = calibval & RTC_TICK_MASK ;
offset_val = offset_val - RTC_CALIB_DEF ;
offset_val = offset_val * tick_mult ;
/* Offset with fractional ticks */
if ( calibval & RTC_FR_EN )
offset_val + = ( ( calibval & RTC_FR_MASK ) > > RTC_FR_DATSHIFT )
* ( tick_mult / RTC_FR_MAX_TICKS ) ;
* offset = offset_val ;
return 0 ;
}
static int xlnx_rtc_set_offset ( struct device * dev , long offset )
{
struct xlnx_rtc_dev * xrtcdev = dev_get_drvdata ( dev ) ;
unsigned long long rtc_ppb = RTC_PPB ;
unsigned int tick_mult = do_div ( rtc_ppb , xrtcdev - > freq ) ;
2022-07-27 13:00:18 +03:00
unsigned char fract_tick = 0 ;
2022-06-26 10:08:17 +03:00
unsigned int calibval ;
short int max_tick ;
int fract_offset ;
if ( offset < RTC_MIN_OFFSET | | offset > RTC_MAX_OFFSET )
return - ERANGE ;
/* Number ticks for given offset */
max_tick = div_s64_rem ( offset , tick_mult , & fract_offset ) ;
/* Number fractional ticks for given offset */
if ( fract_offset ) {
if ( fract_offset < 0 ) {
fract_offset = fract_offset + tick_mult ;
max_tick - - ;
}
if ( fract_offset > ( tick_mult / RTC_FR_MAX_TICKS ) ) {
for ( fract_tick = 1 ; fract_tick < 16 ; fract_tick + + ) {
if ( fract_offset < =
( fract_tick *
( tick_mult / RTC_FR_MAX_TICKS ) ) )
break ;
}
}
}
/* Zynqmp RTC uses second and fractional tick
* counters for compensation
2015-08-19 12:53:22 +03:00
*/
2022-06-26 10:08:17 +03:00
calibval = max_tick + RTC_CALIB_DEF ;
if ( fract_tick )
calibval | = RTC_FR_EN ;
calibval | = ( fract_tick < < RTC_FR_DATSHIFT ) ;
writel ( calibval , ( xrtcdev - > reg_base + RTC_CALIB_WR ) ) ;
return 0 ;
2015-08-19 12:53:22 +03:00
}
static const struct rtc_class_ops xlnx_rtc_ops = {
. set_time = xlnx_rtc_set_time ,
. read_time = xlnx_rtc_read_time ,
. read_alarm = xlnx_rtc_read_alarm ,
. set_alarm = xlnx_rtc_set_alarm ,
. alarm_irq_enable = xlnx_rtc_alarm_irq_enable ,
2022-06-26 10:08:17 +03:00
. read_offset = xlnx_rtc_read_offset ,
. set_offset = xlnx_rtc_set_offset ,
2015-08-19 12:53:22 +03:00
} ;
static irqreturn_t xlnx_rtc_interrupt ( int irq , void * id )
{
struct xlnx_rtc_dev * xrtcdev = ( struct xlnx_rtc_dev * ) id ;
unsigned int status ;
status = readl ( xrtcdev - > reg_base + RTC_INT_STS ) ;
/* Check if interrupt asserted */
if ( ! ( status & ( RTC_INT_SEC | RTC_INT_ALRM ) ) )
return IRQ_NONE ;
2020-02-12 13:24:39 +03:00
/* Disable RTC_INT_ALRM interrupt only */
writel ( RTC_INT_ALRM , xrtcdev - > reg_base + RTC_INT_DIS ) ;
2015-08-19 12:53:22 +03:00
if ( status & RTC_INT_ALRM )
rtc_update_irq ( xrtcdev - > rtc , 1 , RTC_IRQF | RTC_AF ) ;
return IRQ_HANDLED ;
}
static int xlnx_rtc_probe ( struct platform_device * pdev )
{
struct xlnx_rtc_dev * xrtcdev ;
int ret ;
xrtcdev = devm_kzalloc ( & pdev - > dev , sizeof ( * xrtcdev ) , GFP_KERNEL ) ;
if ( ! xrtcdev )
return - ENOMEM ;
platform_set_drvdata ( pdev , xrtcdev ) ;
2019-03-04 00:24:44 +03:00
xrtcdev - > rtc = devm_rtc_allocate_device ( & pdev - > dev ) ;
if ( IS_ERR ( xrtcdev - > rtc ) )
return PTR_ERR ( xrtcdev - > rtc ) ;
xrtcdev - > rtc - > ops = & xlnx_rtc_ops ;
2019-03-04 00:38:34 +03:00
xrtcdev - > rtc - > range_max = U32_MAX ;
2019-03-04 00:24:44 +03:00
2019-10-06 13:29:20 +03:00
xrtcdev - > reg_base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2015-08-19 12:53:22 +03:00
if ( IS_ERR ( xrtcdev - > reg_base ) )
return PTR_ERR ( xrtcdev - > reg_base ) ;
xrtcdev - > alarm_irq = platform_get_irq_byname ( pdev , " alarm " ) ;
2019-07-30 21:15:39 +03:00
if ( xrtcdev - > alarm_irq < 0 )
2015-08-19 12:53:22 +03:00
return xrtcdev - > alarm_irq ;
ret = devm_request_irq ( & pdev - > dev , xrtcdev - > alarm_irq ,
xlnx_rtc_interrupt , 0 ,
dev_name ( & pdev - > dev ) , xrtcdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " request irq failed \n " ) ;
return ret ;
}
xrtcdev - > sec_irq = platform_get_irq_byname ( pdev , " sec " ) ;
2019-07-30 21:15:39 +03:00
if ( xrtcdev - > sec_irq < 0 )
2015-08-19 12:53:22 +03:00
return xrtcdev - > sec_irq ;
ret = devm_request_irq ( & pdev - > dev , xrtcdev - > sec_irq ,
xlnx_rtc_interrupt , 0 ,
dev_name ( & pdev - > dev ) , xrtcdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " request irq failed \n " ) ;
return ret ;
}
2022-06-26 10:08:17 +03:00
/* Getting the rtc_clk info */
xrtcdev - > rtc_clk = devm_clk_get_optional ( & pdev - > dev , " rtc_clk " ) ;
if ( IS_ERR ( xrtcdev - > rtc_clk ) ) {
if ( PTR_ERR ( xrtcdev - > rtc_clk ) ! = - EPROBE_DEFER )
dev_warn ( & pdev - > dev , " Device clock not found. \n " ) ;
}
xrtcdev - > freq = clk_get_rate ( xrtcdev - > rtc_clk ) ;
if ( ! xrtcdev - > freq ) {
ret = of_property_read_u32 ( pdev - > dev . of_node , " calibration " ,
& xrtcdev - > freq ) ;
if ( ret )
xrtcdev - > freq = RTC_CALIB_DEF ;
}
ret = readl ( xrtcdev - > reg_base + RTC_CALIB_RD ) ;
if ( ! ret )
writel ( xrtcdev - > freq , ( xrtcdev - > reg_base + RTC_CALIB_WR ) ) ;
2015-08-19 12:53:22 +03:00
2016-04-12 15:15:45 +03:00
xlnx_init_rtc ( xrtcdev ) ;
2015-08-19 12:53:22 +03:00
device_init_wakeup ( & pdev - > dev , 1 ) ;
2020-11-09 19:34:08 +03:00
return devm_rtc_register_device ( xrtcdev - > rtc ) ;
2015-08-19 12:53:22 +03:00
}
2023-03-04 16:30:28 +03:00
static void xlnx_rtc_remove ( struct platform_device * pdev )
2015-08-19 12:53:22 +03:00
{
xlnx_rtc_alarm_irq_enable ( & pdev - > dev , 0 ) ;
device_init_wakeup ( & pdev - > dev , 0 ) ;
}
static int __maybe_unused xlnx_rtc_suspend ( struct device * dev )
{
2018-04-19 17:06:14 +03:00
struct xlnx_rtc_dev * xrtcdev = dev_get_drvdata ( dev ) ;
2015-08-19 12:53:22 +03:00
2018-04-19 17:06:14 +03:00
if ( device_may_wakeup ( dev ) )
2015-08-19 12:53:22 +03:00
enable_irq_wake ( xrtcdev - > alarm_irq ) ;
else
xlnx_rtc_alarm_irq_enable ( dev , 0 ) ;
return 0 ;
}
static int __maybe_unused xlnx_rtc_resume ( struct device * dev )
{
2018-04-19 17:06:14 +03:00
struct xlnx_rtc_dev * xrtcdev = dev_get_drvdata ( dev ) ;
2015-08-19 12:53:22 +03:00
2018-04-19 17:06:14 +03:00
if ( device_may_wakeup ( dev ) )
2015-08-19 12:53:22 +03:00
disable_irq_wake ( xrtcdev - > alarm_irq ) ;
else
xlnx_rtc_alarm_irq_enable ( dev , 1 ) ;
return 0 ;
}
static SIMPLE_DEV_PM_OPS ( xlnx_rtc_pm_ops , xlnx_rtc_suspend , xlnx_rtc_resume ) ;
static const struct of_device_id xlnx_rtc_of_match [ ] = {
{ . compatible = " xlnx,zynqmp-rtc " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , xlnx_rtc_of_match ) ;
static struct platform_driver xlnx_rtc_driver = {
. probe = xlnx_rtc_probe ,
2023-03-04 16:30:28 +03:00
. remove_new = xlnx_rtc_remove ,
2015-08-19 12:53:22 +03:00
. driver = {
. name = KBUILD_MODNAME ,
. pm = & xlnx_rtc_pm_ops ,
. of_match_table = xlnx_rtc_of_match ,
} ,
} ;
module_platform_driver ( xlnx_rtc_driver ) ;
MODULE_DESCRIPTION ( " Xilinx Zynq MPSoC RTC driver " ) ;
MODULE_AUTHOR ( " Xilinx Inc. " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;