diff options
author | David Brownell <david-b@pacbell.net> | 2008-02-06 04:38:59 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-06 13:41:14 -0500 |
commit | 4cdf854f7d60498bbda436068a118b95059b244b (patch) | |
tree | e7d5169176a48a9b5dd980baa42c9083244736e7 /drivers/rtc | |
parent | f618258ad8af0413f08af60bd0eb050562e700fa (diff) |
rtc: at91sam9 RTC support (RTT and/or RTC)
AT91sam9 RTC support, primarily in the form of an RTT-as-RTC driver that was
extracted from 2.6.23-at91 patch and updated:
- Relies on now-merged platform updates, which associate the RTT
hardware address with each RTT and use the "at91_rtt" name.
- RTC framework related fixes and cleanups, notably:
* removed now-needless suspend/resume clock offset logic
* alarm read/write now respects the "enabled" flag
* suspend always disables update irqs
* shutdown (and startup) disables all irqs
- Misc cleanup:
* use dev_*() messaging
* add comments
* remove globals,
* ... etc
- Don't force use of RTT0 and GPBR0. Either resource may need
to be used for other purposes (like NO_HZ support).
- Update "AT91RM9200 RTC" Kconfig to allow it on SAM9RL chips
(it has both RTT and RTC).
Driver binding uses bus_find_device() to avoid needing any kind of "timer
library" code when there's more than one RTT module. (This timer can be used
as an RTC, to support NO_HZ operation, or potentially for other stuff. The
choice is a per-system policy.)
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: Michel Benoit <murpme@gmail.com>
Cc: Nicolas Ferre <nicolas.ferre@rfo.atmel.com>
Cc: Andrew Victor <linux@maxim.org.za>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 45 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91sam9.c | 520 |
3 files changed, 562 insertions, 4 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 484de8e4c5ae..6402d699072b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -469,10 +469,47 @@ config RTC_DRV_AT32AP700X | |||
469 | AT32AP700x family processors. | 469 | AT32AP700x family processors. |
470 | 470 | ||
471 | config RTC_DRV_AT91RM9200 | 471 | config RTC_DRV_AT91RM9200 |
472 | tristate "AT91RM9200" | 472 | tristate "AT91RM9200 or AT91SAM9RL" |
473 | depends on ARCH_AT91RM9200 | 473 | depends on ARCH_AT91RM9200 || ARCH_AT91SAM9RL |
474 | help | 474 | help |
475 | Driver for the Atmel AT91RM9200's internal RTC (Realtime Clock). | 475 | Driver for the internal RTC (Realtime Clock) module found on |
476 | Atmel AT91RM9200's and AT91SAM9RL chips. On SAM9RL chips | ||
477 | this is powered by the backup power supply. | ||
478 | |||
479 | config RTC_DRV_AT91SAM9 | ||
480 | tristate "AT91SAM9x" | ||
481 | depends on ARCH_AT91 && !(ARCH_AT91RM9200 || ARCH_AT91X40) | ||
482 | help | ||
483 | RTC driver for the Atmel AT91SAM9x internal RTT (Real Time Timer). | ||
484 | These timers are powered by the backup power supply (such as a | ||
485 | small coin cell battery), but do not need to be used as RTCs. | ||
486 | |||
487 | (On AT91SAM9rl chips you probably want to use the dedicated RTC | ||
488 | module and leave the RTT available for other uses.) | ||
489 | |||
490 | config RTC_DRV_AT91SAM9_RTT | ||
491 | int | ||
492 | range 0 1 | ||
493 | default 0 | ||
494 | prompt "RTT module Number" if ARCH_AT91SAM9263 | ||
495 | depends on RTC_DRV_AT91SAM9 | ||
496 | help | ||
497 | More than one RTT module is available. You can choose which | ||
498 | one will be used as an RTC. The default of zero is normally | ||
499 | OK to use, though some systems use that for non-RTC purposes. | ||
500 | |||
501 | config RTC_DRV_AT91SAM9_GPBR | ||
502 | int | ||
503 | range 0 3 if !ARCH_AT91SAM9263 | ||
504 | range 0 15 if ARCH_AT91SAM9263 | ||
505 | default 0 | ||
506 | prompt "Backup Register Number" | ||
507 | depends on RTC_DRV_AT91SAM9 | ||
508 | help | ||
509 | The RTC driver needs to use one of the General Purpose Backup | ||
510 | Registers (GPBRs) as well as the RTT. You can choose which one | ||
511 | will be used. The default of zero is normally OK to use, but | ||
512 | on some systems other software needs to use that register. | ||
476 | 513 | ||
477 | config RTC_DRV_BFIN | 514 | config RTC_DRV_BFIN |
478 | tristate "Blackfin On-Chip RTC" | 515 | tristate "Blackfin On-Chip RTC" |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 81f7c9a05a4a..ec703f34ab86 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -19,6 +19,7 @@ rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o | |||
19 | 19 | ||
20 | obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o | 20 | obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o |
21 | obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o | 21 | obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o |
22 | obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o | ||
22 | obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o | 23 | obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o |
23 | obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o | 24 | obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o |
24 | obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o | 25 | obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o |
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c new file mode 100644 index 000000000000..bbf10ecf416c --- /dev/null +++ b/drivers/rtc/rtc-at91sam9.c | |||
@@ -0,0 +1,520 @@ | |||
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> | ||
21 | |||
22 | #include <asm/mach/time.h> | ||
23 | #include <asm/arch/board.h> | ||
24 | #include <asm/arch/at91_rtt.h> | ||
25 | |||
26 | |||
27 | /* | ||
28 | * This driver uses two configurable hardware resources that live in the | ||
29 | * AT91SAM9 backup power domain (intended to be powered at all times) | ||
30 | * to implement the Real Time Clock interfaces | ||
31 | * | ||
32 | * - A "Real-time Timer" (RTT) counts up in seconds from a base time. | ||
33 | * We can't assign the counter value (CRTV) ... but we can reset it. | ||
34 | * | ||
35 | * - One of the "General Purpose Backup Registers" (GPBRs) holds the | ||
36 | * base time, normally an offset from the beginning of the POSIX | ||
37 | * epoch (1970-Jan-1 00:00:00 UTC). Some systems also include the | ||
38 | * local timezone's offset. | ||
39 | * | ||
40 | * The RTC's value is the RTT counter plus that offset. The RTC's alarm | ||
41 | * is likewise a base (ALMV) plus that offset. | ||
42 | * | ||
43 | * Not all RTTs will be used as RTCs; some systems have multiple RTTs to | ||
44 | * choose from, or a "real" RTC module. All systems have multiple GPBR | ||
45 | * registers available, likewise usable for more than "RTC" support. | ||
46 | */ | ||
47 | |||
48 | /* | ||
49 | * We store ALARM_DISABLED in ALMV to record that no alarm is set. | ||
50 | * It's also the reset value for that field. | ||
51 | */ | ||
52 | #define ALARM_DISABLED ((u32)~0) | ||
53 | |||
54 | |||
55 | struct sam9_rtc { | ||
56 | void __iomem *rtt; | ||
57 | struct rtc_device *rtcdev; | ||
58 | u32 imr; | ||
59 | }; | ||
60 | |||
61 | #define rtt_readl(rtc, field) \ | ||
62 | __raw_readl((rtc)->rtt + AT91_RTT_ ## field) | ||
63 | #define rtt_writel(rtc, field, val) \ | ||
64 | __raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field) | ||
65 | |||
66 | #define gpbr_readl(rtc) \ | ||
67 | at91_sys_read(AT91_GPBR + 4 * CONFIG_RTC_DRV_AT91SAM9_GPBR) | ||
68 | #define gpbr_writel(rtc, val) \ | ||
69 | at91_sys_write(AT91_GPBR + 4 * CONFIG_RTC_DRV_AT91SAM9_GPBR, (val)) | ||
70 | |||
71 | /* | ||
72 | * Read current time and date in RTC | ||
73 | */ | ||
74 | static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm) | ||
75 | { | ||
76 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | ||
77 | u32 secs, secs2; | ||
78 | u32 offset; | ||
79 | |||
80 | /* read current time offset */ | ||
81 | offset = gpbr_readl(rtc); | ||
82 | if (offset == 0) | ||
83 | return -EILSEQ; | ||
84 | |||
85 | /* reread the counter to help sync the two clock domains */ | ||
86 | secs = rtt_readl(rtc, VR); | ||
87 | secs2 = rtt_readl(rtc, VR); | ||
88 | if (secs != secs2) | ||
89 | secs = rtt_readl(rtc, VR); | ||
90 | |||
91 | rtc_time_to_tm(offset + secs, tm); | ||
92 | |||
93 | dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readtime", | ||
94 | 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, | ||
95 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * Set current time and date in RTC | ||
102 | */ | ||
103 | static int at91_rtc_settime(struct device *dev, struct rtc_time *tm) | ||
104 | { | ||
105 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | ||
106 | int err; | ||
107 | u32 offset, alarm, mr; | ||
108 | unsigned long secs; | ||
109 | |||
110 | dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "settime", | ||
111 | 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, | ||
112 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
113 | |||
114 | err = rtc_tm_to_time(tm, &secs); | ||
115 | if (err != 0) | ||
116 | return err; | ||
117 | |||
118 | mr = rtt_readl(rtc, MR); | ||
119 | |||
120 | /* disable interrupts */ | ||
121 | rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); | ||
122 | |||
123 | /* read current time offset */ | ||
124 | offset = gpbr_readl(rtc); | ||
125 | |||
126 | /* store the new base time in a battery backup register */ | ||
127 | secs += 1; | ||
128 | gpbr_writel(rtc, secs); | ||
129 | |||
130 | /* adjust the alarm time for the new base */ | ||
131 | alarm = rtt_readl(rtc, AR); | ||
132 | if (alarm != ALARM_DISABLED) { | ||
133 | if (offset > secs) { | ||
134 | /* time jumped backwards, increase time until alarm */ | ||
135 | alarm += (offset - secs); | ||
136 | } else if ((alarm + offset) > secs) { | ||
137 | /* time jumped forwards, decrease time until alarm */ | ||
138 | alarm -= (secs - offset); | ||
139 | } else { | ||
140 | /* time jumped past the alarm, disable alarm */ | ||
141 | alarm = ALARM_DISABLED; | ||
142 | mr &= ~AT91_RTT_ALMIEN; | ||
143 | } | ||
144 | rtt_writel(rtc, AR, alarm); | ||
145 | } | ||
146 | |||
147 | /* reset the timer, and re-enable interrupts */ | ||
148 | rtt_writel(rtc, MR, mr | AT91_RTT_RTTRST); | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
154 | { | ||
155 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | ||
156 | struct rtc_time *tm = &alrm->time; | ||
157 | u32 alarm = rtt_readl(rtc, AR); | ||
158 | u32 offset; | ||
159 | |||
160 | offset = gpbr_readl(rtc); | ||
161 | if (offset == 0) | ||
162 | return -EILSEQ; | ||
163 | |||
164 | memset(alrm, 0, sizeof(alrm)); | ||
165 | if (alarm != ALARM_DISABLED && offset != 0) { | ||
166 | rtc_time_to_tm(offset + alarm, tm); | ||
167 | |||
168 | dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readalarm", | ||
169 | 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, | ||
170 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
171 | |||
172 | if (rtt_readl(rtc, MR) & AT91_RTT_ALMIEN) | ||
173 | alrm->enabled = 1; | ||
174 | } | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
180 | { | ||
181 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | ||
182 | struct rtc_time *tm = &alrm->time; | ||
183 | unsigned long secs; | ||
184 | u32 offset; | ||
185 | u32 mr; | ||
186 | int err; | ||
187 | |||
188 | err = rtc_tm_to_time(tm, &secs); | ||
189 | if (err != 0) | ||
190 | return err; | ||
191 | |||
192 | offset = gpbr_readl(rtc); | ||
193 | if (offset == 0) { | ||
194 | /* time is not set */ | ||
195 | return -EILSEQ; | ||
196 | } | ||
197 | mr = rtt_readl(rtc, MR); | ||
198 | rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN); | ||
199 | |||
200 | /* alarm in the past? finish and leave disabled */ | ||
201 | if (secs <= offset) { | ||
202 | rtt_writel(rtc, AR, ALARM_DISABLED); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | /* else set alarm and maybe enable it */ | ||
207 | rtt_writel(rtc, AR, secs - offset); | ||
208 | if (alrm->enabled) | ||
209 | rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN); | ||
210 | |||
211 | dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "setalarm", | ||
212 | tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, | ||
213 | tm->tm_min, tm->tm_sec); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * Handle commands from user-space | ||
220 | */ | ||
221 | static int at91_rtc_ioctl(struct device *dev, unsigned int cmd, | ||
222 | unsigned long arg) | ||
223 | { | ||
224 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | ||
225 | int ret = 0; | ||
226 | u32 mr = rtt_readl(rtc, MR); | ||
227 | |||
228 | dev_dbg(dev, "ioctl: cmd=%08x, arg=%08lx, mr %08x\n", cmd, arg, mr); | ||
229 | |||
230 | switch (cmd) { | ||
231 | case RTC_AIE_OFF: /* alarm off */ | ||
232 | rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN); | ||
233 | break; | ||
234 | case RTC_AIE_ON: /* alarm on */ | ||
235 | rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN); | ||
236 | break; | ||
237 | case RTC_UIE_OFF: /* update off */ | ||
238 | rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN); | ||
239 | break; | ||
240 | case RTC_UIE_ON: /* update on */ | ||
241 | rtt_writel(rtc, MR, mr | AT91_RTT_RTTINCIEN); | ||
242 | break; | ||
243 | default: | ||
244 | ret = -ENOIOCTLCMD; | ||
245 | break; | ||
246 | } | ||
247 | |||
248 | return ret; | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * Provide additional RTC information in /proc/driver/rtc | ||
253 | */ | ||
254 | static int at91_rtc_proc(struct device *dev, struct seq_file *seq) | ||
255 | { | ||
256 | struct sam9_rtc *rtc = dev_get_drvdata(dev); | ||
257 | u32 mr = mr = rtt_readl(rtc, MR); | ||
258 | |||
259 | seq_printf(seq, "update_IRQ\t: %s\n", | ||
260 | (mr & AT91_RTT_RTTINCIEN) ? "yes" : "no"); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * IRQ handler for the RTC | ||
266 | */ | ||
267 | static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc) | ||
268 | { | ||
269 | struct sam9_rtc *rtc = _rtc; | ||
270 | u32 sr, mr; | ||
271 | unsigned long events = 0; | ||
272 | |||
273 | /* Shared interrupt may be for another device. Note: reading | ||
274 | * SR clears it, so we must only read it in this irq handler! | ||
275 | */ | ||
276 | mr = rtt_readl(rtc, MR) & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); | ||
277 | sr = rtt_readl(rtc, SR) & mr; | ||
278 | if (!sr) | ||
279 | return IRQ_NONE; | ||
280 | |||
281 | /* alarm status */ | ||
282 | if (sr & AT91_RTT_ALMS) | ||
283 | events |= (RTC_AF | RTC_IRQF); | ||
284 | |||
285 | /* timer update/increment */ | ||
286 | if (sr & AT91_RTT_RTTINC) | ||
287 | events |= (RTC_UF | RTC_IRQF); | ||
288 | |||
289 | rtc_update_irq(rtc->rtcdev, 1, events); | ||
290 | |||
291 | pr_debug("%s: num=%ld, events=0x%02lx\n", __FUNCTION__, | ||
292 | events >> 8, events & 0x000000FF); | ||
293 | |||
294 | return IRQ_HANDLED; | ||
295 | } | ||
296 | |||
297 | static const struct rtc_class_ops at91_rtc_ops = { | ||
298 | .ioctl = at91_rtc_ioctl, | ||
299 | .read_time = at91_rtc_readtime, | ||
300 | .set_time = at91_rtc_settime, | ||
301 | .read_alarm = at91_rtc_readalarm, | ||
302 | .set_alarm = at91_rtc_setalarm, | ||
303 | .proc = at91_rtc_proc, | ||
304 | }; | ||
305 | |||
306 | /* | ||
307 | * Initialize and install RTC driver | ||
308 | */ | ||
309 | static int __init at91_rtc_probe(struct platform_device *pdev) | ||
310 | { | ||
311 | struct resource *r; | ||
312 | struct sam9_rtc *rtc; | ||
313 | int ret; | ||
314 | u32 mr; | ||
315 | |||
316 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
317 | if (!r) | ||
318 | return -ENODEV; | ||
319 | |||
320 | rtc = kzalloc(sizeof *rtc, GFP_KERNEL); | ||
321 | if (!rtc) | ||
322 | return -ENOMEM; | ||
323 | |||
324 | platform_set_drvdata(pdev, rtc); | ||
325 | rtc->rtt = (void __force __iomem *) (AT91_VA_BASE_SYS - AT91_BASE_SYS); | ||
326 | rtc->rtt += r->start; | ||
327 | |||
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); | ||
344 | goto fail; | ||
345 | } | ||
346 | |||
347 | /* register irq handler after we know what name we'll use */ | ||
348 | ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, | ||
349 | IRQF_DISABLED | IRQF_SHARED, | ||
350 | rtc->rtcdev->dev.bus_id, rtc); | ||
351 | if (ret) { | ||
352 | dev_dbg(&pdev->dev, "can't share IRQ %d?\n", AT91_ID_SYS); | ||
353 | rtc_device_unregister(rtc->rtcdev); | ||
354 | goto fail; | ||
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", | ||
365 | rtc->rtcdev->dev.bus_id); | ||
366 | |||
367 | return 0; | ||
368 | |||
369 | fail: | ||
370 | platform_set_drvdata(pdev, NULL); | ||
371 | kfree(rtc); | ||
372 | return ret; | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * Disable and remove the RTC driver | ||
377 | */ | ||
378 | static int __exit at91_rtc_remove(struct platform_device *pdev) | ||
379 | { | ||
380 | struct sam9_rtc *rtc = platform_get_drvdata(pdev); | ||
381 | u32 mr = rtt_readl(rtc, MR); | ||
382 | |||
383 | /* disable all interrupts */ | ||
384 | rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); | ||
385 | free_irq(AT91_ID_SYS, rtc); | ||
386 | |||
387 | rtc_device_unregister(rtc->rtcdev); | ||
388 | |||
389 | platform_set_drvdata(pdev, NULL); | ||
390 | kfree(rtc); | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static void at91_rtc_shutdown(struct platform_device *pdev) | ||
395 | { | ||
396 | struct sam9_rtc *rtc = platform_get_drvdata(pdev); | ||
397 | u32 mr = rtt_readl(rtc, MR); | ||
398 | |||
399 | rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); | ||
400 | rtt_writel(rtc, MR, mr & ~rtc->imr); | ||
401 | } | ||
402 | |||
403 | #ifdef CONFIG_PM | ||
404 | |||
405 | /* AT91SAM9 RTC Power management control */ | ||
406 | |||
407 | static int at91_rtc_suspend(struct platform_device *pdev, | ||
408 | pm_message_t state) | ||
409 | { | ||
410 | struct sam9_rtc *rtc = platform_get_drvdata(pdev); | ||
411 | u32 mr = rtt_readl(rtc, MR); | ||
412 | |||
413 | /* | ||
414 | * This IRQ is shared with DBGU and other hardware which isn't | ||
415 | * necessarily a wakeup event source. | ||
416 | */ | ||
417 | rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN); | ||
418 | if (rtc->imr) { | ||
419 | if (device_may_wakeup(&pdev->dev) && (mr & AT91_RTT_ALMIEN)) { | ||
420 | enable_irq_wake(AT91_ID_SYS); | ||
421 | /* don't let RTTINC cause wakeups */ | ||
422 | if (mr & AT91_RTT_RTTINCIEN) | ||
423 | rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN); | ||
424 | } else | ||
425 | rtt_writel(rtc, MR, mr & ~rtc->imr); | ||
426 | } | ||
427 | |||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | static int at91_rtc_resume(struct platform_device *pdev) | ||
432 | { | ||
433 | struct sam9_rtc *rtc = platform_get_drvdata(pdev); | ||
434 | u32 mr; | ||
435 | |||
436 | if (rtc->imr) { | ||
437 | if (device_may_wakeup(&pdev->dev)) | ||
438 | disable_irq_wake(AT91_ID_SYS); | ||
439 | mr = rtt_readl(rtc, MR); | ||
440 | rtt_writel(rtc, MR, mr | rtc->imr); | ||
441 | } | ||
442 | |||
443 | return 0; | ||
444 | } | ||
445 | #else | ||
446 | #define at91_rtc_suspend NULL | ||
447 | #define at91_rtc_resume NULL | ||
448 | #endif | ||
449 | |||
450 | static struct platform_driver at91_rtc_driver = { | ||
451 | .driver.name = "rtc-at91sam9", | ||
452 | .driver.owner = THIS_MODULE, | ||
453 | .remove = __exit_p(at91_rtc_remove), | ||
454 | .shutdown = at91_rtc_shutdown, | ||
455 | .suspend = at91_rtc_suspend, | ||
456 | .resume = at91_rtc_resume, | ||
457 | }; | ||
458 | |||
459 | /* Chips can have more than one RTT module, and they can be used for more | ||
460 | * than just RTCs. So we can't just register as "the" RTT driver. | ||
461 | * | ||
462 | * A normal approach in such cases is to create a library to allocate and | ||
463 | * free the modules. Here we just use bus_find_device() as like such a | ||
464 | * library, binding directly ... no runtime "library" footprint is needed. | ||
465 | */ | ||
466 | static int __init at91_rtc_match(struct device *dev, void *v) | ||
467 | { | ||
468 | struct platform_device *pdev = to_platform_device(dev); | ||
469 | int ret; | ||
470 | |||
471 | /* continue searching if this isn't the RTT we need */ | ||
472 | if (strcmp("at91_rtt", pdev->name) != 0 | ||
473 | || pdev->id != CONFIG_RTC_DRV_AT91SAM9_RTT) | ||
474 | goto fail; | ||
475 | |||
476 | /* else we found it ... but fail unless we can bind to the RTC driver */ | ||
477 | if (dev->driver) { | ||
478 | dev_dbg(dev, "busy, can't use as RTC!\n"); | ||
479 | goto fail; | ||
480 | } | ||
481 | dev->driver = &at91_rtc_driver.driver; | ||
482 | if (device_attach(dev) == 0) { | ||
483 | dev_dbg(dev, "can't attach RTC!\n"); | ||
484 | goto fail; | ||
485 | } | ||
486 | ret = at91_rtc_probe(pdev); | ||
487 | if (ret == 0) | ||
488 | return true; | ||
489 | |||
490 | dev_dbg(dev, "RTC probe err %d!\n", ret); | ||
491 | fail: | ||
492 | return false; | ||
493 | } | ||
494 | |||
495 | static int __init at91_rtc_init(void) | ||
496 | { | ||
497 | int status; | ||
498 | struct device *rtc; | ||
499 | |||
500 | status = platform_driver_register(&at91_rtc_driver); | ||
501 | if (status) | ||
502 | return status; | ||
503 | rtc = bus_find_device(&platform_bus_type, NULL, | ||
504 | NULL, at91_rtc_match); | ||
505 | if (!rtc) | ||
506 | platform_driver_unregister(&at91_rtc_driver); | ||
507 | return rtc ? 0 : -ENODEV; | ||
508 | } | ||
509 | module_init(at91_rtc_init); | ||
510 | |||
511 | static void __exit at91_rtc_exit(void) | ||
512 | { | ||
513 | platform_driver_unregister(&at91_rtc_driver); | ||
514 | } | ||
515 | module_exit(at91_rtc_exit); | ||
516 | |||
517 | |||
518 | MODULE_AUTHOR("Michel Benoit"); | ||
519 | MODULE_DESCRIPTION("RTC driver for Atmel AT91SAM9x"); | ||
520 | MODULE_LICENSE("GPL"); | ||