Commit | Line | Data |
---|---|---|
4cdf854f DB |
1 | /* |
2 | * "RTT as Real Time Clock" driver for AT91SAM9 SoC family | |
3 | * | |
4 | * (C) 2007 Michel Benoit | |
5 | * | |
6 | * Based on rtc-at91rm9200.c by Rick Bronson | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * as published by the Free Software Foundation; either version | |
11 | * 2 of the License, or (at your option) any later version. | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/time.h> | |
18 | #include <linux/rtc.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/ioctl.h> | |
5a0e3ad6 | 21 | #include <linux/slab.h> |
4cdf854f | 22 | |
a09e64fb RK |
23 | #include <mach/board.h> |
24 | #include <mach/at91_rtt.h> | |
7be90a6b | 25 | #include <mach/cpu.h> |
4cdf854f DB |
26 | |
27 | ||
28 | /* | |
29 | * This driver uses two configurable hardware resources that live in the | |
30 | * AT91SAM9 backup power domain (intended to be powered at all times) | |
31 | * to implement the Real Time Clock interfaces | |
32 | * | |
33 | * - A "Real-time Timer" (RTT) counts up in seconds from a base time. | |
34 | * We can't assign the counter value (CRTV) ... but we can reset it. | |
35 | * | |
36 | * - One of the "General Purpose Backup Registers" (GPBRs) holds the | |
37 | * base time, normally an offset from the beginning of the POSIX | |
38 | * epoch (1970-Jan-1 00:00:00 UTC). Some systems also include the | |
39 | * local timezone's offset. | |
40 | * | |
41 | * The RTC's value is the RTT counter plus that offset. The RTC's alarm | |
42 | * is likewise a base (ALMV) plus that offset. | |
43 | * | |
44 | * Not all RTTs will be used as RTCs; some systems have multiple RTTs to | |
45 | * choose from, or a "real" RTC module. All systems have multiple GPBR | |
46 | * registers available, likewise usable for more than "RTC" support. | |
47 | */ | |
48 | ||
49 | /* | |
50 | * We store ALARM_DISABLED in ALMV to record that no alarm is set. | |
51 | * It's also the reset value for that field. | |
52 | */ | |
53 | #define ALARM_DISABLED ((u32)~0) | |
54 | ||
55 | ||
56 | struct sam9_rtc { | |
57 | void __iomem *rtt; | |
58 | struct rtc_device *rtcdev; | |
59 | u32 imr; | |
b3af8b49 | 60 | void __iomem *gpbr; |
4cdf854f DB |
61 | }; |
62 | ||
63 | #define rtt_readl(rtc, field) \ | |
64 | __raw_readl((rtc)->rtt + AT91_RTT_ ## field) | |
65 | #define rtt_writel(rtc, field, val) \ | |
66 | __raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field) | |
67 | ||
68 | #define gpbr_readl(rtc) \ | |
b3af8b49 | 69 | __raw_readl((rtc)->gpbr) |
4cdf854f | 70 | #define gpbr_writel(rtc, val) \ |
b3af8b49 | 71 | __raw_writel((val), (rtc)->gpbr) |
4cdf854f DB |
72 | |
73 | /* | |
74 | * Read current time and date in RTC | |
75 | */ | |
76 | static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm) | |
77 | { | |
78 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | |
79 | u32 secs, secs2; | |
80 | u32 offset; | |
81 | ||
82 | /* read current time offset */ | |
83 | offset = gpbr_readl(rtc); | |
84 | if (offset == 0) | |
85 | return -EILSEQ; | |
86 | ||
87 | /* reread the counter to help sync the two clock domains */ | |
88 | secs = rtt_readl(rtc, VR); | |
89 | secs2 = rtt_readl(rtc, VR); | |
90 | if (secs != secs2) | |
91 | secs = rtt_readl(rtc, VR); | |
92 | ||
93 | rtc_time_to_tm(offset + secs, tm); | |
94 | ||
95 | dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readtime", | |
96 | 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, | |
97 | tm->tm_hour, tm->tm_min, tm->tm_sec); | |
98 | ||
99 | return 0; | |
100 | } | |
101 | ||
102 | /* | |
103 | * Set current time and date in RTC | |
104 | */ | |
105 | static int at91_rtc_settime(struct device *dev, struct rtc_time *tm) | |
106 | { | |
107 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | |
108 | int err; | |
109 | u32 offset, alarm, mr; | |
110 | unsigned long secs; | |
111 | ||
112 | dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "settime", | |
113 | 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, | |
114 | tm->tm_hour, tm->tm_min, tm->tm_sec); | |
115 | ||
116 | err = rtc_tm_to_time(tm, &secs); | |
117 | if (err != 0) | |
118 | return err; | |
119 | ||
120 | mr = rtt_readl(rtc, MR); | |
121 | ||
122 | /* disable interrupts */ | |
123 | rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); | |
124 | ||
125 | /* read current time offset */ | |
126 | offset = gpbr_readl(rtc); | |
127 | ||
128 | /* store the new base time in a battery backup register */ | |
129 | secs += 1; | |
130 | gpbr_writel(rtc, secs); | |
131 | ||
132 | /* adjust the alarm time for the new base */ | |
133 | alarm = rtt_readl(rtc, AR); | |
134 | if (alarm != ALARM_DISABLED) { | |
135 | if (offset > secs) { | |
136 | /* time jumped backwards, increase time until alarm */ | |
137 | alarm += (offset - secs); | |
138 | } else if ((alarm + offset) > secs) { | |
139 | /* time jumped forwards, decrease time until alarm */ | |
140 | alarm -= (secs - offset); | |
141 | } else { | |
142 | /* time jumped past the alarm, disable alarm */ | |
143 | alarm = ALARM_DISABLED; | |
144 | mr &= ~AT91_RTT_ALMIEN; | |
145 | } | |
146 | rtt_writel(rtc, AR, alarm); | |
147 | } | |
148 | ||
149 | /* reset the timer, and re-enable interrupts */ | |
150 | rtt_writel(rtc, MR, mr | AT91_RTT_RTTRST); | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) | |
156 | { | |
157 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | |
158 | struct rtc_time *tm = &alrm->time; | |
159 | u32 alarm = rtt_readl(rtc, AR); | |
160 | u32 offset; | |
161 | ||
162 | offset = gpbr_readl(rtc); | |
163 | if (offset == 0) | |
164 | return -EILSEQ; | |
165 | ||
870a2761 | 166 | memset(alrm, 0, sizeof(*alrm)); |
4cdf854f DB |
167 | if (alarm != ALARM_DISABLED && offset != 0) { |
168 | rtc_time_to_tm(offset + alarm, tm); | |
169 | ||
170 | dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readalarm", | |
171 | 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, | |
172 | tm->tm_hour, tm->tm_min, tm->tm_sec); | |
173 | ||
174 | if (rtt_readl(rtc, MR) & AT91_RTT_ALMIEN) | |
175 | alrm->enabled = 1; | |
176 | } | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | |
182 | { | |
183 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | |
184 | struct rtc_time *tm = &alrm->time; | |
185 | unsigned long secs; | |
186 | u32 offset; | |
187 | u32 mr; | |
188 | int err; | |
189 | ||
190 | err = rtc_tm_to_time(tm, &secs); | |
191 | if (err != 0) | |
192 | return err; | |
193 | ||
194 | offset = gpbr_readl(rtc); | |
195 | if (offset == 0) { | |
196 | /* time is not set */ | |
197 | return -EILSEQ; | |
198 | } | |
199 | mr = rtt_readl(rtc, MR); | |
200 | rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN); | |
201 | ||
202 | /* alarm in the past? finish and leave disabled */ | |
203 | if (secs <= offset) { | |
204 | rtt_writel(rtc, AR, ALARM_DISABLED); | |
205 | return 0; | |
206 | } | |
207 | ||
208 | /* else set alarm and maybe enable it */ | |
209 | rtt_writel(rtc, AR, secs - offset); | |
210 | if (alrm->enabled) | |
211 | rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN); | |
212 | ||
213 | dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "setalarm", | |
214 | tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, | |
215 | tm->tm_min, tm->tm_sec); | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
16380c15 JS |
220 | static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
221 | { | |
222 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | |
223 | u32 mr = rtt_readl(rtc, MR); | |
224 | ||
225 | dev_dbg(dev, "alarm_irq_enable: enabled=%08x, mr %08x\n", enabled, mr); | |
226 | if (enabled) | |
227 | rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN); | |
228 | else | |
229 | rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN); | |
230 | return 0; | |
231 | } | |
232 | ||
4cdf854f DB |
233 | /* |
234 | * Provide additional RTC information in /proc/driver/rtc | |
235 | */ | |
236 | static int at91_rtc_proc(struct device *dev, struct seq_file *seq) | |
237 | { | |
238 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | |
239 | u32 mr = mr = rtt_readl(rtc, MR); | |
240 | ||
241 | seq_printf(seq, "update_IRQ\t: %s\n", | |
242 | (mr & AT91_RTT_RTTINCIEN) ? "yes" : "no"); | |
243 | return 0; | |
244 | } | |
245 | ||
246 | /* | |
247 | * IRQ handler for the RTC | |
248 | */ | |
249 | static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) | |
250 | { | |
251 | struct sam9_rtc *rtc = _rtc; | |
252 | u32 sr, mr; | |
253 | unsigned long events = 0; | |
254 | ||
255 | /* Shared interrupt may be for another device. Note: reading | |
256 | * SR clears it, so we must only read it in this irq handler! | |
257 | */ | |
258 | mr = rtt_readl(rtc, MR) & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); | |
9fedc9f1 | 259 | sr = rtt_readl(rtc, SR) & (mr >> 16); |
4cdf854f DB |
260 | if (!sr) |
261 | return IRQ_NONE; | |
262 | ||
263 | /* alarm status */ | |
264 | if (sr & AT91_RTT_ALMS) | |
265 | events |= (RTC_AF | RTC_IRQF); | |
266 | ||
267 | /* timer update/increment */ | |
268 | if (sr & AT91_RTT_RTTINC) | |
269 | events |= (RTC_UF | RTC_IRQF); | |
270 | ||
271 | rtc_update_irq(rtc->rtcdev, 1, events); | |
272 | ||
2a4e2b87 | 273 | pr_debug("%s: num=%ld, events=0x%02lx\n", __func__, |
4cdf854f DB |
274 | events >> 8, events & 0x000000FF); |
275 | ||
276 | return IRQ_HANDLED; | |
277 | } | |
278 | ||
279 | static const struct rtc_class_ops at91_rtc_ops = { | |
4cdf854f DB |
280 | .read_time = at91_rtc_readtime, |
281 | .set_time = at91_rtc_settime, | |
282 | .read_alarm = at91_rtc_readalarm, | |
283 | .set_alarm = at91_rtc_setalarm, | |
284 | .proc = at91_rtc_proc, | |
d4035850 | 285 | .alarm_irq_enable = at91_rtc_alarm_irq_enable, |
4cdf854f DB |
286 | }; |
287 | ||
288 | /* | |
289 | * Initialize and install RTC driver | |
290 | */ | |
205056a3 | 291 | static int __devinit at91_rtc_probe(struct platform_device *pdev) |
4cdf854f | 292 | { |
b3af8b49 | 293 | struct resource *r, *r_gpbr; |
4cdf854f DB |
294 | struct sam9_rtc *rtc; |
295 | int ret; | |
296 | u32 mr; | |
297 | ||
298 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
b3af8b49 JCPV |
299 | r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
300 | if (!r || !r_gpbr) { | |
301 | dev_err(&pdev->dev, "need 2 ressources\n"); | |
4cdf854f | 302 | return -ENODEV; |
b3af8b49 | 303 | } |
4cdf854f DB |
304 | |
305 | rtc = kzalloc(sizeof *rtc, GFP_KERNEL); | |
306 | if (!rtc) | |
307 | return -ENOMEM; | |
308 | ||
9fedc9f1 DB |
309 | /* platform setup code should have handled this; sigh */ |
310 | if (!device_can_wakeup(&pdev->dev)) | |
311 | device_init_wakeup(&pdev->dev, 1); | |
312 | ||
4cdf854f | 313 | platform_set_drvdata(pdev, rtc); |
2dcc90e6 JCPV |
314 | rtc->rtt = ioremap(r->start, resource_size(r)); |
315 | if (!rtc->rtt) { | |
316 | dev_err(&pdev->dev, "failed to map registers, aborting.\n"); | |
317 | ret = -ENOMEM; | |
318 | goto fail; | |
319 | } | |
4cdf854f | 320 | |
b3af8b49 JCPV |
321 | rtc->gpbr = ioremap(r_gpbr->start, resource_size(r_gpbr)); |
322 | if (!rtc->gpbr) { | |
323 | dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n"); | |
324 | ret = -ENOMEM; | |
325 | goto fail_gpbr; | |
326 | } | |
327 | ||
4cdf854f DB |
328 | mr = rtt_readl(rtc, MR); |
329 | ||
330 | /* unless RTT is counting at 1 Hz, re-initialize it */ | |
331 | if ((mr & AT91_RTT_RTPRES) != AT91_SLOW_CLOCK) { | |
332 | mr = AT91_RTT_RTTRST | (AT91_SLOW_CLOCK & AT91_RTT_RTPRES); | |
333 | gpbr_writel(rtc, 0); | |
334 | } | |
335 | ||
336 | /* disable all interrupts (same as on shutdown path) */ | |
337 | mr &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); | |
338 | rtt_writel(rtc, MR, mr); | |
339 | ||
340 | rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev, | |
341 | &at91_rtc_ops, THIS_MODULE); | |
342 | if (IS_ERR(rtc->rtcdev)) { | |
343 | ret = PTR_ERR(rtc->rtcdev); | |
2dcc90e6 | 344 | goto fail_register; |
4cdf854f DB |
345 | } |
346 | ||
347 | /* register irq handler after we know what name we'll use */ | |
348 | ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, | |
2f6e5f94 | 349 | IRQF_SHARED, |
744bcb13 | 350 | dev_name(&rtc->rtcdev->dev), rtc); |
4cdf854f DB |
351 | if (ret) { |
352 | dev_dbg(&pdev->dev, "can't share IRQ %d?\n", AT91_ID_SYS); | |
353 | rtc_device_unregister(rtc->rtcdev); | |
b3af8b49 | 354 | goto fail_register; |
4cdf854f DB |
355 | } |
356 | ||
357 | /* NOTE: sam9260 rev A silicon has a ROM bug which resets the | |
358 | * RTT on at least some reboots. If you have that chip, you must | |
359 | * initialize the time from some external source like a GPS, wall | |
360 | * clock, discrete RTC, etc | |
361 | */ | |
362 | ||
363 | if (gpbr_readl(rtc) == 0) | |
364 | dev_warn(&pdev->dev, "%s: SET TIME!\n", | |
744bcb13 | 365 | dev_name(&rtc->rtcdev->dev)); |
4cdf854f DB |
366 | |
367 | return 0; | |
368 | ||
2dcc90e6 | 369 | fail_register: |
b3af8b49 JCPV |
370 | iounmap(rtc->gpbr); |
371 | fail_gpbr: | |
2dcc90e6 | 372 | iounmap(rtc->rtt); |
4cdf854f DB |
373 | fail: |
374 | platform_set_drvdata(pdev, NULL); | |
375 | kfree(rtc); | |
376 | return ret; | |
377 | } | |
378 | ||
379 | /* | |
380 | * Disable and remove the RTC driver | |
381 | */ | |
205056a3 | 382 | static int __devexit at91_rtc_remove(struct platform_device *pdev) |
4cdf854f DB |
383 | { |
384 | struct sam9_rtc *rtc = platform_get_drvdata(pdev); | |
385 | u32 mr = rtt_readl(rtc, MR); | |
386 | ||
387 | /* disable all interrupts */ | |
388 | rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); | |
389 | free_irq(AT91_ID_SYS, rtc); | |
390 | ||
391 | rtc_device_unregister(rtc->rtcdev); | |
392 | ||
b3af8b49 | 393 | iounmap(rtc->gpbr); |
2dcc90e6 | 394 | iounmap(rtc->rtt); |
4cdf854f DB |
395 | platform_set_drvdata(pdev, NULL); |
396 | kfree(rtc); | |
397 | return 0; | |
398 | } | |
399 | ||
400 | static void at91_rtc_shutdown(struct platform_device *pdev) | |
401 | { | |
402 | struct sam9_rtc *rtc = platform_get_drvdata(pdev); | |
403 | u32 mr = rtt_readl(rtc, MR); | |
404 | ||
405 | rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); | |
406 | rtt_writel(rtc, MR, mr & ~rtc->imr); | |
407 | } | |
408 | ||
409 | #ifdef CONFIG_PM | |
410 | ||
411 | /* AT91SAM9 RTC Power management control */ | |
412 | ||
413 | static int at91_rtc_suspend(struct platform_device *pdev, | |
414 | pm_message_t state) | |
415 | { | |
416 | struct sam9_rtc *rtc = platform_get_drvdata(pdev); | |
417 | u32 mr = rtt_readl(rtc, MR); | |
418 | ||
419 | /* | |
420 | * This IRQ is shared with DBGU and other hardware which isn't | |
421 | * necessarily a wakeup event source. | |
422 | */ | |
423 | rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); | |
424 | if (rtc->imr) { | |
425 | if (device_may_wakeup(&pdev->dev) && (mr & AT91_RTT_ALMIEN)) { | |
426 | enable_irq_wake(AT91_ID_SYS); | |
427 | /* don't let RTTINC cause wakeups */ | |
428 | if (mr & AT91_RTT_RTTINCIEN) | |
429 | rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN); | |
430 | } else | |
431 | rtt_writel(rtc, MR, mr & ~rtc->imr); | |
432 | } | |
433 | ||
434 | return 0; | |
435 | } | |
436 | ||
437 | static int at91_rtc_resume(struct platform_device *pdev) | |
438 | { | |
439 | struct sam9_rtc *rtc = platform_get_drvdata(pdev); | |
440 | u32 mr; | |
441 | ||
442 | if (rtc->imr) { | |
443 | if (device_may_wakeup(&pdev->dev)) | |
444 | disable_irq_wake(AT91_ID_SYS); | |
445 | mr = rtt_readl(rtc, MR); | |
446 | rtt_writel(rtc, MR, mr | rtc->imr); | |
447 | } | |
448 | ||
449 | return 0; | |
450 | } | |
451 | #else | |
452 | #define at91_rtc_suspend NULL | |
453 | #define at91_rtc_resume NULL | |
454 | #endif | |
455 | ||
456 | static struct platform_driver at91_rtc_driver = { | |
205056a3 JCPV |
457 | .probe = at91_rtc_probe, |
458 | .remove = __devexit_p(at91_rtc_remove), | |
4cdf854f DB |
459 | .shutdown = at91_rtc_shutdown, |
460 | .suspend = at91_rtc_suspend, | |
461 | .resume = at91_rtc_resume, | |
205056a3 JCPV |
462 | .driver = { |
463 | .name = "rtc-at91sam9", | |
464 | .owner = THIS_MODULE, | |
465 | }, | |
4cdf854f DB |
466 | }; |
467 | ||
4cdf854f DB |
468 | static int __init at91_rtc_init(void) |
469 | { | |
205056a3 | 470 | return platform_driver_register(&at91_rtc_driver); |
4cdf854f DB |
471 | } |
472 | module_init(at91_rtc_init); | |
473 | ||
474 | static void __exit at91_rtc_exit(void) | |
475 | { | |
476 | platform_driver_unregister(&at91_rtc_driver); | |
477 | } | |
478 | module_exit(at91_rtc_exit); | |
479 | ||
480 | ||
481 | MODULE_AUTHOR("Michel Benoit"); | |
482 | MODULE_DESCRIPTION("RTC driver for Atmel AT91SAM9x"); | |
483 | MODULE_LICENSE("GPL"); |