aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorAndrew Victor <andrew@sanpeople.com>2006-06-25 08:48:27 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-25 13:01:14 -0400
commit788b1fc619a31ebdbadd3a8863631f59a4bd2944 (patch)
tree8903053fd78c938078ea2b7c30bc1f434507416e /drivers/rtc
parent8232212e0b4ee4eb3e407f5a9b098f6377820164 (diff)
[PATCH] AT91RM9200 RTC driver
Adds support for the RTC integrated in the Atmel AT91RM9200 SoC. Driver was originally written for 2.4 by Rick Bronson. Then converted to 2.6 ARM RTC API by Steven Scholz. Now converted to the RTC class model. Signed-off-by: Andrew Victor <andrew@sanpeople.com> Signed-off-by: Alessandro Zummo <a.zummo@towertech.it> Cc: Russell King <rmk@arm.linux.org.uk> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig6
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-at91.c388
3 files changed, 395 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8534012ebdef..0bfd3aad6179 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -202,6 +202,12 @@ config RTC_DRV_PL031
202 To compile this driver as a module, choose M here: the 202 To compile this driver as a module, choose M here: the
203 module will be called rtc-pl031. 203 module will be called rtc-pl031.
204 204
205config RTC_DRV_AT91
206 tristate "AT91RM9200"
207 depends on RTC_CLASS && ARCH_AT91RM9200
208 help
209 Driver for the Atmel AT91RM9200's internal RTC (Realtime Clock).
210
205config RTC_DRV_TEST 211config RTC_DRV_TEST
206 tristate "Test driver/device" 212 tristate "Test driver/device"
207 depends on RTC_CLASS 213 depends on RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index cbb8e8a7f620..f39992b865ce 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
25obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o 25obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
26obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o 26obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
27obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o 27obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
28obj-$(CONFIG_RTC_DRV_AT91) += rtc-at91.o
diff --git a/drivers/rtc/rtc-at91.c b/drivers/rtc/rtc-at91.c
new file mode 100644
index 000000000000..b03aba5f39eb
--- /dev/null
+++ b/drivers/rtc/rtc-at91.c
@@ -0,0 +1,388 @@
1/*
2 * Real Time Clock interface for Linux on Atmel AT91RM9200
3 *
4 * Copyright (C) 2002 Rick Bronson
5 *
6 * Converted to RTC class model by Andrew Victor
7 *
8 * Ported to Linux 2.6 by Steven Scholz
9 * Based on s3c2410-rtc.c Simtec Electronics
10 *
11 * Based on sa1100-rtc.c by Nils Faerber
12 * Based on rtc.c by Paul Gortmaker
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version
17 * 2 of the License, or (at your option) any later version.
18 *
19 */
20
21#include <linux/module.h>
22#include <linux/kernel.h>
23#include <linux/platform_device.h>
24#include <linux/time.h>
25#include <linux/rtc.h>
26#include <linux/bcd.h>
27#include <linux/interrupt.h>
28#include <linux/ioctl.h>
29#include <linux/completion.h>
30
31#include <asm/uaccess.h>
32#include <asm/rtc.h>
33
34#include <asm/mach/time.h>
35
36
37#define AT91_RTC_FREQ 1
38#define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */
39
40static DECLARE_COMPLETION(at91_rtc_updated);
41static unsigned int at91_alarm_year = AT91_RTC_EPOCH;
42
43
44/*
45 * Decode time/date into rtc_time structure
46 */
47static void at91_rtc_decodetime(unsigned int timereg, unsigned int calreg, struct rtc_time *tm)
48{
49 unsigned int time, date;
50
51 /* must read twice in case it changes */
52 do {
53 time = at91_sys_read(timereg);
54 date = at91_sys_read(calreg);
55 } while ((time != at91_sys_read(timereg)) || (date != at91_sys_read(calreg)));
56
57 tm->tm_sec = BCD2BIN((time & AT91_RTC_SEC) >> 0);
58 tm->tm_min = BCD2BIN((time & AT91_RTC_MIN) >> 8);
59 tm->tm_hour = BCD2BIN((time & AT91_RTC_HOUR) >> 16);
60
61 /*
62 * The Calendar Alarm register does not have a field for
63 * the year - so these will return an invalid value. When an
64 * alarm is set, at91_alarm_year wille store the current year.
65 */
66 tm->tm_year = BCD2BIN(date & AT91_RTC_CENT) * 100; /* century */
67 tm->tm_year += BCD2BIN((date & AT91_RTC_YEAR) >> 8); /* year */
68
69 tm->tm_wday = BCD2BIN((date & AT91_RTC_DAY) >> 21) - 1; /* day of the week [0-6], Sunday=0 */
70 tm->tm_mon = BCD2BIN((date & AT91_RTC_MONTH) >> 16) - 1;
71 tm->tm_mday = BCD2BIN((date & AT91_RTC_DATE) >> 24);
72}
73
74/*
75 * Read current time and date in RTC
76 */
77static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
78{
79 at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, tm);
80 tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
81 tm->tm_year = tm->tm_year - 1900;
82
83 pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
84 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
85
86 return 0;
87}
88
89/*
90 * Set current time and date in RTC
91 */
92static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
93{
94 unsigned long cr;
95
96 pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
97 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
98
99 /* Stop Time/Calendar from counting */
100 cr = at91_sys_read(AT91_RTC_CR);
101 at91_sys_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM);
102
103 at91_sys_write(AT91_RTC_IER, AT91_RTC_ACKUPD);
104 wait_for_completion(&at91_rtc_updated); /* wait for ACKUPD interrupt */
105 at91_sys_write(AT91_RTC_IDR, AT91_RTC_ACKUPD);
106
107 at91_sys_write(AT91_RTC_TIMR,
108 BIN2BCD(tm->tm_sec) << 0
109 | BIN2BCD(tm->tm_min) << 8
110 | BIN2BCD(tm->tm_hour) << 16);
111
112 at91_sys_write(AT91_RTC_CALR,
113 BIN2BCD((tm->tm_year + 1900) / 100) /* century */
114 | BIN2BCD(tm->tm_year % 100) << 8 /* year */
115 | BIN2BCD(tm->tm_mon + 1) << 16 /* tm_mon starts at zero */
116 | BIN2BCD(tm->tm_wday + 1) << 21 /* day of the week [0-6], Sunday=0 */
117 | BIN2BCD(tm->tm_mday) << 24);
118
119 /* Restart Time/Calendar */
120 cr = at91_sys_read(AT91_RTC_CR);
121 at91_sys_write(AT91_RTC_CR, cr & ~(AT91_RTC_UPDCAL | AT91_RTC_UPDTIM));
122
123 return 0;
124}
125
126/*
127 * Read alarm time and date in RTC
128 */
129static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
130{
131 struct rtc_time *tm = &alrm->time;
132
133 at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm);
134 tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
135 tm->tm_year = at91_alarm_year - 1900;
136
137 pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
138 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
139
140 return 0;
141}
142
143/*
144 * Set alarm time and date in RTC
145 */
146static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
147{
148 struct rtc_time tm;
149
150 at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm);
151
152 at91_alarm_year = tm.tm_year;
153
154 tm.tm_hour = alrm->time.tm_hour;
155 tm.tm_min = alrm->time.tm_min;
156 tm.tm_sec = alrm->time.tm_sec;
157
158 at91_sys_write(AT91_RTC_TIMALR,
159 BIN2BCD(tm.tm_sec) << 0
160 | BIN2BCD(tm.tm_min) << 8
161 | BIN2BCD(tm.tm_hour) << 16
162 | AT91_RTC_HOUREN | AT91_RTC_MINEN | AT91_RTC_SECEN);
163 at91_sys_write(AT91_RTC_CALALR,
164 BIN2BCD(tm.tm_mon + 1) << 16 /* tm_mon starts at zero */
165 | BIN2BCD(tm.tm_mday) << 24
166 | AT91_RTC_DATEEN | AT91_RTC_MTHEN);
167
168 pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
169 at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
170
171 return 0;
172}
173
174/*
175 * Handle commands from user-space
176 */
177static int at91_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
178{
179 int ret = 0;
180
181 pr_debug("%s(): cmd=%08x, arg=%08lx.\n", __FUNCTION__, cmd, arg);
182
183 switch (cmd) {
184 case RTC_AIE_OFF: /* alarm off */
185 at91_sys_write(AT91_RTC_IDR, AT91_RTC_ALARM);
186 break;
187 case RTC_AIE_ON: /* alarm on */
188 at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM);
189 break;
190 case RTC_UIE_OFF: /* update off */
191 case RTC_PIE_OFF: /* periodic off */
192 at91_sys_write(AT91_RTC_IDR, AT91_RTC_SECEV);
193 break;
194 case RTC_UIE_ON: /* update on */
195 case RTC_PIE_ON: /* periodic on */
196 at91_sys_write(AT91_RTC_IER, AT91_RTC_SECEV);
197 break;
198 case RTC_IRQP_READ: /* read periodic alarm frequency */
199 ret = put_user(AT91_RTC_FREQ, (unsigned long *) arg);
200 break;
201 case RTC_IRQP_SET: /* set periodic alarm frequency */
202 if (arg != AT91_RTC_FREQ)
203 ret = -EINVAL;
204 break;
205 default:
206 ret = -ENOIOCTLCMD;
207 break;
208 }
209
210 return ret;
211}
212
213/*
214 * Provide additional RTC information in /proc/driver/rtc
215 */
216static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
217{
218 unsigned long imr = at91_sys_read(AT91_RTC_IMR);
219
220 seq_printf(seq, "alarm_IRQ\t: %s\n", (imr & AT91_RTC_ALARM) ? "yes" : "no");
221 seq_printf(seq, "update_IRQ\t: %s\n", (imr & AT91_RTC_ACKUPD) ? "yes" : "no");
222 seq_printf(seq, "periodic_IRQ\t: %s\n", (imr & AT91_RTC_SECEV) ? "yes" : "no");
223 seq_printf(seq, "periodic_freq\t: %ld\n", (unsigned long) AT91_RTC_FREQ);
224
225 return 0;
226}
227
228/*
229 * IRQ handler for the RTC
230 */
231static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
232{
233 struct platform_device *pdev = (struct platform_device *)dev_id;
234 struct rtc_device *rtc = platform_get_drvdata(pdev);
235 unsigned int rtsr;
236 unsigned long events = 0;
237
238 rtsr = at91_sys_read(AT91_RTC_SR) & at91_sys_read(AT91_RTC_IMR);
239 if (rtsr) { /* this interrupt is shared! Is it ours? */
240 if (rtsr & AT91_RTC_ALARM)
241 events |= (RTC_AF | RTC_IRQF);
242 if (rtsr & AT91_RTC_SECEV)
243 events |= (RTC_UF | RTC_IRQF);
244 if (rtsr & AT91_RTC_ACKUPD)
245 complete(&at91_rtc_updated);
246
247 at91_sys_write(AT91_RTC_SCCR, rtsr); /* clear status reg */
248
249 rtc_update_irq(&rtc->class_dev, 1, events);
250
251 pr_debug("%s(): num=%ld, events=0x%02lx\n", __FUNCTION__,
252 events >> 8, events & 0x000000FF);
253
254 return IRQ_HANDLED;
255 }
256 return IRQ_NONE; /* not handled */
257}
258
259static struct rtc_class_ops at91_rtc_ops = {
260 .ioctl = at91_rtc_ioctl,
261 .read_time = at91_rtc_readtime,
262 .set_time = at91_rtc_settime,
263 .read_alarm = at91_rtc_readalarm,
264 .set_alarm = at91_rtc_setalarm,
265 .proc = at91_rtc_proc,
266};
267
268/*
269 * Initialize and install RTC driver
270 */
271static int __init at91_rtc_probe(struct platform_device *pdev)
272{
273 struct rtc_device *rtc;
274 int ret;
275
276 at91_sys_write(AT91_RTC_CR, 0);
277 at91_sys_write(AT91_RTC_MR, 0); /* 24 hour mode */
278
279 /* Disable all interrupts */
280 at91_sys_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM | AT91_RTC_SECEV | AT91_RTC_TIMEV | AT91_RTC_CALEV);
281
282 ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, SA_SHIRQ, "at91_rtc", pdev);
283 if (ret) {
284 printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", AT91_ID_SYS);
285 return ret;
286 }
287
288 rtc = rtc_device_register(pdev->name, &pdev->dev, &at91_rtc_ops, THIS_MODULE);
289 if (IS_ERR(rtc)) {
290 free_irq(AT91_ID_SYS, pdev);
291 return PTR_ERR(rtc);
292 }
293 platform_set_drvdata(pdev, rtc);
294
295 printk(KERN_INFO "AT91 Real Time Clock driver.\n");
296 return 0;
297}
298
299/*
300 * Disable and remove the RTC driver
301 */
302static int __devexit at91_rtc_remove(struct platform_device *pdev)
303{
304 struct rtc_device *rtc = platform_get_drvdata(pdev);
305
306 /* Disable all interrupts */
307 at91_sys_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM | AT91_RTC_SECEV | AT91_RTC_TIMEV | AT91_RTC_CALEV);
308 free_irq(AT91_ID_SYS, pdev);
309
310 rtc_device_unregister(rtc);
311 platform_set_drvdata(pdev, NULL);
312
313 return 0;
314}
315
316#ifdef CONFIG_PM
317
318/* AT91RM9200 RTC Power management control */
319
320static struct timespec at91_rtc_delta;
321
322static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)
323{
324 struct rtc_time tm;
325 struct timespec time;
326
327 time.tv_nsec = 0;
328
329 /* calculate time delta for suspend */
330 at91_rtc_readtime(&pdev->dev, &tm);
331 rtc_tm_to_time(&tm, &time.tv_sec);
332 save_time_delta(&at91_rtc_delta, &time);
333
334 pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
335 1900 + tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
336
337 return 0;
338}
339
340static int at91_rtc_resume(struct platform_device *pdev)
341{
342 struct rtc_time tm;
343 struct timespec time;
344
345 time.tv_nsec = 0;
346
347 at91_rtc_readtime(&pdev->dev, &tm);
348 rtc_tm_to_time(&tm, &time.tv_sec);
349 restore_time_delta(&at91_rtc_delta, &time);
350
351 pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
352 1900 + tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
353
354 return 0;
355}
356#else
357#define at91_rtc_suspend NULL
358#define at91_rtc_resume NULL
359#endif
360
361static struct platform_driver at91_rtc_driver = {
362 .probe = at91_rtc_probe,
363 .remove = at91_rtc_remove,
364 .suspend = at91_rtc_suspend,
365 .resume = at91_rtc_resume,
366 .driver = {
367 .name = "at91_rtc",
368 .owner = THIS_MODULE,
369 },
370};
371
372static int __init at91_rtc_init(void)
373{
374 return platform_driver_register(&at91_rtc_driver);
375}
376
377static void __exit at91_rtc_exit(void)
378{
379 platform_driver_unregister(&at91_rtc_driver);
380}
381
382
383module_init(at91_rtc_init);
384module_exit(at91_rtc_exit);
385
386MODULE_AUTHOR("Rick Bronson");
387MODULE_DESCRIPTION("RTC driver for Atmel AT91RM9200");
388MODULE_LICENSE("GPL");