1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Renesas RZ/N1 Real Time Clock interface for Linux |
4 | * |
5 | * Copyright: |
6 | * - 2014 Renesas Electronics Europe Limited |
7 | * - 2022 Schneider Electric |
8 | * |
9 | * Authors: |
10 | * - Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com> |
11 | * - Miquel Raynal <miquel.raynal@bootlin.com> |
12 | */ |
13 | |
14 | #include <linux/bcd.h> |
15 | #include <linux/init.h> |
16 | #include <linux/iopoll.h> |
17 | #include <linux/module.h> |
18 | #include <linux/mod_devicetable.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/pm_runtime.h> |
21 | #include <linux/rtc.h> |
22 | |
23 | #define RZN1_RTC_CTL0 0x00 |
24 | #define RZN1_RTC_CTL0_SLSB_SUBU 0 |
25 | #define RZN1_RTC_CTL0_SLSB_SCMP BIT(4) |
26 | #define RZN1_RTC_CTL0_AMPM BIT(5) |
27 | #define RZN1_RTC_CTL0_CE BIT(7) |
28 | |
29 | #define RZN1_RTC_CTL1 0x04 |
30 | #define RZN1_RTC_CTL1_ALME BIT(4) |
31 | |
32 | #define RZN1_RTC_CTL2 0x08 |
33 | #define RZN1_RTC_CTL2_WAIT BIT(0) |
34 | #define RZN1_RTC_CTL2_WST BIT(1) |
35 | #define RZN1_RTC_CTL2_WUST BIT(5) |
36 | #define RZN1_RTC_CTL2_STOPPED (RZN1_RTC_CTL2_WAIT | RZN1_RTC_CTL2_WST) |
37 | |
38 | #define RZN1_RTC_SEC 0x14 |
39 | #define RZN1_RTC_MIN 0x18 |
40 | #define RZN1_RTC_HOUR 0x1c |
41 | #define RZN1_RTC_WEEK 0x20 |
42 | #define RZN1_RTC_DAY 0x24 |
43 | #define RZN1_RTC_MONTH 0x28 |
44 | #define RZN1_RTC_YEAR 0x2c |
45 | |
46 | #define RZN1_RTC_SUBU 0x38 |
47 | #define RZN1_RTC_SUBU_DEV BIT(7) |
48 | #define RZN1_RTC_SUBU_DECR BIT(6) |
49 | |
50 | #define RZN1_RTC_ALM 0x40 |
51 | #define RZN1_RTC_ALH 0x44 |
52 | #define RZN1_RTC_ALW 0x48 |
53 | |
54 | #define RZN1_RTC_SECC 0x4c |
55 | #define RZN1_RTC_MINC 0x50 |
56 | #define RZN1_RTC_HOURC 0x54 |
57 | #define RZN1_RTC_WEEKC 0x58 |
58 | #define RZN1_RTC_DAYC 0x5c |
59 | #define RZN1_RTC_MONTHC 0x60 |
60 | #define RZN1_RTC_YEARC 0x64 |
61 | |
62 | struct rzn1_rtc { |
63 | struct rtc_device *rtcdev; |
64 | void __iomem *base; |
65 | }; |
66 | |
67 | static void rzn1_rtc_get_time_snapshot(struct rzn1_rtc *rtc, struct rtc_time *tm) |
68 | { |
69 | tm->tm_sec = readl(addr: rtc->base + RZN1_RTC_SECC); |
70 | tm->tm_min = readl(addr: rtc->base + RZN1_RTC_MINC); |
71 | tm->tm_hour = readl(addr: rtc->base + RZN1_RTC_HOURC); |
72 | tm->tm_wday = readl(addr: rtc->base + RZN1_RTC_WEEKC); |
73 | tm->tm_mday = readl(addr: rtc->base + RZN1_RTC_DAYC); |
74 | tm->tm_mon = readl(addr: rtc->base + RZN1_RTC_MONTHC); |
75 | tm->tm_year = readl(addr: rtc->base + RZN1_RTC_YEARC); |
76 | } |
77 | |
78 | static unsigned int rzn1_rtc_tm_to_wday(struct rtc_time *tm) |
79 | { |
80 | time64_t time; |
81 | unsigned int days; |
82 | u32 secs; |
83 | |
84 | time = rtc_tm_to_time64(tm); |
85 | days = div_s64_rem(dividend: time, divisor: 86400, remainder: &secs); |
86 | |
87 | /* day of the week, 1970-01-01 was a Thursday */ |
88 | return (days + 4) % 7; |
89 | } |
90 | |
91 | static int rzn1_rtc_read_time(struct device *dev, struct rtc_time *tm) |
92 | { |
93 | struct rzn1_rtc *rtc = dev_get_drvdata(dev); |
94 | u32 val, secs; |
95 | |
96 | /* |
97 | * The RTC was not started or is stopped and thus does not carry the |
98 | * proper time/date. |
99 | */ |
100 | val = readl(addr: rtc->base + RZN1_RTC_CTL2); |
101 | if (val & RZN1_RTC_CTL2_STOPPED) |
102 | return -EINVAL; |
103 | |
104 | rzn1_rtc_get_time_snapshot(rtc, tm); |
105 | secs = readl(addr: rtc->base + RZN1_RTC_SECC); |
106 | if (tm->tm_sec != secs) |
107 | rzn1_rtc_get_time_snapshot(rtc, tm); |
108 | |
109 | tm->tm_sec = bcd2bin(tm->tm_sec); |
110 | tm->tm_min = bcd2bin(tm->tm_min); |
111 | tm->tm_hour = bcd2bin(tm->tm_hour); |
112 | tm->tm_wday = bcd2bin(tm->tm_wday); |
113 | tm->tm_mday = bcd2bin(tm->tm_mday); |
114 | tm->tm_mon = bcd2bin(tm->tm_mon); |
115 | tm->tm_year = bcd2bin(tm->tm_year); |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm) |
121 | { |
122 | struct rzn1_rtc *rtc = dev_get_drvdata(dev); |
123 | u32 val; |
124 | int ret; |
125 | |
126 | tm->tm_sec = bin2bcd(tm->tm_sec); |
127 | tm->tm_min = bin2bcd(tm->tm_min); |
128 | tm->tm_hour = bin2bcd(tm->tm_hour); |
129 | tm->tm_wday = bin2bcd(rzn1_rtc_tm_to_wday(tm)); |
130 | tm->tm_mday = bin2bcd(tm->tm_mday); |
131 | tm->tm_mon = bin2bcd(tm->tm_mon); |
132 | tm->tm_year = bin2bcd(tm->tm_year); |
133 | |
134 | val = readl(addr: rtc->base + RZN1_RTC_CTL2); |
135 | if (!(val & RZN1_RTC_CTL2_STOPPED)) { |
136 | /* Hold the counter if it was counting up */ |
137 | writel(RZN1_RTC_CTL2_WAIT, addr: rtc->base + RZN1_RTC_CTL2); |
138 | |
139 | /* Wait for the counter to stop: two 32k clock cycles */ |
140 | usleep_range(min: 61, max: 100); |
141 | ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, val, |
142 | val & RZN1_RTC_CTL2_WST, 0, 100); |
143 | if (ret) |
144 | return ret; |
145 | } |
146 | |
147 | writel(val: tm->tm_sec, addr: rtc->base + RZN1_RTC_SEC); |
148 | writel(val: tm->tm_min, addr: rtc->base + RZN1_RTC_MIN); |
149 | writel(val: tm->tm_hour, addr: rtc->base + RZN1_RTC_HOUR); |
150 | writel(val: tm->tm_wday, addr: rtc->base + RZN1_RTC_WEEK); |
151 | writel(val: tm->tm_mday, addr: rtc->base + RZN1_RTC_DAY); |
152 | writel(val: tm->tm_mon, addr: rtc->base + RZN1_RTC_MONTH); |
153 | writel(val: tm->tm_year, addr: rtc->base + RZN1_RTC_YEAR); |
154 | writel(val: 0, addr: rtc->base + RZN1_RTC_CTL2); |
155 | |
156 | return 0; |
157 | } |
158 | |
159 | static irqreturn_t rzn1_rtc_alarm_irq(int irq, void *dev_id) |
160 | { |
161 | struct rzn1_rtc *rtc = dev_id; |
162 | |
163 | rtc_update_irq(rtc: rtc->rtcdev, num: 1, RTC_AF | RTC_IRQF); |
164 | |
165 | return IRQ_HANDLED; |
166 | } |
167 | |
168 | static int rzn1_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) |
169 | { |
170 | struct rzn1_rtc *rtc = dev_get_drvdata(dev); |
171 | u32 ctl1 = readl(addr: rtc->base + RZN1_RTC_CTL1); |
172 | |
173 | if (enable) |
174 | ctl1 |= RZN1_RTC_CTL1_ALME; |
175 | else |
176 | ctl1 &= ~RZN1_RTC_CTL1_ALME; |
177 | |
178 | writel(val: ctl1, addr: rtc->base + RZN1_RTC_CTL1); |
179 | |
180 | return 0; |
181 | } |
182 | |
183 | static int rzn1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
184 | { |
185 | struct rzn1_rtc *rtc = dev_get_drvdata(dev); |
186 | struct rtc_time *tm = &alrm->time; |
187 | unsigned int min, hour, wday, delta_days; |
188 | time64_t alarm; |
189 | u32 ctl1; |
190 | int ret; |
191 | |
192 | ret = rzn1_rtc_read_time(dev, tm); |
193 | if (ret) |
194 | return ret; |
195 | |
196 | min = readl(addr: rtc->base + RZN1_RTC_ALM); |
197 | hour = readl(addr: rtc->base + RZN1_RTC_ALH); |
198 | wday = readl(addr: rtc->base + RZN1_RTC_ALW); |
199 | |
200 | tm->tm_sec = 0; |
201 | tm->tm_min = bcd2bin(min); |
202 | tm->tm_hour = bcd2bin(hour); |
203 | delta_days = ((fls(x: wday) - 1) - tm->tm_wday + 7) % 7; |
204 | tm->tm_wday = fls(x: wday) - 1; |
205 | |
206 | if (delta_days) { |
207 | alarm = rtc_tm_to_time64(tm) + (delta_days * 86400); |
208 | rtc_time64_to_tm(time: alarm, tm); |
209 | } |
210 | |
211 | ctl1 = readl(addr: rtc->base + RZN1_RTC_CTL1); |
212 | alrm->enabled = !!(ctl1 & RZN1_RTC_CTL1_ALME); |
213 | |
214 | return 0; |
215 | } |
216 | |
217 | static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
218 | { |
219 | struct rzn1_rtc *rtc = dev_get_drvdata(dev); |
220 | struct rtc_time *tm = &alrm->time, tm_now; |
221 | unsigned long alarm, farest; |
222 | unsigned int days_ahead, wday; |
223 | int ret; |
224 | |
225 | ret = rzn1_rtc_read_time(dev, tm: &tm_now); |
226 | if (ret) |
227 | return ret; |
228 | |
229 | /* We cannot set alarms more than one week ahead */ |
230 | farest = rtc_tm_to_time64(tm: &tm_now) + rtc->rtcdev->alarm_offset_max; |
231 | alarm = rtc_tm_to_time64(tm); |
232 | if (time_after(alarm, farest)) |
233 | return -ERANGE; |
234 | |
235 | /* Convert alarm day into week day */ |
236 | days_ahead = tm->tm_mday - tm_now.tm_mday; |
237 | wday = (tm_now.tm_wday + days_ahead) % 7; |
238 | |
239 | writel(bin2bcd(tm->tm_min), addr: rtc->base + RZN1_RTC_ALM); |
240 | writel(bin2bcd(tm->tm_hour), addr: rtc->base + RZN1_RTC_ALH); |
241 | writel(BIT(wday), addr: rtc->base + RZN1_RTC_ALW); |
242 | |
243 | rzn1_rtc_alarm_irq_enable(dev, enable: alrm->enabled); |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | static int rzn1_rtc_read_offset(struct device *dev, long *offset) |
249 | { |
250 | struct rzn1_rtc *rtc = dev_get_drvdata(dev); |
251 | unsigned int ppb_per_step; |
252 | bool subtract; |
253 | u32 val; |
254 | |
255 | val = readl(addr: rtc->base + RZN1_RTC_SUBU); |
256 | ppb_per_step = val & RZN1_RTC_SUBU_DEV ? 1017 : 3051; |
257 | subtract = val & RZN1_RTC_SUBU_DECR; |
258 | val &= 0x3F; |
259 | |
260 | if (!val) |
261 | *offset = 0; |
262 | else if (subtract) |
263 | *offset = -(((~val) & 0x3F) + 1) * ppb_per_step; |
264 | else |
265 | *offset = (val - 1) * ppb_per_step; |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | static int rzn1_rtc_set_offset(struct device *dev, long offset) |
271 | { |
272 | struct rzn1_rtc *rtc = dev_get_drvdata(dev); |
273 | int stepsh, stepsl, steps; |
274 | u32 subu = 0, ctl2; |
275 | int ret; |
276 | |
277 | /* |
278 | * Check which resolution mode (every 20 or 60s) can be used. |
279 | * Between 2 and 124 clock pulses can be added or substracted. |
280 | * |
281 | * In 20s mode, the minimum resolution is 2 / (32768 * 20) which is |
282 | * close to 3051 ppb. In 60s mode, the resolution is closer to 1017. |
283 | */ |
284 | stepsh = DIV_ROUND_CLOSEST(offset, 1017); |
285 | stepsl = DIV_ROUND_CLOSEST(offset, 3051); |
286 | |
287 | if (stepsh >= -0x3E && stepsh <= 0x3E) { |
288 | /* 1017 ppb per step */ |
289 | steps = stepsh; |
290 | subu |= RZN1_RTC_SUBU_DEV; |
291 | } else if (stepsl >= -0x3E && stepsl <= 0x3E) { |
292 | /* 3051 ppb per step */ |
293 | steps = stepsl; |
294 | } else { |
295 | return -ERANGE; |
296 | } |
297 | |
298 | if (!steps) |
299 | return 0; |
300 | |
301 | if (steps > 0) { |
302 | subu |= steps + 1; |
303 | } else { |
304 | subu |= RZN1_RTC_SUBU_DECR; |
305 | subu |= (~(-steps - 1)) & 0x3F; |
306 | } |
307 | |
308 | ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, ctl2, |
309 | !(ctl2 & RZN1_RTC_CTL2_WUST), 100, 2000000); |
310 | if (ret) |
311 | return ret; |
312 | |
313 | writel(val: subu, addr: rtc->base + RZN1_RTC_SUBU); |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | static const struct rtc_class_ops rzn1_rtc_ops = { |
319 | .read_time = rzn1_rtc_read_time, |
320 | .set_time = rzn1_rtc_set_time, |
321 | .read_alarm = rzn1_rtc_read_alarm, |
322 | .set_alarm = rzn1_rtc_set_alarm, |
323 | .alarm_irq_enable = rzn1_rtc_alarm_irq_enable, |
324 | .read_offset = rzn1_rtc_read_offset, |
325 | .set_offset = rzn1_rtc_set_offset, |
326 | }; |
327 | |
328 | static int rzn1_rtc_probe(struct platform_device *pdev) |
329 | { |
330 | struct rzn1_rtc *rtc; |
331 | int alarm_irq; |
332 | int ret; |
333 | |
334 | rtc = devm_kzalloc(dev: &pdev->dev, size: sizeof(*rtc), GFP_KERNEL); |
335 | if (!rtc) |
336 | return -ENOMEM; |
337 | |
338 | platform_set_drvdata(pdev, data: rtc); |
339 | |
340 | rtc->base = devm_platform_ioremap_resource(pdev, index: 0); |
341 | if (IS_ERR(ptr: rtc->base)) |
342 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: rtc->base), fmt: "Missing reg\n" ); |
343 | |
344 | alarm_irq = platform_get_irq(pdev, 0); |
345 | if (alarm_irq < 0) |
346 | return alarm_irq; |
347 | |
348 | rtc->rtcdev = devm_rtc_allocate_device(dev: &pdev->dev); |
349 | if (IS_ERR(ptr: rtc->rtcdev)) |
350 | return PTR_ERR(ptr: rtc->rtcdev); |
351 | |
352 | rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; |
353 | rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099; |
354 | rtc->rtcdev->alarm_offset_max = 7 * 86400; |
355 | rtc->rtcdev->ops = &rzn1_rtc_ops; |
356 | set_bit(RTC_FEATURE_ALARM_RES_MINUTE, addr: rtc->rtcdev->features); |
357 | clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, addr: rtc->rtcdev->features); |
358 | |
359 | ret = devm_pm_runtime_enable(dev: &pdev->dev); |
360 | if (ret < 0) |
361 | return ret; |
362 | ret = pm_runtime_resume_and_get(dev: &pdev->dev); |
363 | if (ret < 0) |
364 | return ret; |
365 | |
366 | /* |
367 | * Ensure the clock counter is enabled. |
368 | * Set 24-hour mode and possible oscillator offset compensation in SUBU mode. |
369 | */ |
370 | writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | RZN1_RTC_CTL0_SLSB_SUBU, |
371 | addr: rtc->base + RZN1_RTC_CTL0); |
372 | |
373 | /* Disable all interrupts */ |
374 | writel(val: 0, addr: rtc->base + RZN1_RTC_CTL1); |
375 | |
376 | ret = devm_request_irq(dev: &pdev->dev, irq: alarm_irq, handler: rzn1_rtc_alarm_irq, irqflags: 0, |
377 | devname: dev_name(dev: &pdev->dev), dev_id: rtc); |
378 | if (ret) { |
379 | dev_err(&pdev->dev, "RTC timer interrupt not available\n" ); |
380 | goto dis_runtime_pm; |
381 | } |
382 | |
383 | ret = devm_rtc_register_device(rtc->rtcdev); |
384 | if (ret) |
385 | goto dis_runtime_pm; |
386 | |
387 | return 0; |
388 | |
389 | dis_runtime_pm: |
390 | pm_runtime_put(dev: &pdev->dev); |
391 | |
392 | return ret; |
393 | } |
394 | |
395 | static void rzn1_rtc_remove(struct platform_device *pdev) |
396 | { |
397 | pm_runtime_put(dev: &pdev->dev); |
398 | } |
399 | |
400 | static const struct of_device_id rzn1_rtc_of_match[] = { |
401 | { .compatible = "renesas,rzn1-rtc" }, |
402 | {}, |
403 | }; |
404 | MODULE_DEVICE_TABLE(of, rzn1_rtc_of_match); |
405 | |
406 | static struct platform_driver rzn1_rtc_driver = { |
407 | .probe = rzn1_rtc_probe, |
408 | .remove_new = rzn1_rtc_remove, |
409 | .driver = { |
410 | .name = "rzn1-rtc" , |
411 | .of_match_table = rzn1_rtc_of_match, |
412 | }, |
413 | }; |
414 | module_platform_driver(rzn1_rtc_driver); |
415 | |
416 | MODULE_AUTHOR("Michel Pollet <Michel.Pollet@bp.renesas.com" ); |
417 | MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com" ); |
418 | MODULE_DESCRIPTION("RZ/N1 RTC driver" ); |
419 | MODULE_LICENSE("GPL" ); |
420 | |