]>
Commit | Line | Data |
---|---|---|
42986fb8 JK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | /* | |
4 | * Copyright 2019 Cadence | |
5 | * | |
6 | * Authors: | |
7 | * Jan Kotas <[email protected]> | |
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(reg, crtc->regs + CDNS_RTC_CTLR); | |
92 | } | |
93 | ||
94 | static bool cdns_rtc_get_enabled(struct cdns_rtc *crtc) | |
95 | { | |
96 | return !(readl(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(crtc->regs + CDNS_RTC_EFLR) & CDNS_RTC_AEI_ALRM)) | |
106 | return IRQ_NONE; | |
107 | ||
108 | rtc_update_irq(crtc->rtc_dev, 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, false); | |
136 | ||
137 | reg = readl(crtc->regs + CDNS_RTC_TIMR); | |
138 | cdns_rtc_reg2time(reg, tm); | |
139 | ||
140 | reg = readl(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, 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, 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(timr, crtc->regs + CDNS_RTC_TIMR); | |
172 | writel(calr, crtc->regs + CDNS_RTC_CALR); | |
173 | stsr = readl(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, 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((CDNS_RTC_AEI_SEC | CDNS_RTC_AEI_MIN | CDNS_RTC_AEI_HOUR | |
191 | | CDNS_RTC_AEI_DATE | CDNS_RTC_AEI_MNTH), | |
192 | crtc->regs + CDNS_RTC_AENR); | |
193 | writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IENR); | |
194 | } else { | |
195 | writel(0, crtc->regs + CDNS_RTC_AENR); | |
196 | writel(CDNS_RTC_AEI_ALRM, 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(crtc->regs + CDNS_RTC_TIMAR); | |
208 | cdns_rtc_reg2time(reg, &alarm->time); | |
209 | ||
210 | reg = readl(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, 0); | |
225 | ||
226 | timar = cdns_rtc_time2reg(&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(timar, crtc->regs + CDNS_RTC_TIMAR); | |
233 | writel(calar, crtc->regs + CDNS_RTC_CALAR); | |
234 | stsr = readl(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, 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; | |
42986fb8 JK |
258 | int ret; |
259 | unsigned long ref_clk_freq; | |
260 | ||
261 | crtc = devm_kzalloc(&pdev->dev, sizeof(*crtc), GFP_KERNEL); | |
262 | if (!crtc) | |
263 | return -ENOMEM; | |
264 | ||
09ef18bc | 265 | crtc->regs = devm_platform_ioremap_resource(pdev, 0); |
42986fb8 JK |
266 | if (IS_ERR(crtc->regs)) |
267 | return PTR_ERR(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(&pdev->dev, "pclk"); | |
274 | if (IS_ERR(crtc->pclk)) { | |
275 | ret = PTR_ERR(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(&pdev->dev, "ref_clk"); | |
282 | if (IS_ERR(crtc->ref_clk)) { | |
283 | ret = PTR_ERR(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(&pdev->dev); | |
44c638ce AB |
290 | if (IS_ERR(crtc->rtc_dev)) |
291 | return PTR_ERR(crtc->rtc_dev); | |
42986fb8 JK |
292 | |
293 | platform_set_drvdata(pdev, crtc); | |
294 | ||
295 | ret = clk_prepare_enable(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(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(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(&pdev->dev, crtc->irq, | |
319 | cdns_rtc_irq_handler, 0, | |
320 | dev_name(&pdev->dev), &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(1900, 1, 1, 0, 0, 0); | |
330 | crtc->rtc_dev->range_max = mktime64(2999, 12, 31, 23, 59, 59); | |
331 | ||
332 | crtc->rtc_dev->ops = &cdns_rtc_ops; | |
333 | device_init_wakeup(&pdev->dev, true); | |
334 | ||
335 | /* Always use 24-hour mode and keep the RTC values */ | |
336 | writel(0, crtc->regs + CDNS_RTC_HMR); | |
337 | writel(CDNS_RTC_KRTCR_KRTC, crtc->regs + CDNS_RTC_KRTCR); | |
338 | ||
339 | ret = rtc_register_device(crtc->rtc_dev); | |
44c638ce | 340 | if (ret) |
42986fb8 | 341 | goto err_disable_wakeup; |
42986fb8 JK |
342 | |
343 | return 0; | |
344 | ||
345 | err_disable_wakeup: | |
346 | device_init_wakeup(&pdev->dev, false); | |
347 | ||
348 | err_disable_ref_clk: | |
349 | clk_disable_unprepare(crtc->ref_clk); | |
350 | ||
351 | err_disable_pclk: | |
352 | clk_disable_unprepare(crtc->pclk); | |
353 | ||
354 | return ret; | |
355 | } | |
356 | ||
357 | static int cdns_rtc_remove(struct platform_device *pdev) | |
358 | { | |
359 | struct cdns_rtc *crtc = platform_get_drvdata(pdev); | |
360 | ||
361 | cdns_rtc_alarm_irq_enable(&pdev->dev, 0); | |
362 | device_init_wakeup(&pdev->dev, 0); | |
363 | ||
364 | clk_disable_unprepare(crtc->pclk); | |
365 | clk_disable_unprepare(crtc->ref_clk); | |
366 | ||
367 | return 0; | |
368 | } | |
369 | ||
370 | #ifdef CONFIG_PM_SLEEP | |
371 | static int cdns_rtc_suspend(struct device *dev) | |
372 | { | |
373 | struct cdns_rtc *crtc = dev_get_drvdata(dev); | |
374 | ||
375 | if (device_may_wakeup(dev)) | |
376 | enable_irq_wake(crtc->irq); | |
377 | ||
378 | return 0; | |
379 | } | |
380 | ||
381 | static int cdns_rtc_resume(struct device *dev) | |
382 | { | |
383 | struct cdns_rtc *crtc = dev_get_drvdata(dev); | |
384 | ||
385 | if (device_may_wakeup(dev)) | |
386 | disable_irq_wake(crtc->irq); | |
387 | ||
388 | return 0; | |
389 | } | |
390 | #endif | |
391 | ||
392 | static SIMPLE_DEV_PM_OPS(cdns_rtc_pm_ops, cdns_rtc_suspend, cdns_rtc_resume); | |
393 | ||
394 | static const struct of_device_id cdns_rtc_of_match[] = { | |
395 | { .compatible = "cdns,rtc-r109v3" }, | |
396 | { }, | |
397 | }; | |
398 | MODULE_DEVICE_TABLE(of, cdns_rtc_of_match); | |
399 | ||
400 | static struct platform_driver cdns_rtc_driver = { | |
401 | .driver = { | |
402 | .name = "cdns-rtc", | |
403 | .of_match_table = cdns_rtc_of_match, | |
404 | .pm = &cdns_rtc_pm_ops, | |
405 | }, | |
406 | .probe = cdns_rtc_probe, | |
407 | .remove = cdns_rtc_remove, | |
408 | }; | |
409 | module_platform_driver(cdns_rtc_driver); | |
410 | ||
411 | MODULE_AUTHOR("Jan Kotas <[email protected]>"); | |
412 | MODULE_DESCRIPTION("Cadence RTC driver"); | |
413 | MODULE_LICENSE("GPL v2"); | |
414 | MODULE_ALIAS("platform:cdns-rtc"); |