1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * An RTC driver for Allwinner A10/A20 |
4 | * |
5 | * Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <linux/err.h> |
10 | #include <linux/fs.h> |
11 | #include <linux/init.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/io.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/rtc.h> |
19 | #include <linux/types.h> |
20 | |
21 | #define SUNXI_LOSC_CTRL 0x0000 |
22 | #define SUNXI_LOSC_CTRL_RTC_HMS_ACC BIT(8) |
23 | #define SUNXI_LOSC_CTRL_RTC_YMD_ACC BIT(7) |
24 | |
25 | #define SUNXI_RTC_YMD 0x0004 |
26 | |
27 | #define SUNXI_RTC_HMS 0x0008 |
28 | |
29 | #define SUNXI_ALRM_DHMS 0x000c |
30 | |
31 | #define SUNXI_ALRM_EN 0x0014 |
32 | #define SUNXI_ALRM_EN_CNT_EN BIT(8) |
33 | |
34 | #define SUNXI_ALRM_IRQ_EN 0x0018 |
35 | #define SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0) |
36 | |
37 | #define SUNXI_ALRM_IRQ_STA 0x001c |
38 | #define SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0) |
39 | |
40 | #define SUNXI_MASK_DH 0x0000001f |
41 | #define SUNXI_MASK_SM 0x0000003f |
42 | #define SUNXI_MASK_M 0x0000000f |
43 | #define SUNXI_MASK_LY 0x00000001 |
44 | #define SUNXI_MASK_D 0x00000ffe |
45 | #define SUNXI_MASK_M 0x0000000f |
46 | |
47 | #define SUNXI_GET(x, mask, shift) (((x) & ((mask) << (shift))) \ |
48 | >> (shift)) |
49 | |
50 | #define SUNXI_SET(x, mask, shift) (((x) & (mask)) << (shift)) |
51 | |
52 | /* |
53 | * Get date values |
54 | */ |
55 | #define SUNXI_DATE_GET_DAY_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 0) |
56 | #define SUNXI_DATE_GET_MON_VALUE(x) SUNXI_GET(x, SUNXI_MASK_M, 8) |
57 | #define SUNXI_DATE_GET_YEAR_VALUE(x, mask) SUNXI_GET(x, mask, 16) |
58 | |
59 | /* |
60 | * Get time values |
61 | */ |
62 | #define SUNXI_TIME_GET_SEC_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 0) |
63 | #define SUNXI_TIME_GET_MIN_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 8) |
64 | #define SUNXI_TIME_GET_HOUR_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 16) |
65 | |
66 | /* |
67 | * Get alarm values |
68 | */ |
69 | #define SUNXI_ALRM_GET_SEC_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 0) |
70 | #define SUNXI_ALRM_GET_MIN_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 8) |
71 | #define SUNXI_ALRM_GET_HOUR_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 16) |
72 | |
73 | /* |
74 | * Set date values |
75 | */ |
76 | #define SUNXI_DATE_SET_DAY_VALUE(x) SUNXI_DATE_GET_DAY_VALUE(x) |
77 | #define SUNXI_DATE_SET_MON_VALUE(x) SUNXI_SET(x, SUNXI_MASK_M, 8) |
78 | #define SUNXI_DATE_SET_YEAR_VALUE(x, mask) SUNXI_SET(x, mask, 16) |
79 | #define SUNXI_LEAP_SET_VALUE(x, shift) SUNXI_SET(x, SUNXI_MASK_LY, shift) |
80 | |
81 | /* |
82 | * Set time values |
83 | */ |
84 | #define SUNXI_TIME_SET_SEC_VALUE(x) SUNXI_TIME_GET_SEC_VALUE(x) |
85 | #define SUNXI_TIME_SET_MIN_VALUE(x) SUNXI_SET(x, SUNXI_MASK_SM, 8) |
86 | #define SUNXI_TIME_SET_HOUR_VALUE(x) SUNXI_SET(x, SUNXI_MASK_DH, 16) |
87 | |
88 | /* |
89 | * Set alarm values |
90 | */ |
91 | #define SUNXI_ALRM_SET_SEC_VALUE(x) SUNXI_ALRM_GET_SEC_VALUE(x) |
92 | #define SUNXI_ALRM_SET_MIN_VALUE(x) SUNXI_SET(x, SUNXI_MASK_SM, 8) |
93 | #define SUNXI_ALRM_SET_HOUR_VALUE(x) SUNXI_SET(x, SUNXI_MASK_DH, 16) |
94 | #define SUNXI_ALRM_SET_DAY_VALUE(x) SUNXI_SET(x, SUNXI_MASK_D, 21) |
95 | |
96 | /* |
97 | * Time unit conversions |
98 | */ |
99 | #define SEC_IN_MIN 60 |
100 | #define SEC_IN_HOUR (60 * SEC_IN_MIN) |
101 | #define SEC_IN_DAY (24 * SEC_IN_HOUR) |
102 | |
103 | /* |
104 | * The year parameter passed to the driver is usually an offset relative to |
105 | * the year 1900. This macro is used to convert this offset to another one |
106 | * relative to the minimum year allowed by the hardware. |
107 | */ |
108 | #define SUNXI_YEAR_OFF(x) ((x)->min - 1900) |
109 | |
110 | /* |
111 | * min and max year are arbitrary set considering the limited range of the |
112 | * hardware register field |
113 | */ |
114 | struct sunxi_rtc_data_year { |
115 | unsigned int min; /* min year allowed */ |
116 | unsigned int max; /* max year allowed */ |
117 | unsigned int mask; /* mask for the year field */ |
118 | unsigned char leap_shift; /* bit shift to get the leap year */ |
119 | }; |
120 | |
121 | static const struct sunxi_rtc_data_year data_year_param[] = { |
122 | [0] = { |
123 | .min = 2010, |
124 | .max = 2073, |
125 | .mask = 0x3f, |
126 | .leap_shift = 22, |
127 | }, |
128 | [1] = { |
129 | .min = 1970, |
130 | .max = 2225, |
131 | .mask = 0xff, |
132 | .leap_shift = 24, |
133 | }, |
134 | }; |
135 | |
136 | struct sunxi_rtc_dev { |
137 | struct rtc_device *rtc; |
138 | struct device *dev; |
139 | const struct sunxi_rtc_data_year *data_year; |
140 | void __iomem *base; |
141 | int irq; |
142 | }; |
143 | |
144 | static irqreturn_t sunxi_rtc_alarmirq(int irq, void *id) |
145 | { |
146 | struct sunxi_rtc_dev *chip = (struct sunxi_rtc_dev *) id; |
147 | u32 val; |
148 | |
149 | val = readl(addr: chip->base + SUNXI_ALRM_IRQ_STA); |
150 | |
151 | if (val & SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND) { |
152 | val |= SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND; |
153 | writel(val, addr: chip->base + SUNXI_ALRM_IRQ_STA); |
154 | |
155 | rtc_update_irq(rtc: chip->rtc, num: 1, RTC_AF | RTC_IRQF); |
156 | |
157 | return IRQ_HANDLED; |
158 | } |
159 | |
160 | return IRQ_NONE; |
161 | } |
162 | |
163 | static void sunxi_rtc_setaie(unsigned int to, struct sunxi_rtc_dev *chip) |
164 | { |
165 | u32 alrm_val = 0; |
166 | u32 alrm_irq_val = 0; |
167 | |
168 | if (to) { |
169 | alrm_val = readl(addr: chip->base + SUNXI_ALRM_EN); |
170 | alrm_val |= SUNXI_ALRM_EN_CNT_EN; |
171 | |
172 | alrm_irq_val = readl(addr: chip->base + SUNXI_ALRM_IRQ_EN); |
173 | alrm_irq_val |= SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN; |
174 | } else { |
175 | writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND, |
176 | addr: chip->base + SUNXI_ALRM_IRQ_STA); |
177 | } |
178 | |
179 | writel(val: alrm_val, addr: chip->base + SUNXI_ALRM_EN); |
180 | writel(val: alrm_irq_val, addr: chip->base + SUNXI_ALRM_IRQ_EN); |
181 | } |
182 | |
183 | static int sunxi_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) |
184 | { |
185 | struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); |
186 | struct rtc_time *alrm_tm = &wkalrm->time; |
187 | u32 alrm; |
188 | u32 alrm_en; |
189 | u32 date; |
190 | |
191 | alrm = readl(addr: chip->base + SUNXI_ALRM_DHMS); |
192 | date = readl(addr: chip->base + SUNXI_RTC_YMD); |
193 | |
194 | alrm_tm->tm_sec = SUNXI_ALRM_GET_SEC_VALUE(alrm); |
195 | alrm_tm->tm_min = SUNXI_ALRM_GET_MIN_VALUE(alrm); |
196 | alrm_tm->tm_hour = SUNXI_ALRM_GET_HOUR_VALUE(alrm); |
197 | |
198 | alrm_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date); |
199 | alrm_tm->tm_mon = SUNXI_DATE_GET_MON_VALUE(date); |
200 | alrm_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date, |
201 | chip->data_year->mask); |
202 | |
203 | alrm_tm->tm_mon -= 1; |
204 | |
205 | /* |
206 | * switch from (data_year->min)-relative offset to |
207 | * a (1900)-relative one |
208 | */ |
209 | alrm_tm->tm_year += SUNXI_YEAR_OFF(chip->data_year); |
210 | |
211 | alrm_en = readl(addr: chip->base + SUNXI_ALRM_IRQ_EN); |
212 | if (alrm_en & SUNXI_ALRM_EN_CNT_EN) |
213 | wkalrm->enabled = 1; |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | static int sunxi_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) |
219 | { |
220 | struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); |
221 | u32 date, time; |
222 | |
223 | /* |
224 | * read again in case it changes |
225 | */ |
226 | do { |
227 | date = readl(addr: chip->base + SUNXI_RTC_YMD); |
228 | time = readl(addr: chip->base + SUNXI_RTC_HMS); |
229 | } while ((date != readl(addr: chip->base + SUNXI_RTC_YMD)) || |
230 | (time != readl(addr: chip->base + SUNXI_RTC_HMS))); |
231 | |
232 | rtc_tm->tm_sec = SUNXI_TIME_GET_SEC_VALUE(time); |
233 | rtc_tm->tm_min = SUNXI_TIME_GET_MIN_VALUE(time); |
234 | rtc_tm->tm_hour = SUNXI_TIME_GET_HOUR_VALUE(time); |
235 | |
236 | rtc_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date); |
237 | rtc_tm->tm_mon = SUNXI_DATE_GET_MON_VALUE(date); |
238 | rtc_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date, |
239 | chip->data_year->mask); |
240 | |
241 | rtc_tm->tm_mon -= 1; |
242 | |
243 | /* |
244 | * switch from (data_year->min)-relative offset to |
245 | * a (1900)-relative one |
246 | */ |
247 | rtc_tm->tm_year += SUNXI_YEAR_OFF(chip->data_year); |
248 | |
249 | return 0; |
250 | } |
251 | |
252 | static int sunxi_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) |
253 | { |
254 | struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); |
255 | struct rtc_time *alrm_tm = &wkalrm->time; |
256 | struct rtc_time tm_now; |
257 | u32 alrm; |
258 | time64_t diff; |
259 | unsigned long time_gap; |
260 | unsigned long time_gap_day; |
261 | unsigned long time_gap_hour; |
262 | unsigned long time_gap_min; |
263 | int ret; |
264 | |
265 | ret = sunxi_rtc_gettime(dev, rtc_tm: &tm_now); |
266 | if (ret < 0) { |
267 | dev_err(dev, "Error in getting time\n" ); |
268 | return -EINVAL; |
269 | } |
270 | |
271 | diff = rtc_tm_sub(lhs: alrm_tm, rhs: &tm_now); |
272 | if (diff <= 0) { |
273 | dev_err(dev, "Date to set in the past\n" ); |
274 | return -EINVAL; |
275 | } |
276 | |
277 | if (diff > 255 * SEC_IN_DAY) { |
278 | dev_err(dev, "Day must be in the range 0 - 255\n" ); |
279 | return -EINVAL; |
280 | } |
281 | |
282 | time_gap = diff; |
283 | time_gap_day = time_gap / SEC_IN_DAY; |
284 | time_gap -= time_gap_day * SEC_IN_DAY; |
285 | time_gap_hour = time_gap / SEC_IN_HOUR; |
286 | time_gap -= time_gap_hour * SEC_IN_HOUR; |
287 | time_gap_min = time_gap / SEC_IN_MIN; |
288 | time_gap -= time_gap_min * SEC_IN_MIN; |
289 | |
290 | sunxi_rtc_setaie(to: 0, chip); |
291 | writel(val: 0, addr: chip->base + SUNXI_ALRM_DHMS); |
292 | usleep_range(min: 100, max: 300); |
293 | |
294 | alrm = SUNXI_ALRM_SET_SEC_VALUE(time_gap) | |
295 | SUNXI_ALRM_SET_MIN_VALUE(time_gap_min) | |
296 | SUNXI_ALRM_SET_HOUR_VALUE(time_gap_hour) | |
297 | SUNXI_ALRM_SET_DAY_VALUE(time_gap_day); |
298 | writel(val: alrm, addr: chip->base + SUNXI_ALRM_DHMS); |
299 | |
300 | writel(val: 0, addr: chip->base + SUNXI_ALRM_IRQ_EN); |
301 | writel(SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN, addr: chip->base + SUNXI_ALRM_IRQ_EN); |
302 | |
303 | sunxi_rtc_setaie(to: wkalrm->enabled, chip); |
304 | |
305 | return 0; |
306 | } |
307 | |
308 | static int sunxi_rtc_wait(struct sunxi_rtc_dev *chip, int offset, |
309 | unsigned int mask, unsigned int ms_timeout) |
310 | { |
311 | const unsigned long timeout = jiffies + msecs_to_jiffies(m: ms_timeout); |
312 | u32 reg; |
313 | |
314 | do { |
315 | reg = readl(addr: chip->base + offset); |
316 | reg &= mask; |
317 | |
318 | if (reg == mask) |
319 | return 0; |
320 | |
321 | } while (time_before(jiffies, timeout)); |
322 | |
323 | return -ETIMEDOUT; |
324 | } |
325 | |
326 | static int sunxi_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) |
327 | { |
328 | struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); |
329 | u32 date = 0; |
330 | u32 time = 0; |
331 | unsigned int year; |
332 | |
333 | /* |
334 | * the input rtc_tm->tm_year is the offset relative to 1900. We use |
335 | * the SUNXI_YEAR_OFF macro to rebase it with respect to the min year |
336 | * allowed by the hardware |
337 | */ |
338 | |
339 | year = rtc_tm->tm_year + 1900; |
340 | if (year < chip->data_year->min || year > chip->data_year->max) { |
341 | dev_err(dev, "rtc only supports year in range %u - %u\n" , |
342 | chip->data_year->min, chip->data_year->max); |
343 | return -EINVAL; |
344 | } |
345 | |
346 | rtc_tm->tm_year -= SUNXI_YEAR_OFF(chip->data_year); |
347 | rtc_tm->tm_mon += 1; |
348 | |
349 | date = SUNXI_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | |
350 | SUNXI_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | |
351 | SUNXI_DATE_SET_YEAR_VALUE(rtc_tm->tm_year, |
352 | chip->data_year->mask); |
353 | |
354 | if (is_leap_year(year)) |
355 | date |= SUNXI_LEAP_SET_VALUE(1, chip->data_year->leap_shift); |
356 | |
357 | time = SUNXI_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | |
358 | SUNXI_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | |
359 | SUNXI_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour); |
360 | |
361 | writel(val: 0, addr: chip->base + SUNXI_RTC_HMS); |
362 | writel(val: 0, addr: chip->base + SUNXI_RTC_YMD); |
363 | |
364 | writel(val: time, addr: chip->base + SUNXI_RTC_HMS); |
365 | |
366 | /* |
367 | * After writing the RTC HH-MM-SS register, the |
368 | * SUNXI_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not |
369 | * be cleared until the real writing operation is finished |
370 | */ |
371 | |
372 | if (sunxi_rtc_wait(chip, SUNXI_LOSC_CTRL, |
373 | SUNXI_LOSC_CTRL_RTC_HMS_ACC, ms_timeout: 50)) { |
374 | dev_err(dev, "Failed to set rtc time.\n" ); |
375 | return -1; |
376 | } |
377 | |
378 | writel(val: date, addr: chip->base + SUNXI_RTC_YMD); |
379 | |
380 | /* |
381 | * After writing the RTC YY-MM-DD register, the |
382 | * SUNXI_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not |
383 | * be cleared until the real writing operation is finished |
384 | */ |
385 | |
386 | if (sunxi_rtc_wait(chip, SUNXI_LOSC_CTRL, |
387 | SUNXI_LOSC_CTRL_RTC_YMD_ACC, ms_timeout: 50)) { |
388 | dev_err(dev, "Failed to set rtc time.\n" ); |
389 | return -1; |
390 | } |
391 | |
392 | return 0; |
393 | } |
394 | |
395 | static int sunxi_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
396 | { |
397 | struct sunxi_rtc_dev *chip = dev_get_drvdata(dev); |
398 | |
399 | if (!enabled) |
400 | sunxi_rtc_setaie(to: enabled, chip); |
401 | |
402 | return 0; |
403 | } |
404 | |
405 | static const struct rtc_class_ops sunxi_rtc_ops = { |
406 | .read_time = sunxi_rtc_gettime, |
407 | .set_time = sunxi_rtc_settime, |
408 | .read_alarm = sunxi_rtc_getalarm, |
409 | .set_alarm = sunxi_rtc_setalarm, |
410 | .alarm_irq_enable = sunxi_rtc_alarm_irq_enable |
411 | }; |
412 | |
413 | static const struct of_device_id sunxi_rtc_dt_ids[] = { |
414 | { .compatible = "allwinner,sun4i-a10-rtc" , .data = &data_year_param[0] }, |
415 | { .compatible = "allwinner,sun7i-a20-rtc" , .data = &data_year_param[1] }, |
416 | { /* sentinel */ }, |
417 | }; |
418 | MODULE_DEVICE_TABLE(of, sunxi_rtc_dt_ids); |
419 | |
420 | static int sunxi_rtc_probe(struct platform_device *pdev) |
421 | { |
422 | struct sunxi_rtc_dev *chip; |
423 | int ret; |
424 | |
425 | chip = devm_kzalloc(dev: &pdev->dev, size: sizeof(*chip), GFP_KERNEL); |
426 | if (!chip) |
427 | return -ENOMEM; |
428 | |
429 | platform_set_drvdata(pdev, data: chip); |
430 | chip->dev = &pdev->dev; |
431 | |
432 | chip->rtc = devm_rtc_allocate_device(dev: &pdev->dev); |
433 | if (IS_ERR(ptr: chip->rtc)) |
434 | return PTR_ERR(ptr: chip->rtc); |
435 | |
436 | chip->base = devm_platform_ioremap_resource(pdev, index: 0); |
437 | if (IS_ERR(ptr: chip->base)) |
438 | return PTR_ERR(ptr: chip->base); |
439 | |
440 | chip->irq = platform_get_irq(pdev, 0); |
441 | if (chip->irq < 0) |
442 | return chip->irq; |
443 | ret = devm_request_irq(dev: &pdev->dev, irq: chip->irq, handler: sunxi_rtc_alarmirq, |
444 | irqflags: 0, devname: dev_name(dev: &pdev->dev), dev_id: chip); |
445 | if (ret) { |
446 | dev_err(&pdev->dev, "Could not request IRQ\n" ); |
447 | return ret; |
448 | } |
449 | |
450 | chip->data_year = of_device_get_match_data(dev: &pdev->dev); |
451 | if (!chip->data_year) { |
452 | dev_err(&pdev->dev, "Unable to setup RTC data\n" ); |
453 | return -ENODEV; |
454 | } |
455 | |
456 | /* clear the alarm count value */ |
457 | writel(val: 0, addr: chip->base + SUNXI_ALRM_DHMS); |
458 | |
459 | /* disable alarm, not generate irq pending */ |
460 | writel(val: 0, addr: chip->base + SUNXI_ALRM_EN); |
461 | |
462 | /* disable alarm week/cnt irq, unset to cpu */ |
463 | writel(val: 0, addr: chip->base + SUNXI_ALRM_IRQ_EN); |
464 | |
465 | /* clear alarm week/cnt irq pending */ |
466 | writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND, addr: chip->base + |
467 | SUNXI_ALRM_IRQ_STA); |
468 | |
469 | chip->rtc->ops = &sunxi_rtc_ops; |
470 | |
471 | return devm_rtc_register_device(chip->rtc); |
472 | } |
473 | |
474 | static struct platform_driver sunxi_rtc_driver = { |
475 | .probe = sunxi_rtc_probe, |
476 | .driver = { |
477 | .name = "sunxi-rtc" , |
478 | .of_match_table = sunxi_rtc_dt_ids, |
479 | }, |
480 | }; |
481 | |
482 | module_platform_driver(sunxi_rtc_driver); |
483 | |
484 | MODULE_DESCRIPTION("sunxi RTC driver" ); |
485 | MODULE_AUTHOR("Carlo Caione <carlo.caione@gmail.com>" ); |
486 | MODULE_LICENSE("GPL" ); |
487 | |