1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | /* |
4 | * Copyright 2019 Cadence |
5 | * |
6 | * Authors: |
7 | * Jan Kotas <jank@cadence.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/of.h> |
13 | #include <linux/io.h> |
14 | #include <linux/rtc.h> |
15 | #include <linux/clk.h> |
16 | #include <linux/bcd.h> |
17 | #include <linux/bitfield.h> |
18 | #include <linux/interrupt.h> |
19 | #include <linux/pm_wakeirq.h> |
20 | |
21 | /* Registers */ |
22 | #define CDNS_RTC_CTLR 0x00 |
23 | #define CDNS_RTC_HMR 0x04 |
24 | #define CDNS_RTC_TIMR 0x08 |
25 | #define CDNS_RTC_CALR 0x0C |
26 | #define CDNS_RTC_TIMAR 0x10 |
27 | #define CDNS_RTC_CALAR 0x14 |
28 | #define CDNS_RTC_AENR 0x18 |
29 | #define CDNS_RTC_EFLR 0x1C |
30 | #define CDNS_RTC_IENR 0x20 |
31 | #define CDNS_RTC_IDISR 0x24 |
32 | #define CDNS_RTC_IMSKR 0x28 |
33 | #define CDNS_RTC_STSR 0x2C |
34 | #define CDNS_RTC_KRTCR 0x30 |
35 | |
36 | /* Control */ |
37 | #define CDNS_RTC_CTLR_TIME BIT(0) |
38 | #define CDNS_RTC_CTLR_CAL BIT(1) |
39 | #define CDNS_RTC_CTLR_TIME_CAL (CDNS_RTC_CTLR_TIME | CDNS_RTC_CTLR_CAL) |
40 | |
41 | /* Status */ |
42 | #define CDNS_RTC_STSR_VT BIT(0) |
43 | #define CDNS_RTC_STSR_VC BIT(1) |
44 | #define CDNS_RTC_STSR_VTA BIT(2) |
45 | #define CDNS_RTC_STSR_VCA BIT(3) |
46 | #define CDNS_RTC_STSR_VT_VC (CDNS_RTC_STSR_VT | CDNS_RTC_STSR_VC) |
47 | #define CDNS_RTC_STSR_VTA_VCA (CDNS_RTC_STSR_VTA | CDNS_RTC_STSR_VCA) |
48 | |
49 | /* Keep RTC */ |
50 | #define CDNS_RTC_KRTCR_KRTC BIT(0) |
51 | |
52 | /* Alarm, Event, Interrupt */ |
53 | #define CDNS_RTC_AEI_HOS BIT(0) |
54 | #define CDNS_RTC_AEI_SEC BIT(1) |
55 | #define CDNS_RTC_AEI_MIN BIT(2) |
56 | #define CDNS_RTC_AEI_HOUR BIT(3) |
57 | #define CDNS_RTC_AEI_DATE BIT(4) |
58 | #define CDNS_RTC_AEI_MNTH BIT(5) |
59 | #define CDNS_RTC_AEI_ALRM BIT(6) |
60 | |
61 | /* Time */ |
62 | #define CDNS_RTC_TIME_H GENMASK(7, 0) |
63 | #define CDNS_RTC_TIME_S GENMASK(14, 8) |
64 | #define CDNS_RTC_TIME_M GENMASK(22, 16) |
65 | #define CDNS_RTC_TIME_HR GENMASK(29, 24) |
66 | #define CDNS_RTC_TIME_PM BIT(30) |
67 | #define CDNS_RTC_TIME_CH BIT(31) |
68 | |
69 | /* Calendar */ |
70 | #define CDNS_RTC_CAL_DAY GENMASK(2, 0) |
71 | #define CDNS_RTC_CAL_M GENMASK(7, 3) |
72 | #define CDNS_RTC_CAL_D GENMASK(13, 8) |
73 | #define CDNS_RTC_CAL_Y GENMASK(23, 16) |
74 | #define CDNS_RTC_CAL_C GENMASK(29, 24) |
75 | #define CDNS_RTC_CAL_CH BIT(31) |
76 | |
77 | #define CDNS_RTC_MAX_REGS_TRIES 3 |
78 | |
79 | struct cdns_rtc { |
80 | struct rtc_device *rtc_dev; |
81 | struct clk *pclk; |
82 | struct clk *ref_clk; |
83 | void __iomem *regs; |
84 | int irq; |
85 | }; |
86 | |
87 | static void cdns_rtc_set_enabled(struct cdns_rtc *crtc, bool enabled) |
88 | { |
89 | u32 reg = enabled ? 0x0 : CDNS_RTC_CTLR_TIME_CAL; |
90 | |
91 | writel(val: reg, addr: crtc->regs + CDNS_RTC_CTLR); |
92 | } |
93 | |
94 | static bool cdns_rtc_get_enabled(struct cdns_rtc *crtc) |
95 | { |
96 | return !(readl(addr: crtc->regs + CDNS_RTC_CTLR) & CDNS_RTC_CTLR_TIME_CAL); |
97 | } |
98 | |
99 | static irqreturn_t cdns_rtc_irq_handler(int irq, void *id) |
100 | { |
101 | struct device *dev = id; |
102 | struct cdns_rtc *crtc = dev_get_drvdata(dev); |
103 | |
104 | /* Reading the register clears it */ |
105 | if (!(readl(addr: crtc->regs + CDNS_RTC_EFLR) & CDNS_RTC_AEI_ALRM)) |
106 | return IRQ_NONE; |
107 | |
108 | rtc_update_irq(rtc: crtc->rtc_dev, num: 1, RTC_IRQF | RTC_AF); |
109 | return IRQ_HANDLED; |
110 | } |
111 | |
112 | static u32 cdns_rtc_time2reg(struct rtc_time *tm) |
113 | { |
114 | return FIELD_PREP(CDNS_RTC_TIME_S, bin2bcd(tm->tm_sec)) |
115 | | FIELD_PREP(CDNS_RTC_TIME_M, bin2bcd(tm->tm_min)) |
116 | | FIELD_PREP(CDNS_RTC_TIME_HR, bin2bcd(tm->tm_hour)); |
117 | } |
118 | |
119 | static void cdns_rtc_reg2time(u32 reg, struct rtc_time *tm) |
120 | { |
121 | tm->tm_sec = bcd2bin(FIELD_GET(CDNS_RTC_TIME_S, reg)); |
122 | tm->tm_min = bcd2bin(FIELD_GET(CDNS_RTC_TIME_M, reg)); |
123 | tm->tm_hour = bcd2bin(FIELD_GET(CDNS_RTC_TIME_HR, reg)); |
124 | } |
125 | |
126 | static int cdns_rtc_read_time(struct device *dev, struct rtc_time *tm) |
127 | { |
128 | struct cdns_rtc *crtc = dev_get_drvdata(dev); |
129 | u32 reg; |
130 | |
131 | /* If the RTC is disabled, assume the values are invalid */ |
132 | if (!cdns_rtc_get_enabled(crtc)) |
133 | return -EINVAL; |
134 | |
135 | cdns_rtc_set_enabled(crtc, enabled: false); |
136 | |
137 | reg = readl(addr: crtc->regs + CDNS_RTC_TIMR); |
138 | cdns_rtc_reg2time(reg, tm); |
139 | |
140 | reg = readl(addr: crtc->regs + CDNS_RTC_CALR); |
141 | tm->tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg)); |
142 | tm->tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1; |
143 | tm->tm_year = bcd2bin(FIELD_GET(CDNS_RTC_CAL_Y, reg)) |
144 | + bcd2bin(FIELD_GET(CDNS_RTC_CAL_C, reg)) * 100 - 1900; |
145 | tm->tm_wday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_DAY, reg)) - 1; |
146 | |
147 | cdns_rtc_set_enabled(crtc, enabled: true); |
148 | return 0; |
149 | } |
150 | |
151 | static int cdns_rtc_set_time(struct device *dev, struct rtc_time *tm) |
152 | { |
153 | struct cdns_rtc *crtc = dev_get_drvdata(dev); |
154 | u32 timr, calr, stsr; |
155 | int ret = -EIO; |
156 | int year = tm->tm_year + 1900; |
157 | int tries; |
158 | |
159 | cdns_rtc_set_enabled(crtc, enabled: false); |
160 | |
161 | timr = cdns_rtc_time2reg(tm); |
162 | |
163 | calr = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(tm->tm_mday)) |
164 | | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(tm->tm_mon + 1)) |
165 | | FIELD_PREP(CDNS_RTC_CAL_Y, bin2bcd(year % 100)) |
166 | | FIELD_PREP(CDNS_RTC_CAL_C, bin2bcd(year / 100)) |
167 | | FIELD_PREP(CDNS_RTC_CAL_DAY, tm->tm_wday + 1); |
168 | |
169 | /* Update registers, check valid flags */ |
170 | for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) { |
171 | writel(val: timr, addr: crtc->regs + CDNS_RTC_TIMR); |
172 | writel(val: calr, addr: crtc->regs + CDNS_RTC_CALR); |
173 | stsr = readl(addr: crtc->regs + CDNS_RTC_STSR); |
174 | |
175 | if ((stsr & CDNS_RTC_STSR_VT_VC) == CDNS_RTC_STSR_VT_VC) { |
176 | ret = 0; |
177 | break; |
178 | } |
179 | } |
180 | |
181 | cdns_rtc_set_enabled(crtc, enabled: true); |
182 | return ret; |
183 | } |
184 | |
185 | static int cdns_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
186 | { |
187 | struct cdns_rtc *crtc = dev_get_drvdata(dev); |
188 | |
189 | if (enabled) { |
190 | writel(val: (CDNS_RTC_AEI_SEC | CDNS_RTC_AEI_MIN | CDNS_RTC_AEI_HOUR |
191 | | CDNS_RTC_AEI_DATE | CDNS_RTC_AEI_MNTH), |
192 | addr: crtc->regs + CDNS_RTC_AENR); |
193 | writel(CDNS_RTC_AEI_ALRM, addr: crtc->regs + CDNS_RTC_IENR); |
194 | } else { |
195 | writel(val: 0, addr: crtc->regs + CDNS_RTC_AENR); |
196 | writel(CDNS_RTC_AEI_ALRM, addr: crtc->regs + CDNS_RTC_IDISR); |
197 | } |
198 | |
199 | return 0; |
200 | } |
201 | |
202 | static int cdns_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
203 | { |
204 | struct cdns_rtc *crtc = dev_get_drvdata(dev); |
205 | u32 reg; |
206 | |
207 | reg = readl(addr: crtc->regs + CDNS_RTC_TIMAR); |
208 | cdns_rtc_reg2time(reg, tm: &alarm->time); |
209 | |
210 | reg = readl(addr: crtc->regs + CDNS_RTC_CALAR); |
211 | alarm->time.tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg)); |
212 | alarm->time.tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1; |
213 | |
214 | return 0; |
215 | } |
216 | |
217 | static int cdns_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
218 | { |
219 | struct cdns_rtc *crtc = dev_get_drvdata(dev); |
220 | int ret = -EIO; |
221 | int tries; |
222 | u32 timar, calar, stsr; |
223 | |
224 | cdns_rtc_alarm_irq_enable(dev, enabled: 0); |
225 | |
226 | timar = cdns_rtc_time2reg(tm: &alarm->time); |
227 | calar = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(alarm->time.tm_mday)) |
228 | | FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(alarm->time.tm_mon + 1)); |
229 | |
230 | /* Update registers, check valid alarm flags */ |
231 | for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) { |
232 | writel(val: timar, addr: crtc->regs + CDNS_RTC_TIMAR); |
233 | writel(val: calar, addr: crtc->regs + CDNS_RTC_CALAR); |
234 | stsr = readl(addr: crtc->regs + CDNS_RTC_STSR); |
235 | |
236 | if ((stsr & CDNS_RTC_STSR_VTA_VCA) == CDNS_RTC_STSR_VTA_VCA) { |
237 | ret = 0; |
238 | break; |
239 | } |
240 | } |
241 | |
242 | if (!ret) |
243 | cdns_rtc_alarm_irq_enable(dev, enabled: alarm->enabled); |
244 | return ret; |
245 | } |
246 | |
247 | static const struct rtc_class_ops cdns_rtc_ops = { |
248 | .read_time = cdns_rtc_read_time, |
249 | .set_time = cdns_rtc_set_time, |
250 | .read_alarm = cdns_rtc_read_alarm, |
251 | .set_alarm = cdns_rtc_set_alarm, |
252 | .alarm_irq_enable = cdns_rtc_alarm_irq_enable, |
253 | }; |
254 | |
255 | static int cdns_rtc_probe(struct platform_device *pdev) |
256 | { |
257 | struct cdns_rtc *crtc; |
258 | int ret; |
259 | unsigned long ref_clk_freq; |
260 | |
261 | crtc = devm_kzalloc(dev: &pdev->dev, size: sizeof(*crtc), GFP_KERNEL); |
262 | if (!crtc) |
263 | return -ENOMEM; |
264 | |
265 | crtc->regs = devm_platform_ioremap_resource(pdev, index: 0); |
266 | if (IS_ERR(ptr: crtc->regs)) |
267 | return PTR_ERR(ptr: crtc->regs); |
268 | |
269 | crtc->irq = platform_get_irq(pdev, 0); |
270 | if (crtc->irq < 0) |
271 | return -EINVAL; |
272 | |
273 | crtc->pclk = devm_clk_get(dev: &pdev->dev, id: "pclk" ); |
274 | if (IS_ERR(ptr: crtc->pclk)) { |
275 | ret = PTR_ERR(ptr: crtc->pclk); |
276 | dev_err(&pdev->dev, |
277 | "Failed to retrieve the peripheral clock, %d\n" , ret); |
278 | return ret; |
279 | } |
280 | |
281 | crtc->ref_clk = devm_clk_get(dev: &pdev->dev, id: "ref_clk" ); |
282 | if (IS_ERR(ptr: crtc->ref_clk)) { |
283 | ret = PTR_ERR(ptr: crtc->ref_clk); |
284 | dev_err(&pdev->dev, |
285 | "Failed to retrieve the reference clock, %d\n" , ret); |
286 | return ret; |
287 | } |
288 | |
289 | crtc->rtc_dev = devm_rtc_allocate_device(dev: &pdev->dev); |
290 | if (IS_ERR(ptr: crtc->rtc_dev)) |
291 | return PTR_ERR(ptr: crtc->rtc_dev); |
292 | |
293 | platform_set_drvdata(pdev, data: crtc); |
294 | |
295 | ret = clk_prepare_enable(clk: crtc->pclk); |
296 | if (ret) { |
297 | dev_err(&pdev->dev, |
298 | "Failed to enable the peripheral clock, %d\n" , ret); |
299 | return ret; |
300 | } |
301 | |
302 | ret = clk_prepare_enable(clk: crtc->ref_clk); |
303 | if (ret) { |
304 | dev_err(&pdev->dev, |
305 | "Failed to enable the reference clock, %d\n" , ret); |
306 | goto err_disable_pclk; |
307 | } |
308 | |
309 | ref_clk_freq = clk_get_rate(clk: crtc->ref_clk); |
310 | if ((ref_clk_freq != 1) && (ref_clk_freq != 100)) { |
311 | dev_err(&pdev->dev, |
312 | "Invalid reference clock frequency %lu Hz.\n" , |
313 | ref_clk_freq); |
314 | ret = -EINVAL; |
315 | goto err_disable_ref_clk; |
316 | } |
317 | |
318 | ret = devm_request_irq(dev: &pdev->dev, irq: crtc->irq, |
319 | handler: cdns_rtc_irq_handler, irqflags: 0, |
320 | devname: dev_name(dev: &pdev->dev), dev_id: &pdev->dev); |
321 | if (ret) { |
322 | dev_err(&pdev->dev, |
323 | "Failed to request interrupt for the device, %d\n" , |
324 | ret); |
325 | goto err_disable_ref_clk; |
326 | } |
327 | |
328 | /* The RTC supports 01.01.1900 - 31.12.2999 */ |
329 | crtc->rtc_dev->range_min = mktime64(year: 1900, mon: 1, day: 1, hour: 0, min: 0, sec: 0); |
330 | crtc->rtc_dev->range_max = mktime64(year: 2999, mon: 12, day: 31, hour: 23, min: 59, sec: 59); |
331 | |
332 | crtc->rtc_dev->ops = &cdns_rtc_ops; |
333 | device_init_wakeup(dev: &pdev->dev, enable: true); |
334 | |
335 | /* Always use 24-hour mode and keep the RTC values */ |
336 | writel(val: 0, addr: crtc->regs + CDNS_RTC_HMR); |
337 | writel(CDNS_RTC_KRTCR_KRTC, addr: crtc->regs + CDNS_RTC_KRTCR); |
338 | |
339 | ret = devm_rtc_register_device(crtc->rtc_dev); |
340 | if (ret) |
341 | goto err_disable_wakeup; |
342 | |
343 | return 0; |
344 | |
345 | err_disable_wakeup: |
346 | device_init_wakeup(dev: &pdev->dev, enable: false); |
347 | |
348 | err_disable_ref_clk: |
349 | clk_disable_unprepare(clk: crtc->ref_clk); |
350 | |
351 | err_disable_pclk: |
352 | clk_disable_unprepare(clk: crtc->pclk); |
353 | |
354 | return ret; |
355 | } |
356 | |
357 | static void cdns_rtc_remove(struct platform_device *pdev) |
358 | { |
359 | struct cdns_rtc *crtc = platform_get_drvdata(pdev); |
360 | |
361 | cdns_rtc_alarm_irq_enable(dev: &pdev->dev, enabled: 0); |
362 | device_init_wakeup(dev: &pdev->dev, enable: 0); |
363 | |
364 | clk_disable_unprepare(clk: crtc->pclk); |
365 | clk_disable_unprepare(clk: crtc->ref_clk); |
366 | } |
367 | |
368 | #ifdef CONFIG_PM_SLEEP |
369 | static int cdns_rtc_suspend(struct device *dev) |
370 | { |
371 | struct cdns_rtc *crtc = dev_get_drvdata(dev); |
372 | |
373 | if (device_may_wakeup(dev)) |
374 | enable_irq_wake(irq: crtc->irq); |
375 | |
376 | return 0; |
377 | } |
378 | |
379 | static int cdns_rtc_resume(struct device *dev) |
380 | { |
381 | struct cdns_rtc *crtc = dev_get_drvdata(dev); |
382 | |
383 | if (device_may_wakeup(dev)) |
384 | disable_irq_wake(irq: crtc->irq); |
385 | |
386 | return 0; |
387 | } |
388 | #endif |
389 | |
390 | static SIMPLE_DEV_PM_OPS(cdns_rtc_pm_ops, cdns_rtc_suspend, cdns_rtc_resume); |
391 | |
392 | static const struct of_device_id cdns_rtc_of_match[] = { |
393 | { .compatible = "cdns,rtc-r109v3" }, |
394 | { }, |
395 | }; |
396 | MODULE_DEVICE_TABLE(of, cdns_rtc_of_match); |
397 | |
398 | static struct platform_driver cdns_rtc_driver = { |
399 | .driver = { |
400 | .name = "cdns-rtc" , |
401 | .of_match_table = cdns_rtc_of_match, |
402 | .pm = &cdns_rtc_pm_ops, |
403 | }, |
404 | .probe = cdns_rtc_probe, |
405 | .remove_new = cdns_rtc_remove, |
406 | }; |
407 | module_platform_driver(cdns_rtc_driver); |
408 | |
409 | MODULE_AUTHOR("Jan Kotas <jank@cadence.com>" ); |
410 | MODULE_DESCRIPTION("Cadence RTC driver" ); |
411 | MODULE_LICENSE("GPL v2" ); |
412 | MODULE_ALIAS("platform:cdns-rtc" ); |
413 | |