2018-12-20 11:56:16 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP .
*/
2019-04-09 08:00:07 +03:00
# include <dt-bindings/firmware/imx/rsrc.h>
2019-01-22 06:29:24 +03:00
# include <linux/arm-smccc.h>
2018-12-20 11:56:16 +03:00
# include <linux/firmware/imx/sci.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/rtc.h>
# define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 9
2019-04-09 08:00:07 +03:00
# define IMX_SC_TIMER_FUNC_SET_RTC_ALARM 8
2018-12-20 11:56:16 +03:00
# define IMX_SC_TIMER_FUNC_SET_RTC_TIME 6
2019-01-22 06:29:24 +03:00
# define IMX_SIP_SRTC 0xC2000002
# define IMX_SIP_SRTC_SET_TIME 0x0
2019-04-09 08:00:07 +03:00
# define SC_IRQ_GROUP_RTC 2
# define SC_IRQ_RTC 1
2018-12-20 11:56:16 +03:00
static struct imx_sc_ipc * rtc_ipc_handle ;
static struct rtc_device * imx_sc_rtc ;
struct imx_sc_msg_timer_get_rtc_time {
struct imx_sc_rpc_msg hdr ;
u32 time ;
} __packed ;
2019-04-09 08:00:07 +03:00
struct imx_sc_msg_timer_rtc_set_alarm {
struct imx_sc_rpc_msg hdr ;
u16 year ;
u8 mon ;
u8 day ;
u8 hour ;
u8 min ;
u8 sec ;
2020-02-20 19:29:38 +03:00
} __packed __aligned ( 4 ) ;
2019-04-09 08:00:07 +03:00
2018-12-20 11:56:16 +03:00
static int imx_sc_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
struct imx_sc_msg_timer_get_rtc_time msg ;
struct imx_sc_rpc_msg * hdr = & msg . hdr ;
int ret ;
hdr - > ver = IMX_SC_RPC_VERSION ;
hdr - > svc = IMX_SC_RPC_SVC_TIMER ;
hdr - > func = IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 ;
hdr - > size = 1 ;
ret = imx_scu_call_rpc ( rtc_ipc_handle , & msg , true ) ;
if ( ret ) {
dev_err ( dev , " read rtc time failed, ret %d \n " , ret ) ;
return ret ;
}
2019-03-04 00:12:38 +03:00
rtc_time64_to_tm ( msg . time , tm ) ;
2018-12-20 11:56:16 +03:00
return 0 ;
}
2019-01-22 06:29:24 +03:00
static int imx_sc_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
struct arm_smccc_res res ;
/* pack 2 time parameters into 1 register, 16 bits for each */
arm_smccc_smc ( IMX_SIP_SRTC , IMX_SIP_SRTC_SET_TIME ,
( ( tm - > tm_year + 1900 ) < < 16 ) | ( tm - > tm_mon + 1 ) ,
( tm - > tm_mday < < 16 ) | tm - > tm_hour ,
( tm - > tm_min < < 16 ) | tm - > tm_sec ,
0 , 0 , 0 , & res ) ;
return res . a0 ;
}
2019-04-09 08:00:07 +03:00
static int imx_sc_rtc_alarm_irq_enable ( struct device * dev , unsigned int enable )
{
return imx_scu_irq_group_enable ( SC_IRQ_GROUP_RTC , SC_IRQ_RTC , enable ) ;
}
static int imx_sc_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
/*
* SCU firmware does NOT provide read alarm API , but . read_alarm
* callback is required by RTC framework to support alarm function ,
* so just return here .
*/
return 0 ;
}
static int imx_sc_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct imx_sc_msg_timer_rtc_set_alarm msg ;
struct imx_sc_rpc_msg * hdr = & msg . hdr ;
int ret ;
struct rtc_time * alrm_tm = & alrm - > time ;
hdr - > ver = IMX_SC_RPC_VERSION ;
hdr - > svc = IMX_SC_RPC_SVC_TIMER ;
hdr - > func = IMX_SC_TIMER_FUNC_SET_RTC_ALARM ;
hdr - > size = 3 ;
msg . year = alrm_tm - > tm_year + 1900 ;
msg . mon = alrm_tm - > tm_mon + 1 ;
msg . day = alrm_tm - > tm_mday ;
msg . hour = alrm_tm - > tm_hour ;
msg . min = alrm_tm - > tm_min ;
msg . sec = alrm_tm - > tm_sec ;
ret = imx_scu_call_rpc ( rtc_ipc_handle , & msg , true ) ;
if ( ret ) {
dev_err ( dev , " set rtc alarm failed, ret %d \n " , ret ) ;
return ret ;
}
ret = imx_sc_rtc_alarm_irq_enable ( dev , alrm - > enabled ) ;
if ( ret ) {
dev_err ( dev , " enable rtc alarm failed, ret %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
2018-12-20 11:56:16 +03:00
static const struct rtc_class_ops imx_sc_rtc_ops = {
. read_time = imx_sc_rtc_read_time ,
2019-01-22 06:29:24 +03:00
. set_time = imx_sc_rtc_set_time ,
2019-04-09 08:00:07 +03:00
. read_alarm = imx_sc_rtc_read_alarm ,
. set_alarm = imx_sc_rtc_set_alarm ,
. alarm_irq_enable = imx_sc_rtc_alarm_irq_enable ,
} ;
static int imx_sc_rtc_alarm_notify ( struct notifier_block * nb ,
unsigned long event , void * group )
{
/* ignore non-rtc irq */
if ( ! ( ( event & SC_IRQ_RTC ) & & ( * ( u8 * ) group = = SC_IRQ_GROUP_RTC ) ) )
return 0 ;
rtc_update_irq ( imx_sc_rtc , 1 , RTC_IRQF | RTC_AF ) ;
return 0 ;
}
static struct notifier_block imx_sc_rtc_alarm_sc_notifier = {
. notifier_call = imx_sc_rtc_alarm_notify ,
2018-12-20 11:56:16 +03:00
} ;
static int imx_sc_rtc_probe ( struct platform_device * pdev )
{
int ret ;
ret = imx_scu_get_handle ( & rtc_ipc_handle ) ;
if ( ret )
return ret ;
2019-04-09 08:00:07 +03:00
device_init_wakeup ( & pdev - > dev , true ) ;
2018-12-20 11:56:16 +03:00
imx_sc_rtc = devm_rtc_allocate_device ( & pdev - > dev ) ;
if ( IS_ERR ( imx_sc_rtc ) )
return PTR_ERR ( imx_sc_rtc ) ;
imx_sc_rtc - > ops = & imx_sc_rtc_ops ;
imx_sc_rtc - > range_min = 0 ;
imx_sc_rtc - > range_max = U32_MAX ;
ret = rtc_register_device ( imx_sc_rtc ) ;
2019-08-19 01:00:41 +03:00
if ( ret )
2018-12-20 11:56:16 +03:00
return ret ;
2019-04-09 08:00:07 +03:00
imx_scu_irq_register_notifier ( & imx_sc_rtc_alarm_sc_notifier ) ;
2018-12-20 11:56:16 +03:00
return 0 ;
}
static const struct of_device_id imx_sc_dt_ids [ ] = {
{ . compatible = " fsl,imx8qxp-sc-rtc " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , imx_sc_dt_ids ) ;
static struct platform_driver imx_sc_rtc_driver = {
. driver = {
. name = " imx-sc-rtc " ,
. of_match_table = imx_sc_dt_ids ,
} ,
. probe = imx_sc_rtc_probe ,
} ;
module_platform_driver ( imx_sc_rtc_driver ) ;
MODULE_AUTHOR ( " Anson Huang <Anson.Huang@nxp.com> " ) ;
MODULE_DESCRIPTION ( " NXP i.MX System Controller RTC Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;