18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2018 NXP. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <dt-bindings/firmware/imx/rsrc.h> 78c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 88c2ecf20Sopenharmony_ci#include <linux/firmware/imx/sci.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/rtc.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 9 158c2ecf20Sopenharmony_ci#define IMX_SC_TIMER_FUNC_SET_RTC_ALARM 8 168c2ecf20Sopenharmony_ci#define IMX_SC_TIMER_FUNC_SET_RTC_TIME 6 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define IMX_SIP_SRTC 0xC2000002 198c2ecf20Sopenharmony_ci#define IMX_SIP_SRTC_SET_TIME 0x0 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define SC_IRQ_GROUP_RTC 2 228c2ecf20Sopenharmony_ci#define SC_IRQ_RTC 1 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic struct imx_sc_ipc *rtc_ipc_handle; 258c2ecf20Sopenharmony_cistatic struct rtc_device *imx_sc_rtc; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct imx_sc_msg_timer_get_rtc_time { 288c2ecf20Sopenharmony_ci struct imx_sc_rpc_msg hdr; 298c2ecf20Sopenharmony_ci u32 time; 308c2ecf20Sopenharmony_ci} __packed; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct imx_sc_msg_timer_rtc_set_alarm { 338c2ecf20Sopenharmony_ci struct imx_sc_rpc_msg hdr; 348c2ecf20Sopenharmony_ci u16 year; 358c2ecf20Sopenharmony_ci u8 mon; 368c2ecf20Sopenharmony_ci u8 day; 378c2ecf20Sopenharmony_ci u8 hour; 388c2ecf20Sopenharmony_ci u8 min; 398c2ecf20Sopenharmony_ci u8 sec; 408c2ecf20Sopenharmony_ci} __packed __aligned(4); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct imx_sc_msg_timer_get_rtc_time msg; 458c2ecf20Sopenharmony_ci struct imx_sc_rpc_msg *hdr = &msg.hdr; 468c2ecf20Sopenharmony_ci int ret; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci hdr->ver = IMX_SC_RPC_VERSION; 498c2ecf20Sopenharmony_ci hdr->svc = IMX_SC_RPC_SVC_TIMER; 508c2ecf20Sopenharmony_ci hdr->func = IMX_SC_TIMER_FUNC_GET_RTC_SEC1970; 518c2ecf20Sopenharmony_ci hdr->size = 1; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true); 548c2ecf20Sopenharmony_ci if (ret) { 558c2ecf20Sopenharmony_ci dev_err(dev, "read rtc time failed, ret %d\n", ret); 568c2ecf20Sopenharmony_ci return ret; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci rtc_time64_to_tm(msg.time, tm); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int imx_sc_rtc_set_time(struct device *dev, struct rtc_time *tm) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct arm_smccc_res res; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* pack 2 time parameters into 1 register, 16 bits for each */ 698c2ecf20Sopenharmony_ci arm_smccc_smc(IMX_SIP_SRTC, IMX_SIP_SRTC_SET_TIME, 708c2ecf20Sopenharmony_ci ((tm->tm_year + 1900) << 16) | (tm->tm_mon + 1), 718c2ecf20Sopenharmony_ci (tm->tm_mday << 16) | tm->tm_hour, 728c2ecf20Sopenharmony_ci (tm->tm_min << 16) | tm->tm_sec, 738c2ecf20Sopenharmony_ci 0, 0, 0, &res); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return res.a0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int imx_sc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci return imx_scu_irq_group_enable(SC_IRQ_GROUP_RTC, SC_IRQ_RTC, enable); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int imx_sc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * SCU firmware does NOT provide read alarm API, but .read_alarm 878c2ecf20Sopenharmony_ci * callback is required by RTC framework to support alarm function, 888c2ecf20Sopenharmony_ci * so just return here. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int imx_sc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct imx_sc_msg_timer_rtc_set_alarm msg; 968c2ecf20Sopenharmony_ci struct imx_sc_rpc_msg *hdr = &msg.hdr; 978c2ecf20Sopenharmony_ci int ret; 988c2ecf20Sopenharmony_ci struct rtc_time *alrm_tm = &alrm->time; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci hdr->ver = IMX_SC_RPC_VERSION; 1018c2ecf20Sopenharmony_ci hdr->svc = IMX_SC_RPC_SVC_TIMER; 1028c2ecf20Sopenharmony_ci hdr->func = IMX_SC_TIMER_FUNC_SET_RTC_ALARM; 1038c2ecf20Sopenharmony_ci hdr->size = 3; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci msg.year = alrm_tm->tm_year + 1900; 1068c2ecf20Sopenharmony_ci msg.mon = alrm_tm->tm_mon + 1; 1078c2ecf20Sopenharmony_ci msg.day = alrm_tm->tm_mday; 1088c2ecf20Sopenharmony_ci msg.hour = alrm_tm->tm_hour; 1098c2ecf20Sopenharmony_ci msg.min = alrm_tm->tm_min; 1108c2ecf20Sopenharmony_ci msg.sec = alrm_tm->tm_sec; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true); 1138c2ecf20Sopenharmony_ci if (ret) { 1148c2ecf20Sopenharmony_ci dev_err(dev, "set rtc alarm failed, ret %d\n", ret); 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = imx_sc_rtc_alarm_irq_enable(dev, alrm->enabled); 1198c2ecf20Sopenharmony_ci if (ret) { 1208c2ecf20Sopenharmony_ci dev_err(dev, "enable rtc alarm failed, ret %d\n", ret); 1218c2ecf20Sopenharmony_ci return ret; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic const struct rtc_class_ops imx_sc_rtc_ops = { 1288c2ecf20Sopenharmony_ci .read_time = imx_sc_rtc_read_time, 1298c2ecf20Sopenharmony_ci .set_time = imx_sc_rtc_set_time, 1308c2ecf20Sopenharmony_ci .read_alarm = imx_sc_rtc_read_alarm, 1318c2ecf20Sopenharmony_ci .set_alarm = imx_sc_rtc_set_alarm, 1328c2ecf20Sopenharmony_ci .alarm_irq_enable = imx_sc_rtc_alarm_irq_enable, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int imx_sc_rtc_alarm_notify(struct notifier_block *nb, 1368c2ecf20Sopenharmony_ci unsigned long event, void *group) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci /* ignore non-rtc irq */ 1398c2ecf20Sopenharmony_ci if (!((event & SC_IRQ_RTC) && (*(u8 *)group == SC_IRQ_GROUP_RTC))) 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci rtc_update_irq(imx_sc_rtc, 1, RTC_IRQF | RTC_AF); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic struct notifier_block imx_sc_rtc_alarm_sc_notifier = { 1488c2ecf20Sopenharmony_ci .notifier_call = imx_sc_rtc_alarm_notify, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int imx_sc_rtc_probe(struct platform_device *pdev) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci int ret; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ret = imx_scu_get_handle(&rtc_ipc_handle); 1568c2ecf20Sopenharmony_ci if (ret) 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, true); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci imx_sc_rtc = devm_rtc_allocate_device(&pdev->dev); 1628c2ecf20Sopenharmony_ci if (IS_ERR(imx_sc_rtc)) 1638c2ecf20Sopenharmony_ci return PTR_ERR(imx_sc_rtc); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci imx_sc_rtc->ops = &imx_sc_rtc_ops; 1668c2ecf20Sopenharmony_ci imx_sc_rtc->range_min = 0; 1678c2ecf20Sopenharmony_ci imx_sc_rtc->range_max = U32_MAX; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ret = rtc_register_device(imx_sc_rtc); 1708c2ecf20Sopenharmony_ci if (ret) 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci imx_scu_irq_register_notifier(&imx_sc_rtc_alarm_sc_notifier); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const struct of_device_id imx_sc_dt_ids[] = { 1798c2ecf20Sopenharmony_ci { .compatible = "fsl,imx8qxp-sc-rtc", }, 1808c2ecf20Sopenharmony_ci {} 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_sc_dt_ids); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct platform_driver imx_sc_rtc_driver = { 1858c2ecf20Sopenharmony_ci .driver = { 1868c2ecf20Sopenharmony_ci .name = "imx-sc-rtc", 1878c2ecf20Sopenharmony_ci .of_match_table = imx_sc_dt_ids, 1888c2ecf20Sopenharmony_ci }, 1898c2ecf20Sopenharmony_ci .probe = imx_sc_rtc_probe, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_cimodule_platform_driver(imx_sc_rtc_driver); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); 1948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NXP i.MX System Controller RTC Driver"); 1958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 196