blob: e19ed0f6dca08debeaf8196745f887429fcabacd [file] [log] [blame]
Richard Purdiee842f1c2006-03-27 01:16:46 -08001/*
2 * Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx
3 *
4 * Copyright (c) 2000 Nils Faerber
5 *
6 * Based on rtc.c by Paul Gortmaker
7 *
8 * Original Driver by Nils Faerber <nils@kernelconcepts.de>
9 *
10 * Modifications from:
11 * CIH <cih@coventive.com>
Nicolas Pitre2f82af02009-09-14 03:25:28 -040012 * Nicolas Pitre <nico@fluxnic.net>
Richard Purdiee842f1c2006-03-27 01:16:46 -080013 * Andrew Christian <andrew.christian@hp.com>
14 *
15 * Converted to the RTC subsystem and Driver Model
16 * by Richard Purdie <rpurdie@rpsys.net>
17 *
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version
21 * 2 of the License, or (at your option) any later version.
22 */
23
24#include <linux/platform_device.h>
25#include <linux/module.h>
26#include <linux/rtc.h>
27#include <linux/init.h>
28#include <linux/fs.h>
29#include <linux/interrupt.h>
30#include <linux/string.h>
31#include <linux/pm.h>
Jiri Slaby1977f032007-10-18 23:40:25 -070032#include <linux/bitops.h>
Richard Purdiee842f1c2006-03-27 01:16:46 -080033
Russell Kinga09e64f2008-08-05 16:14:15 +010034#include <mach/hardware.h>
Richard Purdiee842f1c2006-03-27 01:16:46 -080035#include <asm/irq.h>
Richard Purdiee842f1c2006-03-27 01:16:46 -080036
37#ifdef CONFIG_ARCH_PXA
Eric Miao5bf3df32009-01-20 11:04:16 +080038#include <mach/regs-rtc.h>
39#include <mach/regs-ost.h>
Richard Purdiee842f1c2006-03-27 01:16:46 -080040#endif
41
Marcelo Roberto Jimeneza404ad12010-10-18 22:33:53 +010042#define RTC_DEF_DIVIDER (32768 - 1)
Richard Purdiee842f1c2006-03-27 01:16:46 -080043#define RTC_DEF_TRIM 0
44
45static unsigned long rtc_freq = 1024;
Eric Miao67697172008-12-18 11:10:32 +080046static unsigned long timer_freq;
Richard Purdiee842f1c2006-03-27 01:16:46 -080047static struct rtc_time rtc_alarm;
Ingo Molnar34af9462006-06-27 02:53:55 -070048static DEFINE_SPINLOCK(sa1100_rtc_lock);
Richard Purdiee842f1c2006-03-27 01:16:46 -080049
Russell King797276e2008-04-20 12:30:41 +010050static inline int rtc_periodic_alarm(struct rtc_time *tm)
51{
52 return (tm->tm_year == -1) ||
53 ((unsigned)tm->tm_mon >= 12) ||
54 ((unsigned)(tm->tm_mday - 1) >= 31) ||
55 ((unsigned)tm->tm_hour > 23) ||
56 ((unsigned)tm->tm_min > 59) ||
57 ((unsigned)tm->tm_sec > 59);
58}
59
60/*
61 * Calculate the next alarm time given the requested alarm time mask
62 * and the current time.
63 */
Marcelo Roberto Jimeneza404ad12010-10-18 22:33:53 +010064static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
65 struct rtc_time *alrm)
Russell King797276e2008-04-20 12:30:41 +010066{
67 unsigned long next_time;
68 unsigned long now_time;
69
70 next->tm_year = now->tm_year;
71 next->tm_mon = now->tm_mon;
72 next->tm_mday = now->tm_mday;
73 next->tm_hour = alrm->tm_hour;
74 next->tm_min = alrm->tm_min;
75 next->tm_sec = alrm->tm_sec;
76
77 rtc_tm_to_time(now, &now_time);
78 rtc_tm_to_time(next, &next_time);
79
80 if (next_time < now_time) {
81 /* Advance one day */
82 next_time += 60 * 60 * 24;
83 rtc_time_to_tm(next_time, next);
84 }
85}
86
Richard Purdiee842f1c2006-03-27 01:16:46 -080087static int rtc_update_alarm(struct rtc_time *alrm)
88{
89 struct rtc_time alarm_tm, now_tm;
90 unsigned long now, time;
91 int ret;
92
93 do {
94 now = RCNR;
95 rtc_time_to_tm(now, &now_tm);
96 rtc_next_alarm_time(&alarm_tm, &now_tm, alrm);
97 ret = rtc_tm_to_time(&alarm_tm, &time);
98 if (ret != 0)
99 break;
100
101 RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL);
102 RTAR = time;
103 } while (now != RCNR);
104
105 return ret;
106}
107
David Howells7d12e782006-10-05 14:55:46 +0100108static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)
Richard Purdiee842f1c2006-03-27 01:16:46 -0800109{
110 struct platform_device *pdev = to_platform_device(dev_id);
111 struct rtc_device *rtc = platform_get_drvdata(pdev);
112 unsigned int rtsr;
113 unsigned long events = 0;
114
115 spin_lock(&sa1100_rtc_lock);
116
117 rtsr = RTSR;
118 /* clear interrupt sources */
119 RTSR = 0;
120 RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2);
121
122 /* clear alarm interrupt if it has occurred */
123 if (rtsr & RTSR_AL)
124 rtsr &= ~RTSR_ALE;
125 RTSR = rtsr & (RTSR_ALE | RTSR_HZE);
126
127 /* update irq data & counter */
128 if (rtsr & RTSR_AL)
129 events |= RTC_AF | RTC_IRQF;
130 if (rtsr & RTSR_HZ)
131 events |= RTC_UF | RTC_IRQF;
132
David Brownellab6a2d72007-05-08 00:33:30 -0700133 rtc_update_irq(rtc, 1, events);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800134
135 if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm))
136 rtc_update_alarm(&rtc_alarm);
137
138 spin_unlock(&sa1100_rtc_lock);
139
140 return IRQ_HANDLED;
141}
142
143static int rtc_timer1_count;
144
David Howells7d12e782006-10-05 14:55:46 +0100145static irqreturn_t timer1_interrupt(int irq, void *dev_id)
Richard Purdiee842f1c2006-03-27 01:16:46 -0800146{
147 struct platform_device *pdev = to_platform_device(dev_id);
148 struct rtc_device *rtc = platform_get_drvdata(pdev);
149
150 /*
151 * If we match for the first time, rtc_timer1_count will be 1.
152 * Otherwise, we wrapped around (very unlikely but
153 * still possible) so compute the amount of missed periods.
154 * The match reg is updated only when the data is actually retrieved
155 * to avoid unnecessary interrupts.
156 */
157 OSSR = OSSR_M1; /* clear match on timer1 */
158
David Brownellab6a2d72007-05-08 00:33:30 -0700159 rtc_update_irq(rtc, rtc_timer1_count, RTC_PF | RTC_IRQF);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800160
161 if (rtc_timer1_count == 1)
Eric Miao67697172008-12-18 11:10:32 +0800162 rtc_timer1_count = (rtc_freq * ((1 << 30) / (timer_freq >> 2)));
Richard Purdiee842f1c2006-03-27 01:16:46 -0800163
164 return IRQ_HANDLED;
165}
166
167static int sa1100_rtc_read_callback(struct device *dev, int data)
168{
169 if (data & RTC_PF) {
170 /* interpolate missed periods and set match for the next */
Eric Miao67697172008-12-18 11:10:32 +0800171 unsigned long period = timer_freq / rtc_freq;
Richard Purdiee842f1c2006-03-27 01:16:46 -0800172 unsigned long oscr = OSCR;
173 unsigned long osmr1 = OSMR1;
174 unsigned long missed = (oscr - osmr1)/period;
175 data += missed << 8;
176 OSSR = OSSR_M1; /* clear match on timer 1 */
177 OSMR1 = osmr1 + (missed + 1)*period;
178 /* Ensure we didn't miss another match in the mean time.
179 * Here we compare (match - OSCR) 8 instead of 0 --
180 * see comment in pxa_timer_interrupt() for explanation.
181 */
Marcelo Roberto Jimeneza404ad12010-10-18 22:33:53 +0100182 while ((signed long)((osmr1 = OSMR1) - OSCR) <= 8) {
Richard Purdiee842f1c2006-03-27 01:16:46 -0800183 data += 0x100;
184 OSSR = OSSR_M1; /* clear match on timer 1 */
185 OSMR1 = osmr1 + period;
186 }
187 }
188 return data;
189}
190
191static int sa1100_rtc_open(struct device *dev)
192{
193 int ret;
194
Thomas Gleixnerdace1452006-07-01 19:29:38 -0700195 ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED,
Marcelo Roberto Jimeneza404ad12010-10-18 22:33:53 +0100196 "rtc 1Hz", dev);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800197 if (ret) {
Alessandro Zummo2260a252006-04-10 22:54:46 -0700198 dev_err(dev, "IRQ %d already in use.\n", IRQ_RTC1Hz);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800199 goto fail_ui;
200 }
Thomas Gleixnerdace1452006-07-01 19:29:38 -0700201 ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, IRQF_DISABLED,
Marcelo Roberto Jimeneza404ad12010-10-18 22:33:53 +0100202 "rtc Alrm", dev);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800203 if (ret) {
Alessandro Zummo2260a252006-04-10 22:54:46 -0700204 dev_err(dev, "IRQ %d already in use.\n", IRQ_RTCAlrm);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800205 goto fail_ai;
206 }
Thomas Gleixnerdace1452006-07-01 19:29:38 -0700207 ret = request_irq(IRQ_OST1, timer1_interrupt, IRQF_DISABLED,
Marcelo Roberto Jimeneza404ad12010-10-18 22:33:53 +0100208 "rtc timer", dev);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800209 if (ret) {
Alessandro Zummo2260a252006-04-10 22:54:46 -0700210 dev_err(dev, "IRQ %d already in use.\n", IRQ_OST1);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800211 goto fail_pi;
212 }
213 return 0;
214
215 fail_pi:
Russell Kingf1226702006-05-06 11:29:21 +0100216 free_irq(IRQ_RTCAlrm, dev);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800217 fail_ai:
Russell Kingf1226702006-05-06 11:29:21 +0100218 free_irq(IRQ_RTC1Hz, dev);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800219 fail_ui:
220 return ret;
221}
222
223static void sa1100_rtc_release(struct device *dev)
224{
225 spin_lock_irq(&sa1100_rtc_lock);
226 RTSR = 0;
227 OIER &= ~OIER_E1;
228 OSSR = OSSR_M1;
229 spin_unlock_irq(&sa1100_rtc_lock);
230
231 free_irq(IRQ_OST1, dev);
232 free_irq(IRQ_RTCAlrm, dev);
233 free_irq(IRQ_RTC1Hz, dev);
234}
235
236
237static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd,
238 unsigned long arg)
239{
Marcelo Roberto Jimeneza404ad12010-10-18 22:33:53 +0100240 switch (cmd) {
Richard Purdiee842f1c2006-03-27 01:16:46 -0800241 case RTC_AIE_OFF:
242 spin_lock_irq(&sa1100_rtc_lock);
243 RTSR &= ~RTSR_ALE;
244 spin_unlock_irq(&sa1100_rtc_lock);
245 return 0;
246 case RTC_AIE_ON:
247 spin_lock_irq(&sa1100_rtc_lock);
248 RTSR |= RTSR_ALE;
249 spin_unlock_irq(&sa1100_rtc_lock);
250 return 0;
251 case RTC_UIE_OFF:
252 spin_lock_irq(&sa1100_rtc_lock);
253 RTSR &= ~RTSR_HZE;
254 spin_unlock_irq(&sa1100_rtc_lock);
255 return 0;
256 case RTC_UIE_ON:
257 spin_lock_irq(&sa1100_rtc_lock);
258 RTSR |= RTSR_HZE;
259 spin_unlock_irq(&sa1100_rtc_lock);
260 return 0;
261 case RTC_PIE_OFF:
262 spin_lock_irq(&sa1100_rtc_lock);
263 OIER &= ~OIER_E1;
264 spin_unlock_irq(&sa1100_rtc_lock);
265 return 0;
266 case RTC_PIE_ON:
Richard Purdiee842f1c2006-03-27 01:16:46 -0800267 spin_lock_irq(&sa1100_rtc_lock);
Eric Miao67697172008-12-18 11:10:32 +0800268 OSMR1 = timer_freq / rtc_freq + OSCR;
Richard Purdiee842f1c2006-03-27 01:16:46 -0800269 OIER |= OIER_E1;
270 rtc_timer1_count = 1;
271 spin_unlock_irq(&sa1100_rtc_lock);
272 return 0;
273 case RTC_IRQP_READ:
274 return put_user(rtc_freq, (unsigned long *)arg);
275 case RTC_IRQP_SET:
Eric Miao67697172008-12-18 11:10:32 +0800276 if (arg < 1 || arg > timer_freq)
Richard Purdiee842f1c2006-03-27 01:16:46 -0800277 return -EINVAL;
Richard Purdiee842f1c2006-03-27 01:16:46 -0800278 rtc_freq = arg;
279 return 0;
280 }
Alessandro Zummob3969e52006-05-20 15:00:29 -0700281 return -ENOIOCTLCMD;
Richard Purdiee842f1c2006-03-27 01:16:46 -0800282}
283
284static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm)
285{
286 rtc_time_to_tm(RCNR, tm);
287 return 0;
288}
289
290static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm)
291{
292 unsigned long time;
293 int ret;
294
295 ret = rtc_tm_to_time(tm, &time);
296 if (ret == 0)
297 RCNR = time;
298 return ret;
299}
300
301static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
302{
David Brownell32b49da2007-02-20 13:58:13 -0800303 u32 rtsr;
304
Richard Purdiee842f1c2006-03-27 01:16:46 -0800305 memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time));
David Brownell32b49da2007-02-20 13:58:13 -0800306 rtsr = RTSR;
307 alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0;
308 alrm->pending = (rtsr & RTSR_AL) ? 1 : 0;
Richard Purdiee842f1c2006-03-27 01:16:46 -0800309 return 0;
310}
311
312static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
313{
314 int ret;
315
316 spin_lock_irq(&sa1100_rtc_lock);
317 ret = rtc_update_alarm(&alrm->time);
318 if (ret == 0) {
Richard Purdiee842f1c2006-03-27 01:16:46 -0800319 if (alrm->enabled)
David Brownell32b49da2007-02-20 13:58:13 -0800320 RTSR |= RTSR_ALE;
Richard Purdiee842f1c2006-03-27 01:16:46 -0800321 else
David Brownell32b49da2007-02-20 13:58:13 -0800322 RTSR &= ~RTSR_ALE;
Richard Purdiee842f1c2006-03-27 01:16:46 -0800323 }
324 spin_unlock_irq(&sa1100_rtc_lock);
325
326 return ret;
327}
328
329static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq)
330{
David Brownella2db8df2006-12-13 00:35:08 -0800331 seq_printf(seq, "trim/divider\t: 0x%08x\n", (u32) RTTR);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800332 seq_printf(seq, "update_IRQ\t: %s\n",
333 (RTSR & RTSR_HZE) ? "yes" : "no");
334 seq_printf(seq, "periodic_IRQ\t: %s\n",
335 (OIER & OIER_E1) ? "yes" : "no");
336 seq_printf(seq, "periodic_freq\t: %ld\n", rtc_freq);
337
338 return 0;
339}
340
David Brownellff8371a2006-09-30 23:28:17 -0700341static const struct rtc_class_ops sa1100_rtc_ops = {
Richard Purdiee842f1c2006-03-27 01:16:46 -0800342 .open = sa1100_rtc_open,
343 .read_callback = sa1100_rtc_read_callback,
344 .release = sa1100_rtc_release,
345 .ioctl = sa1100_rtc_ioctl,
346 .read_time = sa1100_rtc_read_time,
347 .set_time = sa1100_rtc_set_time,
348 .read_alarm = sa1100_rtc_read_alarm,
349 .set_alarm = sa1100_rtc_set_alarm,
350 .proc = sa1100_rtc_proc,
351};
352
353static int sa1100_rtc_probe(struct platform_device *pdev)
354{
355 struct rtc_device *rtc;
356
Eric Miao67697172008-12-18 11:10:32 +0800357 timer_freq = get_clock_tick_rate();
358
Richard Purdiee842f1c2006-03-27 01:16:46 -0800359 /*
360 * According to the manual we should be able to let RTTR be zero
361 * and then a default diviser for a 32.768KHz clock is used.
362 * Apparently this doesn't work, at least for my SA1110 rev 5.
363 * If the clock divider is uninitialized then reset it to the
364 * default value to get the 1Hz clock.
365 */
366 if (RTTR == 0) {
367 RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
Marcelo Roberto Jimeneza404ad12010-10-18 22:33:53 +0100368 dev_warn(&pdev->dev, "warning: "
369 "initializing default clock divider/trim value\n");
Richard Purdiee842f1c2006-03-27 01:16:46 -0800370 /* The current RTC value probably doesn't make sense either */
371 RCNR = 0;
372 }
373
Uli Luckase5a2c9c2008-06-18 09:54:03 +0100374 device_init_wakeup(&pdev->dev, 1);
375
Richard Purdiee842f1c2006-03-27 01:16:46 -0800376 rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops,
377 THIS_MODULE);
378
Alessandro Zummo2260a252006-04-10 22:54:46 -0700379 if (IS_ERR(rtc))
Richard Purdiee842f1c2006-03-27 01:16:46 -0800380 return PTR_ERR(rtc);
Richard Purdiee842f1c2006-03-27 01:16:46 -0800381
382 platform_set_drvdata(pdev, rtc);
383
Richard Purdiee842f1c2006-03-27 01:16:46 -0800384 return 0;
385}
386
387static int sa1100_rtc_remove(struct platform_device *pdev)
388{
389 struct rtc_device *rtc = platform_get_drvdata(pdev);
390
Marcelo Roberto Jimeneza404ad12010-10-18 22:33:53 +0100391 if (rtc)
Richard Purdiee842f1c2006-03-27 01:16:46 -0800392 rtc_device_unregister(rtc);
393
394 return 0;
395}
396
Russell King6bc54e62007-11-12 22:49:58 +0000397#ifdef CONFIG_PM
Haojian Zhuang5d027cd2009-07-21 14:31:09 +0800398static int sa1100_rtc_suspend(struct device *dev)
Russell King6bc54e62007-11-12 22:49:58 +0000399{
Haojian Zhuang5d027cd2009-07-21 14:31:09 +0800400 if (device_may_wakeup(dev))
David Brownellf6182582008-02-06 01:38:57 -0800401 enable_irq_wake(IRQ_RTCAlrm);
Russell King6bc54e62007-11-12 22:49:58 +0000402 return 0;
403}
404
Haojian Zhuang5d027cd2009-07-21 14:31:09 +0800405static int sa1100_rtc_resume(struct device *dev)
Russell King6bc54e62007-11-12 22:49:58 +0000406{
Haojian Zhuang5d027cd2009-07-21 14:31:09 +0800407 if (device_may_wakeup(dev))
David Brownellf6182582008-02-06 01:38:57 -0800408 disable_irq_wake(IRQ_RTCAlrm);
Russell King6bc54e62007-11-12 22:49:58 +0000409 return 0;
410}
Haojian Zhuang5d027cd2009-07-21 14:31:09 +0800411
Alexey Dobriyan47145212009-12-14 18:00:08 -0800412static const struct dev_pm_ops sa1100_rtc_pm_ops = {
Haojian Zhuang5d027cd2009-07-21 14:31:09 +0800413 .suspend = sa1100_rtc_suspend,
414 .resume = sa1100_rtc_resume,
415};
Russell King6bc54e62007-11-12 22:49:58 +0000416#endif
417
Richard Purdiee842f1c2006-03-27 01:16:46 -0800418static struct platform_driver sa1100_rtc_driver = {
419 .probe = sa1100_rtc_probe,
420 .remove = sa1100_rtc_remove,
Richard Purdiee842f1c2006-03-27 01:16:46 -0800421 .driver = {
Haojian Zhuang5d027cd2009-07-21 14:31:09 +0800422 .name = "sa1100-rtc",
423#ifdef CONFIG_PM
424 .pm = &sa1100_rtc_pm_ops,
425#endif
Richard Purdiee842f1c2006-03-27 01:16:46 -0800426 },
427};
428
429static int __init sa1100_rtc_init(void)
430{
431 return platform_driver_register(&sa1100_rtc_driver);
432}
433
434static void __exit sa1100_rtc_exit(void)
435{
436 platform_driver_unregister(&sa1100_rtc_driver);
437}
438
439module_init(sa1100_rtc_init);
440module_exit(sa1100_rtc_exit);
441
442MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
443MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)");
444MODULE_LICENSE("GPL");
Kay Sieversad28a072008-04-10 21:29:25 -0700445MODULE_ALIAS("platform:sa1100-rtc");