1e01b5781SAnson Huang // SPDX-License-Identifier: GPL-2.0+ 2e01b5781SAnson Huang /* 3e01b5781SAnson Huang * Copyright 2018 NXP. 4e01b5781SAnson Huang */ 5e01b5781SAnson Huang 6a3094fc1SAnson Huang #include <dt-bindings/firmware/imx/rsrc.h> 713a929f3SAnson Huang #include <linux/arm-smccc.h> 8e01b5781SAnson Huang #include <linux/firmware/imx/sci.h> 9e01b5781SAnson Huang #include <linux/module.h> 10e01b5781SAnson Huang #include <linux/of.h> 11e01b5781SAnson Huang #include <linux/platform_device.h> 12e01b5781SAnson Huang #include <linux/rtc.h> 13e01b5781SAnson Huang 14e01b5781SAnson Huang #define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 9 15a3094fc1SAnson Huang #define IMX_SC_TIMER_FUNC_SET_RTC_ALARM 8 16e01b5781SAnson Huang #define IMX_SC_TIMER_FUNC_SET_RTC_TIME 6 17e01b5781SAnson Huang 1813a929f3SAnson Huang #define IMX_SIP_SRTC 0xC2000002 1913a929f3SAnson Huang #define IMX_SIP_SRTC_SET_TIME 0x0 2013a929f3SAnson Huang 21a3094fc1SAnson Huang #define SC_IRQ_GROUP_RTC 2 22a3094fc1SAnson Huang #define SC_IRQ_RTC 1 23a3094fc1SAnson Huang 24e01b5781SAnson Huang static struct imx_sc_ipc *rtc_ipc_handle; 25e01b5781SAnson Huang static struct rtc_device *imx_sc_rtc; 26e01b5781SAnson Huang 27e01b5781SAnson Huang struct imx_sc_msg_timer_get_rtc_time { 28e01b5781SAnson Huang struct imx_sc_rpc_msg hdr; 29e01b5781SAnson Huang u32 time; 30e01b5781SAnson Huang } __packed; 31e01b5781SAnson Huang 32a3094fc1SAnson Huang struct imx_sc_msg_timer_rtc_set_alarm { 33a3094fc1SAnson Huang struct imx_sc_rpc_msg hdr; 34a3094fc1SAnson Huang u16 year; 35a3094fc1SAnson Huang u8 mon; 36a3094fc1SAnson Huang u8 day; 37a3094fc1SAnson Huang u8 hour; 38a3094fc1SAnson Huang u8 min; 39a3094fc1SAnson Huang u8 sec; 40a29de865SLeonard Crestez } __packed __aligned(4); 41a3094fc1SAnson Huang 42e01b5781SAnson Huang static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm) 43e01b5781SAnson Huang { 44e01b5781SAnson Huang struct imx_sc_msg_timer_get_rtc_time msg; 45e01b5781SAnson Huang struct imx_sc_rpc_msg *hdr = &msg.hdr; 46e01b5781SAnson Huang int ret; 47e01b5781SAnson Huang 48e01b5781SAnson Huang hdr->ver = IMX_SC_RPC_VERSION; 49e01b5781SAnson Huang hdr->svc = IMX_SC_RPC_SVC_TIMER; 50e01b5781SAnson Huang hdr->func = IMX_SC_TIMER_FUNC_GET_RTC_SEC1970; 51e01b5781SAnson Huang hdr->size = 1; 52e01b5781SAnson Huang 53e01b5781SAnson Huang ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true); 54e01b5781SAnson Huang if (ret) { 55e01b5781SAnson Huang dev_err(dev, "read rtc time failed, ret %d\n", ret); 56e01b5781SAnson Huang return ret; 57e01b5781SAnson Huang } 58e01b5781SAnson Huang 5930adde6bSAlexandre Belloni rtc_time64_to_tm(msg.time, tm); 60e01b5781SAnson Huang 61e01b5781SAnson Huang return 0; 62e01b5781SAnson Huang } 63e01b5781SAnson Huang 6413a929f3SAnson Huang static int imx_sc_rtc_set_time(struct device *dev, struct rtc_time *tm) 6513a929f3SAnson Huang { 6613a929f3SAnson Huang struct arm_smccc_res res; 6713a929f3SAnson Huang 6813a929f3SAnson Huang /* pack 2 time parameters into 1 register, 16 bits for each */ 6913a929f3SAnson Huang arm_smccc_smc(IMX_SIP_SRTC, IMX_SIP_SRTC_SET_TIME, 7013a929f3SAnson Huang ((tm->tm_year + 1900) << 16) | (tm->tm_mon + 1), 7113a929f3SAnson Huang (tm->tm_mday << 16) | tm->tm_hour, 7213a929f3SAnson Huang (tm->tm_min << 16) | tm->tm_sec, 7313a929f3SAnson Huang 0, 0, 0, &res); 7413a929f3SAnson Huang 7513a929f3SAnson Huang return res.a0; 7613a929f3SAnson Huang } 7713a929f3SAnson Huang 78a3094fc1SAnson Huang static int imx_sc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 79a3094fc1SAnson Huang { 80a3094fc1SAnson Huang return imx_scu_irq_group_enable(SC_IRQ_GROUP_RTC, SC_IRQ_RTC, enable); 81a3094fc1SAnson Huang } 82a3094fc1SAnson Huang 83a3094fc1SAnson Huang static int imx_sc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 84a3094fc1SAnson Huang { 85a3094fc1SAnson Huang /* 86a3094fc1SAnson Huang * SCU firmware does NOT provide read alarm API, but .read_alarm 87a3094fc1SAnson Huang * callback is required by RTC framework to support alarm function, 88a3094fc1SAnson Huang * so just return here. 89a3094fc1SAnson Huang */ 90a3094fc1SAnson Huang return 0; 91a3094fc1SAnson Huang } 92a3094fc1SAnson Huang 93a3094fc1SAnson Huang static int imx_sc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 94a3094fc1SAnson Huang { 95a3094fc1SAnson Huang struct imx_sc_msg_timer_rtc_set_alarm msg; 96a3094fc1SAnson Huang struct imx_sc_rpc_msg *hdr = &msg.hdr; 97a3094fc1SAnson Huang int ret; 98a3094fc1SAnson Huang struct rtc_time *alrm_tm = &alrm->time; 99a3094fc1SAnson Huang 100a3094fc1SAnson Huang hdr->ver = IMX_SC_RPC_VERSION; 101a3094fc1SAnson Huang hdr->svc = IMX_SC_RPC_SVC_TIMER; 102a3094fc1SAnson Huang hdr->func = IMX_SC_TIMER_FUNC_SET_RTC_ALARM; 103a3094fc1SAnson Huang hdr->size = 3; 104a3094fc1SAnson Huang 105a3094fc1SAnson Huang msg.year = alrm_tm->tm_year + 1900; 106a3094fc1SAnson Huang msg.mon = alrm_tm->tm_mon + 1; 107a3094fc1SAnson Huang msg.day = alrm_tm->tm_mday; 108a3094fc1SAnson Huang msg.hour = alrm_tm->tm_hour; 109a3094fc1SAnson Huang msg.min = alrm_tm->tm_min; 110a3094fc1SAnson Huang msg.sec = alrm_tm->tm_sec; 111a3094fc1SAnson Huang 112a3094fc1SAnson Huang ret = imx_scu_call_rpc(rtc_ipc_handle, &msg, true); 113a3094fc1SAnson Huang if (ret) { 114a3094fc1SAnson Huang dev_err(dev, "set rtc alarm failed, ret %d\n", ret); 115a3094fc1SAnson Huang return ret; 116a3094fc1SAnson Huang } 117a3094fc1SAnson Huang 118a3094fc1SAnson Huang ret = imx_sc_rtc_alarm_irq_enable(dev, alrm->enabled); 119a3094fc1SAnson Huang if (ret) { 120a3094fc1SAnson Huang dev_err(dev, "enable rtc alarm failed, ret %d\n", ret); 121a3094fc1SAnson Huang return ret; 122a3094fc1SAnson Huang } 123a3094fc1SAnson Huang 124a3094fc1SAnson Huang return 0; 125a3094fc1SAnson Huang } 126a3094fc1SAnson Huang 127e01b5781SAnson Huang static const struct rtc_class_ops imx_sc_rtc_ops = { 128e01b5781SAnson Huang .read_time = imx_sc_rtc_read_time, 12913a929f3SAnson Huang .set_time = imx_sc_rtc_set_time, 130a3094fc1SAnson Huang .read_alarm = imx_sc_rtc_read_alarm, 131a3094fc1SAnson Huang .set_alarm = imx_sc_rtc_set_alarm, 132a3094fc1SAnson Huang .alarm_irq_enable = imx_sc_rtc_alarm_irq_enable, 133a3094fc1SAnson Huang }; 134a3094fc1SAnson Huang 135a3094fc1SAnson Huang static int imx_sc_rtc_alarm_notify(struct notifier_block *nb, 136a3094fc1SAnson Huang unsigned long event, void *group) 137a3094fc1SAnson Huang { 138a3094fc1SAnson Huang /* ignore non-rtc irq */ 139a3094fc1SAnson Huang if (!((event & SC_IRQ_RTC) && (*(u8 *)group == SC_IRQ_GROUP_RTC))) 140a3094fc1SAnson Huang return 0; 141a3094fc1SAnson Huang 142a3094fc1SAnson Huang rtc_update_irq(imx_sc_rtc, 1, RTC_IRQF | RTC_AF); 143a3094fc1SAnson Huang 144a3094fc1SAnson Huang return 0; 145a3094fc1SAnson Huang } 146a3094fc1SAnson Huang 147a3094fc1SAnson Huang static struct notifier_block imx_sc_rtc_alarm_sc_notifier = { 148a3094fc1SAnson Huang .notifier_call = imx_sc_rtc_alarm_notify, 149e01b5781SAnson Huang }; 150e01b5781SAnson Huang 151e01b5781SAnson Huang static int imx_sc_rtc_probe(struct platform_device *pdev) 152e01b5781SAnson Huang { 153e01b5781SAnson Huang int ret; 154e01b5781SAnson Huang 155e01b5781SAnson Huang ret = imx_scu_get_handle(&rtc_ipc_handle); 156e01b5781SAnson Huang if (ret) 157e01b5781SAnson Huang return ret; 158e01b5781SAnson Huang 159a3094fc1SAnson Huang device_init_wakeup(&pdev->dev, true); 160a3094fc1SAnson Huang 161e01b5781SAnson Huang imx_sc_rtc = devm_rtc_allocate_device(&pdev->dev); 162e01b5781SAnson Huang if (IS_ERR(imx_sc_rtc)) 163e01b5781SAnson Huang return PTR_ERR(imx_sc_rtc); 164e01b5781SAnson Huang 165e01b5781SAnson Huang imx_sc_rtc->ops = &imx_sc_rtc_ops; 166e01b5781SAnson Huang imx_sc_rtc->range_min = 0; 167e01b5781SAnson Huang imx_sc_rtc->range_max = U32_MAX; 168e01b5781SAnson Huang 169*fdcfd854SBartosz Golaszewski ret = devm_rtc_register_device(imx_sc_rtc); 17044c638ceSAlexandre Belloni if (ret) 171e01b5781SAnson Huang return ret; 172e01b5781SAnson Huang 173a3094fc1SAnson Huang imx_scu_irq_register_notifier(&imx_sc_rtc_alarm_sc_notifier); 174a3094fc1SAnson Huang 175e01b5781SAnson Huang return 0; 176e01b5781SAnson Huang } 177e01b5781SAnson Huang 178e01b5781SAnson Huang static const struct of_device_id imx_sc_dt_ids[] = { 179e01b5781SAnson Huang { .compatible = "fsl,imx8qxp-sc-rtc", }, 180e01b5781SAnson Huang {} 181e01b5781SAnson Huang }; 182e01b5781SAnson Huang MODULE_DEVICE_TABLE(of, imx_sc_dt_ids); 183e01b5781SAnson Huang 184e01b5781SAnson Huang static struct platform_driver imx_sc_rtc_driver = { 185e01b5781SAnson Huang .driver = { 186e01b5781SAnson Huang .name = "imx-sc-rtc", 187e01b5781SAnson Huang .of_match_table = imx_sc_dt_ids, 188e01b5781SAnson Huang }, 189e01b5781SAnson Huang .probe = imx_sc_rtc_probe, 190e01b5781SAnson Huang }; 191e01b5781SAnson Huang module_platform_driver(imx_sc_rtc_driver); 192e01b5781SAnson Huang 193e01b5781SAnson Huang MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); 194e01b5781SAnson Huang MODULE_DESCRIPTION("NXP i.MX System Controller RTC Driver"); 195e01b5781SAnson Huang MODULE_LICENSE("GPL"); 196