2019-08-13 06:01:56 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale FlexTimer Module ( FTM ) alarm device driver .
*
* Copyright 2014 Freescale Semiconductor , Inc .
2020-08-18 09:36:09 +03:00
* Copyright 2019 - 2020 NXP
2019-08-13 06:01:56 +03:00
*
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
# include <linux/platform_device.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/module.h>
# include <linux/fsl/ftm.h>
# include <linux/rtc.h>
# include <linux/time.h>
2020-03-18 05:53:54 +03:00
# include <linux/acpi.h>
2020-06-01 10:19:14 +03:00
# include <linux/pm_wakeirq.h>
2019-08-13 06:01:56 +03:00
# define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_MASK_SHIFT)
/*
* Select Fixed frequency clock ( 32 KHz ) as clock source
* of FlexTimer Module
*/
# define FTM_SC_CLKS_FIXED_FREQ 0x02
# define FIXED_FREQ_CLK 32000
/* Select 128 (2^7) as divider factor */
# define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK)
/* Maximum counter value in FlexTimer's CNT registers */
# define MAX_COUNT_VAL 0xffff
struct ftm_rtc {
struct rtc_device * rtc_dev ;
void __iomem * base ;
bool big_endian ;
u32 alarm_freq ;
} ;
static inline u32 rtc_readl ( struct ftm_rtc * dev , u32 reg )
{
if ( dev - > big_endian )
return ioread32be ( dev - > base + reg ) ;
else
return ioread32 ( dev - > base + reg ) ;
}
static inline void rtc_writel ( struct ftm_rtc * dev , u32 reg , u32 val )
{
if ( dev - > big_endian )
iowrite32be ( val , dev - > base + reg ) ;
else
iowrite32 ( val , dev - > base + reg ) ;
}
static inline void ftm_counter_enable ( struct ftm_rtc * rtc )
{
u32 val ;
/* select and enable counter clock source */
val = rtc_readl ( rtc , FTM_SC ) ;
val & = ~ ( FTM_SC_PS_MASK | FTM_SC_CLK_MASK ) ;
val | = ( FTM_SC_PS_MASK | FTM_SC_CLK ( FTM_SC_CLKS_FIXED_FREQ ) ) ;
rtc_writel ( rtc , FTM_SC , val ) ;
}
static inline void ftm_counter_disable ( struct ftm_rtc * rtc )
{
u32 val ;
/* disable counter clock source */
val = rtc_readl ( rtc , FTM_SC ) ;
val & = ~ ( FTM_SC_PS_MASK | FTM_SC_CLK_MASK ) ;
rtc_writel ( rtc , FTM_SC , val ) ;
}
static inline void ftm_irq_acknowledge ( struct ftm_rtc * rtc )
{
unsigned int timeout = 100 ;
/*
* Fix errata A - 00772 8 for flextimer
* If the FTM counter reaches the FTM_MOD value between
* the reading of the TOF bit and the writing of 0 to
* the TOF bit , the process of clearing the TOF bit
* does not work as expected when FTMx_CONF [ NUMTOF ] ! = 0
* and the current TOF count is less than FTMx_CONF [ NUMTOF ] .
* If the above condition is met , the TOF bit remains set .
* If the TOF interrupt is enabled ( FTMx_SC [ TOIE ] = 1 ) , the
* TOF interrupt also remains asserted .
*
* Above is the errata discription
*
* In one word : software clearing TOF bit not works when
* FTMx_CONF [ NUMTOF ] was seted as nonzero and FTM counter
* reaches the FTM_MOD value .
*
* The workaround is clearing TOF bit until it works
* ( FTM counter doesn ' t always reache the FTM_MOD anyway ) ,
* which may cost some cycles .
*/
while ( ( FTM_SC_TOF & rtc_readl ( rtc , FTM_SC ) ) & & timeout - - )
rtc_writel ( rtc , FTM_SC , rtc_readl ( rtc , FTM_SC ) & ( ~ FTM_SC_TOF ) ) ;
}
static inline void ftm_irq_enable ( struct ftm_rtc * rtc )
{
u32 val ;
val = rtc_readl ( rtc , FTM_SC ) ;
val | = FTM_SC_TOIE ;
rtc_writel ( rtc , FTM_SC , val ) ;
}
static inline void ftm_irq_disable ( struct ftm_rtc * rtc )
{
u32 val ;
val = rtc_readl ( rtc , FTM_SC ) ;
val & = ~ FTM_SC_TOIE ;
rtc_writel ( rtc , FTM_SC , val ) ;
}
static inline void ftm_reset_counter ( struct ftm_rtc * rtc )
{
/*
* The CNT register contains the FTM counter value .
* Reset clears the CNT register . Writing any value to COUNT
* updates the counter with its initial value , CNTIN .
*/
rtc_writel ( rtc , FTM_CNT , 0x00 ) ;
}
static void ftm_clean_alarm ( struct ftm_rtc * rtc )
{
ftm_counter_disable ( rtc ) ;
rtc_writel ( rtc , FTM_CNTIN , 0x00 ) ;
rtc_writel ( rtc , FTM_MOD , ~ 0U ) ;
ftm_reset_counter ( rtc ) ;
}
static irqreturn_t ftm_rtc_alarm_interrupt ( int irq , void * dev )
{
struct ftm_rtc * rtc = dev ;
2020-03-27 11:44:57 +03:00
rtc_update_irq ( rtc - > rtc_dev , 1 , RTC_IRQF | RTC_AF ) ;
2019-08-13 06:01:56 +03:00
ftm_irq_acknowledge ( rtc ) ;
ftm_irq_disable ( rtc ) ;
ftm_clean_alarm ( rtc ) ;
return IRQ_HANDLED ;
}
static int ftm_rtc_alarm_irq_enable ( struct device * dev ,
unsigned int enabled )
{
struct ftm_rtc * rtc = dev_get_drvdata ( dev ) ;
if ( enabled )
ftm_irq_enable ( rtc ) ;
else
ftm_irq_disable ( rtc ) ;
return 0 ;
}
/*
* Note :
* The function is not really getting time from the RTC
* since FlexTimer is not a RTC device , but we need to
* get time to setup alarm , so we are using system time
* for now .
*/
static int ftm_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
2019-10-16 23:12:22 +03:00
rtc_time64_to_tm ( ktime_get_real_seconds ( ) , tm ) ;
2019-08-13 06:01:56 +03:00
return 0 ;
}
static int ftm_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
return 0 ;
}
/*
* 1. Select fixed frequency clock ( 32 KHz ) as clock source ;
* 2. Select 128 ( 2 ^ 7 ) as divider factor ;
* So clock is 250 Hz ( 32 KHz / 128 ) .
*
* 3. FlexTimer ' s CNT register is a 32 bit register ,
* but the register ' s 16 bit as counter value , it ' s other 16 bit
* is reserved . So minimum counter value is 0x0 , maximum counter
* value is 0xffff .
* So max alarm value is 262 ( 65536 / 250 ) seconds
*/
static int ftm_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alm )
{
2019-10-16 23:12:23 +03:00
time64_t alm_time ;
2019-10-16 23:12:22 +03:00
unsigned long long cycle ;
2019-08-13 06:01:56 +03:00
struct ftm_rtc * rtc = dev_get_drvdata ( dev ) ;
2019-10-16 23:12:22 +03:00
alm_time = rtc_tm_to_time64 ( & alm - > time ) ;
2019-08-13 06:01:56 +03:00
ftm_clean_alarm ( rtc ) ;
2019-10-16 23:12:23 +03:00
cycle = ( alm_time - ktime_get_real_seconds ( ) ) * rtc - > alarm_freq ;
2019-08-13 06:01:56 +03:00
if ( cycle > MAX_COUNT_VAL ) {
pr_err ( " Out of alarm range {0~262} seconds. \n " ) ;
return - ERANGE ;
}
ftm_irq_disable ( rtc ) ;
/*
* The counter increments until the value of MOD is reached ,
* at which point the counter is reloaded with the value of CNTIN .
* The TOF ( the overflow flag ) bit is set when the FTM counter
* changes from MOD to CNTIN . So we should using the cycle - 1.
*/
rtc_writel ( rtc , FTM_MOD , cycle - 1 ) ;
ftm_counter_enable ( rtc ) ;
ftm_irq_enable ( rtc ) ;
return 0 ;
}
static const struct rtc_class_ops ftm_rtc_ops = {
. read_time = ftm_rtc_read_time ,
. read_alarm = ftm_rtc_read_alarm ,
. set_alarm = ftm_rtc_set_alarm ,
. alarm_irq_enable = ftm_rtc_alarm_irq_enable ,
} ;
static int ftm_rtc_probe ( struct platform_device * pdev )
{
int irq ;
int ret ;
struct ftm_rtc * rtc ;
rtc = devm_kzalloc ( & pdev - > dev , sizeof ( * rtc ) , GFP_KERNEL ) ;
if ( unlikely ( ! rtc ) ) {
dev_err ( & pdev - > dev , " cannot alloc memory for rtc \n " ) ;
return - ENOMEM ;
}
platform_set_drvdata ( pdev , rtc ) ;
rtc - > rtc_dev = devm_rtc_allocate_device ( & pdev - > dev ) ;
if ( IS_ERR ( rtc - > rtc_dev ) )
return PTR_ERR ( rtc - > rtc_dev ) ;
2019-09-21 12:49:01 +03:00
rtc - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2019-08-13 06:01:56 +03:00
if ( IS_ERR ( rtc - > base ) ) {
dev_err ( & pdev - > dev , " cannot ioremap resource for rtc \n " ) ;
return PTR_ERR ( rtc - > base ) ;
}
2020-03-18 05:53:54 +03:00
irq = platform_get_irq ( pdev , 0 ) ;
2020-04-05 18:30:15 +03:00
if ( irq < 0 )
2020-03-18 05:53:54 +03:00
return irq ;
2019-08-13 06:01:56 +03:00
ret = devm_request_irq ( & pdev - > dev , irq , ftm_rtc_alarm_interrupt ,
2020-06-01 10:19:14 +03:00
0 , dev_name ( & pdev - > dev ) , rtc ) ;
2019-08-13 06:01:56 +03:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to request irq \n " ) ;
return ret ;
}
2020-03-18 05:53:54 +03:00
rtc - > big_endian =
device_property_read_bool ( & pdev - > dev , " big-endian " ) ;
2019-08-13 06:01:56 +03:00
rtc - > alarm_freq = ( u32 ) FIXED_FREQ_CLK / ( u32 ) MAX_FREQ_DIV ;
rtc - > rtc_dev - > ops = & ftm_rtc_ops ;
device_init_wakeup ( & pdev - > dev , true ) ;
2020-06-01 10:19:14 +03:00
ret = dev_pm_set_wake_irq ( & pdev - > dev , irq ) ;
if ( ret )
dev_err ( & pdev - > dev , " failed to enable irq wake \n " ) ;
2019-08-13 06:01:56 +03:00
2020-11-09 19:34:08 +03:00
ret = devm_rtc_register_device ( rtc - > rtc_dev ) ;
2019-08-13 06:01:56 +03:00
if ( ret ) {
dev_err ( & pdev - > dev , " can't register rtc device \n " ) ;
return ret ;
}
return 0 ;
}
static const struct of_device_id ftm_rtc_match [ ] = {
{ . compatible = " fsl,ls1012a-ftm-alarm " , } ,
{ . compatible = " fsl,ls1021a-ftm-alarm " , } ,
{ . compatible = " fsl,ls1028a-ftm-alarm " , } ,
{ . compatible = " fsl,ls1043a-ftm-alarm " , } ,
{ . compatible = " fsl,ls1046a-ftm-alarm " , } ,
{ . compatible = " fsl,ls1088a-ftm-alarm " , } ,
{ . compatible = " fsl,ls208xa-ftm-alarm " , } ,
{ . compatible = " fsl,lx2160a-ftm-alarm " , } ,
{ } ,
} ;
2021-04-14 11:40:06 +03:00
MODULE_DEVICE_TABLE ( of , ftm_rtc_match ) ;
2019-08-13 06:01:56 +03:00
2020-03-18 05:53:54 +03:00
static const struct acpi_device_id ftm_imx_acpi_ids [ ] = {
2020-08-18 09:36:09 +03:00
{ " NXP0014 " , } ,
2020-03-18 05:53:54 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , ftm_imx_acpi_ids ) ;
2019-08-13 06:01:56 +03:00
static struct platform_driver ftm_rtc_driver = {
. probe = ftm_rtc_probe ,
. driver = {
. name = " ftm-alarm " ,
. of_match_table = ftm_rtc_match ,
2020-03-18 05:53:54 +03:00
. acpi_match_table = ACPI_PTR ( ftm_imx_acpi_ids ) ,
2019-08-13 06:01:56 +03:00
} ,
} ;
static int __init ftm_alarm_init ( void )
{
return platform_driver_register ( & ftm_rtc_driver ) ;
}
device_initcall ( ftm_alarm_init ) ;
MODULE_DESCRIPTION ( " NXP/Freescale FlexTimer alarm driver " ) ;
MODULE_AUTHOR ( " Biwen Li <biwen.li@nxp.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;