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