aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorJamie Lenehan <lenehan@twibble.org>2006-12-08 01:26:15 -0500
committerPaul Mundt <lethal@linux-sh.org>2006-12-11 18:42:08 -0500
commit1b73e6ae45d0353a062d7bea707757a235473cf9 (patch)
tree22f4d14de41371e48bf513e11e3734adb4dd788c /drivers/rtc
parenta16147965ca7a84dc08c4457961782e06ac7cd0d (diff)
rtc: rtc-sh: alarm support.
This adds alarm support for the RTC_ALM_SET, RTC_ALM_READ, RTC_WKALM_SET and RTC_WKALM_RD operations to rtc-sh. The only unusual part is the handling of the alarm interrupt. If you clear the alarm flag (AF) while the time in the RTC still matches the time in the alarm registers than AF is immediately re-set, and if the alarm interrupt (AIE) is still enabled then it re-triggers. I was originally getting around 20k+ interrupts generated during the second when the RTC and alarm registers matches. The solution I've used is to clear AIE when the alarm goes off and then use the carry interrupt to re-enabled it. The carry interrupt will check AF and re-enabled AIE if it's clear. If AF is not clear it'll clear it and then the check will be repeated next carry interrupt. This a bit in rtc structure that indicates that it's waiting to have AIE re-enabled so it doesn't turn it on when it wasn't enabled anyway. Signed-off-by: Jamie Lenehan <lenehan@twibble.org> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/rtc-sh.c226
1 files changed, 201 insertions, 25 deletions
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index 8f22eb1aff5c..72ba1a70f35f 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -2,6 +2,7 @@
2 * SuperH On-Chip RTC Support 2 * SuperH On-Chip RTC Support
3 * 3 *
4 * Copyright (C) 2006 Paul Mundt 4 * Copyright (C) 2006 Paul Mundt
5 * Copyright (C) 2006 Jamie Lenehan
5 * 6 *
6 * Based on the old arch/sh/kernel/cpu/rtc.c by: 7 * Based on the old arch/sh/kernel/cpu/rtc.c by:
7 * 8 *
@@ -23,6 +24,9 @@
23#include <linux/spinlock.h> 24#include <linux/spinlock.h>
24#include <linux/io.h> 25#include <linux/io.h>
25 26
27#define DRV_NAME "sh-rtc"
28#define DRV_VERSION "0.1.2"
29
26#ifdef CONFIG_CPU_SH3 30#ifdef CONFIG_CPU_SH3
27#define rtc_reg_size sizeof(u16) 31#define rtc_reg_size sizeof(u16)
28#define RTC_BIT_INVERTED 0 /* No bug on SH7708, SH7709A */ 32#define RTC_BIT_INVERTED 0 /* No bug on SH7708, SH7709A */
@@ -34,21 +38,25 @@
34#define RTC_REG(r) ((r) * rtc_reg_size) 38#define RTC_REG(r) ((r) * rtc_reg_size)
35 39
36#define R64CNT RTC_REG(0) 40#define R64CNT RTC_REG(0)
37#define RSECCNT RTC_REG(1) 41
38#define RMINCNT RTC_REG(2) 42#define RSECCNT RTC_REG(1) /* RTC sec */
39#define RHRCNT RTC_REG(3) 43#define RMINCNT RTC_REG(2) /* RTC min */
40#define RWKCNT RTC_REG(4) 44#define RHRCNT RTC_REG(3) /* RTC hour */
41#define RDAYCNT RTC_REG(5) 45#define RWKCNT RTC_REG(4) /* RTC week */
42#define RMONCNT RTC_REG(6) 46#define RDAYCNT RTC_REG(5) /* RTC day */
43#define RYRCNT RTC_REG(7) 47#define RMONCNT RTC_REG(6) /* RTC month */
44#define RSECAR RTC_REG(8) 48#define RYRCNT RTC_REG(7) /* RTC year */
45#define RMINAR RTC_REG(9) 49#define RSECAR RTC_REG(8) /* ALARM sec */
46#define RHRAR RTC_REG(10) 50#define RMINAR RTC_REG(9) /* ALARM min */
47#define RWKAR RTC_REG(11) 51#define RHRAR RTC_REG(10) /* ALARM hour */
48#define RDAYAR RTC_REG(12) 52#define RWKAR RTC_REG(11) /* ALARM week */
49#define RMONAR RTC_REG(13) 53#define RDAYAR RTC_REG(12) /* ALARM day */
50#define RCR1 RTC_REG(14) 54#define RMONAR RTC_REG(13) /* ALARM month */
51#define RCR2 RTC_REG(15) 55#define RCR1 RTC_REG(14) /* Control */
56#define RCR2 RTC_REG(15) /* Control */
57
58/* ALARM Bits - or with BCD encoded value */
59#define AR_ENB 0x80 /* Enable for alarm cmp */
52 60
53/* RCR1 Bits */ 61/* RCR1 Bits */
54#define RCR1_CF 0x80 /* Carry Flag */ 62#define RCR1_CF 0x80 /* Carry Flag */
@@ -71,6 +79,7 @@ struct sh_rtc {
71 unsigned int alarm_irq, periodic_irq, carry_irq; 79 unsigned int alarm_irq, periodic_irq, carry_irq;
72 struct rtc_device *rtc_dev; 80 struct rtc_device *rtc_dev;
73 spinlock_t lock; 81 spinlock_t lock;
82 int rearm_aie;
74}; 83};
75 84
76static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) 85static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
@@ -82,11 +91,16 @@ static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
82 spin_lock(&rtc->lock); 91 spin_lock(&rtc->lock);
83 92
84 tmp = readb(rtc->regbase + RCR1); 93 tmp = readb(rtc->regbase + RCR1);
94 tmp &= ~RCR1_CF;
85 95
86 if (tmp & RCR1_AF) 96 if (rtc->rearm_aie) {
87 events |= RTC_AF | RTC_IRQF; 97 if (tmp & RCR1_AF)
88 98 tmp &= ~RCR1_AF; /* try to clear AF again */
89 tmp &= ~(RCR1_CF | RCR1_AF); 99 else {
100 tmp |= RCR1_AIE; /* AF has cleared, rearm IRQ */
101 rtc->rearm_aie = 0;
102 }
103 }
90 104
91 writeb(tmp, rtc->regbase + RCR1); 105 writeb(tmp, rtc->regbase + RCR1);
92 106
@@ -97,6 +111,41 @@ static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
97 return IRQ_HANDLED; 111 return IRQ_HANDLED;
98} 112}
99 113
114static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
115{
116 struct platform_device *pdev = to_platform_device(dev_id);
117 struct sh_rtc *rtc = platform_get_drvdata(pdev);
118 unsigned int tmp, events = 0;
119
120 spin_lock(&rtc->lock);
121
122 tmp = readb(rtc->regbase + RCR1);
123
124 /*
125 * If AF is set then the alarm has triggered. If we clear AF while
126 * the alarm time still matches the RTC time then AF will
127 * immediately be set again, and if AIE is enabled then the alarm
128 * interrupt will immediately be retrigger. So we clear AIE here
129 * and use rtc->rearm_aie so that the carry interrupt will keep
130 * trying to clear AF and once it stays cleared it'll re-enable
131 * AIE.
132 */
133 if (tmp & RCR1_AF) {
134 events |= RTC_AF | RTC_IRQF;
135
136 tmp &= ~(RCR1_AF|RCR1_AIE);
137
138 writeb(tmp, rtc->regbase + RCR1);
139
140 rtc->rearm_aie = 1;
141
142 rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events);
143 }
144
145 spin_unlock(&rtc->lock);
146 return IRQ_HANDLED;
147}
148
100static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) 149static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)
101{ 150{
102 struct platform_device *pdev = to_platform_device(dev_id); 151 struct platform_device *pdev = to_platform_device(dev_id);
@@ -140,10 +189,11 @@ static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
140 189
141 tmp = readb(rtc->regbase + RCR1); 190 tmp = readb(rtc->regbase + RCR1);
142 191
143 if (enable) 192 if (!enable) {
144 tmp |= RCR1_AIE;
145 else
146 tmp &= ~RCR1_AIE; 193 tmp &= ~RCR1_AIE;
194 rtc->rearm_aie = 0;
195 } else if (rtc->rearm_aie == 0)
196 tmp |= RCR1_AIE;
147 197
148 writeb(tmp, rtc->regbase + RCR1); 198 writeb(tmp, rtc->regbase + RCR1);
149 199
@@ -178,7 +228,7 @@ static int sh_rtc_open(struct device *dev)
178 goto err_bad_carry; 228 goto err_bad_carry;
179 } 229 }
180 230
181 ret = request_irq(rtc->alarm_irq, sh_rtc_interrupt, IRQF_DISABLED, 231 ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED,
182 "sh-rtc alarm", dev); 232 "sh-rtc alarm", dev);
183 if (unlikely(ret)) { 233 if (unlikely(ret)) {
184 dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n", 234 dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n",
@@ -201,6 +251,7 @@ static void sh_rtc_release(struct device *dev)
201 struct sh_rtc *rtc = dev_get_drvdata(dev); 251 struct sh_rtc *rtc = dev_get_drvdata(dev);
202 252
203 sh_rtc_setpie(dev, 0); 253 sh_rtc_setpie(dev, 0);
254 sh_rtc_setaie(dev, 0);
204 255
205 free_irq(rtc->periodic_irq, dev); 256 free_irq(rtc->periodic_irq, dev);
206 free_irq(rtc->carry_irq, dev); 257 free_irq(rtc->carry_irq, dev);
@@ -345,12 +396,136 @@ static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
345 return 0; 396 return 0;
346} 397}
347 398
399static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off)
400{
401 unsigned int byte;
402 int value = 0xff; /* return 0xff for ignored values */
403
404 byte = readb(rtc->regbase + reg_off);
405 if (byte & AR_ENB) {
406 byte &= ~AR_ENB; /* strip the enable bit */
407 value = BCD2BIN(byte);
408 }
409
410 return value;
411}
412
413static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
414{
415 struct platform_device *pdev = to_platform_device(dev);
416 struct sh_rtc *rtc = platform_get_drvdata(pdev);
417 struct rtc_time* tm = &wkalrm->time;
418
419 spin_lock_irq(&rtc->lock);
420
421 tm->tm_sec = sh_rtc_read_alarm_value(rtc, RSECAR);
422 tm->tm_min = sh_rtc_read_alarm_value(rtc, RMINAR);
423 tm->tm_hour = sh_rtc_read_alarm_value(rtc, RHRAR);
424 tm->tm_wday = sh_rtc_read_alarm_value(rtc, RWKAR);
425 tm->tm_mday = sh_rtc_read_alarm_value(rtc, RDAYAR);
426 tm->tm_mon = sh_rtc_read_alarm_value(rtc, RMONAR);
427 if (tm->tm_mon > 0)
428 tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
429 tm->tm_year = 0xffff;
430
431 spin_unlock_irq(&rtc->lock);
432
433 return 0;
434}
435
436static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc,
437 int value, int reg_off)
438{
439 /* < 0 for a value that is ignored */
440 if (value < 0)
441 writeb(0, rtc->regbase + reg_off);
442 else
443 writeb(BIN2BCD(value) | AR_ENB, rtc->regbase + reg_off);
444}
445
446static int sh_rtc_check_alarm(struct rtc_time* tm)
447{
448 /*
449 * The original rtc says anything > 0xc0 is "don't care" or "match
450 * all" - most users use 0xff but rtc-dev uses -1 for the same thing.
451 * The original rtc doesn't support years - some things use -1 and
452 * some 0xffff. We use -1 to make out tests easier.
453 */
454 if (tm->tm_year == 0xffff)
455 tm->tm_year = -1;
456 if (tm->tm_mon >= 0xff)
457 tm->tm_mon = -1;
458 if (tm->tm_mday >= 0xff)
459 tm->tm_mday = -1;
460 if (tm->tm_wday >= 0xff)
461 tm->tm_wday = -1;
462 if (tm->tm_hour >= 0xff)
463 tm->tm_hour = -1;
464 if (tm->tm_min >= 0xff)
465 tm->tm_min = -1;
466 if (tm->tm_sec >= 0xff)
467 tm->tm_sec = -1;
468
469 if (tm->tm_year > 9999 ||
470 tm->tm_mon >= 12 ||
471 tm->tm_mday == 0 || tm->tm_mday >= 32 ||
472 tm->tm_wday >= 7 ||
473 tm->tm_hour >= 24 ||
474 tm->tm_min >= 60 ||
475 tm->tm_sec >= 60)
476 return -EINVAL;
477
478 return 0;
479}
480
481static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
482{
483 struct platform_device *pdev = to_platform_device(dev);
484 struct sh_rtc *rtc = platform_get_drvdata(pdev);
485 unsigned int rcr1;
486 struct rtc_time *tm = &wkalrm->time;
487 int mon, err;
488
489 err = sh_rtc_check_alarm(tm);
490 if (unlikely(err < 0))
491 return err;
492
493 spin_lock_irq(&rtc->lock);
494
495 /* disable alarm interrupt and clear flag */
496 rcr1 = readb(rtc->regbase + RCR1);
497 rcr1 &= ~RCR1_AF;
498 writeb(rcr1 & ~RCR1_AIE, rtc->regbase + RCR1);
499
500 rtc->rearm_aie = 0;
501
502 /* set alarm time */
503 sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR);
504 sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR);
505 sh_rtc_write_alarm_value(rtc, tm->tm_hour, RHRAR);
506 sh_rtc_write_alarm_value(rtc, tm->tm_wday, RWKAR);
507 sh_rtc_write_alarm_value(rtc, tm->tm_mday, RDAYAR);
508 mon = tm->tm_mon;
509 if (mon >= 0)
510 mon += 1;
511 sh_rtc_write_alarm_value(rtc, mon, RMONAR);
512
513 /* Restore interrupt activation status */
514 writeb(rcr1, rtc->regbase + RCR1);
515
516 spin_unlock_irq(&rtc->lock);
517
518 return 0;
519}
520
348static struct rtc_class_ops sh_rtc_ops = { 521static struct rtc_class_ops sh_rtc_ops = {
349 .open = sh_rtc_open, 522 .open = sh_rtc_open,
350 .release = sh_rtc_release, 523 .release = sh_rtc_release,
351 .ioctl = sh_rtc_ioctl, 524 .ioctl = sh_rtc_ioctl,
352 .read_time = sh_rtc_read_time, 525 .read_time = sh_rtc_read_time,
353 .set_time = sh_rtc_set_time, 526 .set_time = sh_rtc_set_time,
527 .read_alarm = sh_rtc_read_alarm,
528 .set_alarm = sh_rtc_set_alarm,
354 .proc = sh_rtc_proc, 529 .proc = sh_rtc_proc,
355}; 530};
356 531
@@ -443,7 +618,7 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev)
443} 618}
444static struct platform_driver sh_rtc_platform_driver = { 619static struct platform_driver sh_rtc_platform_driver = {
445 .driver = { 620 .driver = {
446 .name = "sh-rtc", 621 .name = DRV_NAME,
447 .owner = THIS_MODULE, 622 .owner = THIS_MODULE,
448 }, 623 },
449 .probe = sh_rtc_probe, 624 .probe = sh_rtc_probe,
@@ -464,5 +639,6 @@ module_init(sh_rtc_init);
464module_exit(sh_rtc_exit); 639module_exit(sh_rtc_exit);
465 640
466MODULE_DESCRIPTION("SuperH on-chip RTC driver"); 641MODULE_DESCRIPTION("SuperH on-chip RTC driver");
467MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); 642MODULE_VERSION(DRV_VERSION);
643MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, Jamie Lenehan <lenehan@twibble.org>");
468MODULE_LICENSE("GPL"); 644MODULE_LICENSE("GPL");