blob: 5ed66acf8ca5113da9a743f54d53b88644a7ab97 [file] [log] [blame]
Paul Mundt317a6102006-09-27 17:13:19 +09001/*
2 * SuperH On-Chip RTC Support
3 *
Angelo Castellob420b1a2008-03-06 12:50:53 +09004 * Copyright (C) 2006, 2007, 2008 Paul Mundt
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +09005 * Copyright (C) 2006 Jamie Lenehan
Angelo Castellob420b1a2008-03-06 12:50:53 +09006 * Copyright (C) 2008 Angelo Castello
Paul Mundt317a6102006-09-27 17:13:19 +09007 *
8 * Based on the old arch/sh/kernel/cpu/rtc.c by:
9 *
10 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
11 * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
12 *
13 * This file is subject to the terms and conditions of the GNU General Public
14 * License. See the file "COPYING" in the main directory of this archive
15 * for more details.
16 */
17#include <linux/module.h>
18#include <linux/kernel.h>
19#include <linux/bcd.h>
20#include <linux/rtc.h>
21#include <linux/init.h>
22#include <linux/platform_device.h>
23#include <linux/seq_file.h>
24#include <linux/interrupt.h>
25#include <linux/spinlock.h>
Jamie Lenehan31ccb082006-12-07 17:23:50 +090026#include <linux/io.h>
Paul Mundtad89f872007-08-03 14:19:58 +090027#include <asm/rtc.h>
Paul Mundt317a6102006-09-27 17:13:19 +090028
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +090029#define DRV_NAME "sh-rtc"
Angelo Castellob420b1a2008-03-06 12:50:53 +090030#define DRV_VERSION "0.2.0"
Paul Mundt317a6102006-09-27 17:13:19 +090031
32#define RTC_REG(r) ((r) * rtc_reg_size)
33
Jamie Lenehan31ccb082006-12-07 17:23:50 +090034#define R64CNT RTC_REG(0)
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +090035
36#define RSECCNT RTC_REG(1) /* RTC sec */
37#define RMINCNT RTC_REG(2) /* RTC min */
38#define RHRCNT RTC_REG(3) /* RTC hour */
39#define RWKCNT RTC_REG(4) /* RTC week */
40#define RDAYCNT RTC_REG(5) /* RTC day */
41#define RMONCNT RTC_REG(6) /* RTC month */
42#define RYRCNT RTC_REG(7) /* RTC year */
43#define RSECAR RTC_REG(8) /* ALARM sec */
44#define RMINAR RTC_REG(9) /* ALARM min */
45#define RHRAR RTC_REG(10) /* ALARM hour */
46#define RWKAR RTC_REG(11) /* ALARM week */
47#define RDAYAR RTC_REG(12) /* ALARM day */
48#define RMONAR RTC_REG(13) /* ALARM month */
49#define RCR1 RTC_REG(14) /* Control */
50#define RCR2 RTC_REG(15) /* Control */
51
Paul Mundtff1b7502007-11-26 17:56:31 +090052/*
53 * Note on RYRAR and RCR3: Up until this point most of the register
54 * definitions are consistent across all of the available parts. However,
55 * the placement of the optional RYRAR and RCR3 (the RYRAR control
56 * register used to control RYRCNT/RYRAR compare) varies considerably
57 * across various parts, occasionally being mapped in to a completely
58 * unrelated address space. For proper RYRAR support a separate resource
59 * would have to be handed off, but as this is purely optional in
60 * practice, we simply opt not to support it, thereby keeping the code
61 * quite a bit more simplified.
62 */
63
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +090064/* ALARM Bits - or with BCD encoded value */
65#define AR_ENB 0x80 /* Enable for alarm cmp */
Paul Mundt317a6102006-09-27 17:13:19 +090066
Angelo Castellob420b1a2008-03-06 12:50:53 +090067/* Period Bits */
68#define PF_HP 0x100 /* Enable Half Period to support 8,32,128Hz */
69#define PF_COUNT 0x200 /* Half periodic counter */
70#define PF_OXS 0x400 /* Periodic One x Second */
71#define PF_KOU 0x800 /* Kernel or User periodic request 1=kernel */
72#define PF_MASK 0xf00
73
Paul Mundt317a6102006-09-27 17:13:19 +090074/* RCR1 Bits */
75#define RCR1_CF 0x80 /* Carry Flag */
76#define RCR1_CIE 0x10 /* Carry Interrupt Enable */
77#define RCR1_AIE 0x08 /* Alarm Interrupt Enable */
78#define RCR1_AF 0x01 /* Alarm Flag */
79
80/* RCR2 Bits */
81#define RCR2_PEF 0x80 /* PEriodic interrupt Flag */
82#define RCR2_PESMASK 0x70 /* Periodic interrupt Set */
83#define RCR2_RTCEN 0x08 /* ENable RTC */
84#define RCR2_ADJ 0x04 /* ADJustment (30-second) */
85#define RCR2_RESET 0x02 /* Reset bit */
86#define RCR2_START 0x01 /* Start bit */
87
88struct sh_rtc {
89 void __iomem *regbase;
90 unsigned long regsize;
91 struct resource *res;
Anton Vorontsov2fac6672009-01-06 14:42:11 -080092 int alarm_irq;
93 int periodic_irq;
94 int carry_irq;
Paul Mundt317a6102006-09-27 17:13:19 +090095 struct rtc_device *rtc_dev;
96 spinlock_t lock;
Paul Mundtad89f872007-08-03 14:19:58 +090097 unsigned long capabilities; /* See asm-sh/rtc.h for cap bits */
Angelo Castellob420b1a2008-03-06 12:50:53 +090098 unsigned short periodic_freq;
Paul Mundt317a6102006-09-27 17:13:19 +090099};
100
Jamie Lenehan31ccb082006-12-07 17:23:50 +0900101static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
Paul Mundt317a6102006-09-27 17:13:19 +0900102{
Angelo Castellob420b1a2008-03-06 12:50:53 +0900103 struct sh_rtc *rtc = dev_id;
104 unsigned int tmp;
Paul Mundt317a6102006-09-27 17:13:19 +0900105
106 spin_lock(&rtc->lock);
107
108 tmp = readb(rtc->regbase + RCR1);
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900109 tmp &= ~RCR1_CF;
Paul Mundt317a6102006-09-27 17:13:19 +0900110 writeb(tmp, rtc->regbase + RCR1);
111
Angelo Castellob420b1a2008-03-06 12:50:53 +0900112 /* Users have requested One x Second IRQ */
113 if (rtc->periodic_freq & PF_OXS)
114 rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
Paul Mundt317a6102006-09-27 17:13:19 +0900115
116 spin_unlock(&rtc->lock);
117
118 return IRQ_HANDLED;
119}
120
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900121static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
122{
Angelo Castellob420b1a2008-03-06 12:50:53 +0900123 struct sh_rtc *rtc = dev_id;
124 unsigned int tmp;
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900125
126 spin_lock(&rtc->lock);
127
128 tmp = readb(rtc->regbase + RCR1);
Angelo Castellob420b1a2008-03-06 12:50:53 +0900129 tmp &= ~(RCR1_AF | RCR1_AIE);
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900130 writeb(tmp, rtc->regbase + RCR1);
131
Angelo Castellob420b1a2008-03-06 12:50:53 +0900132 rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900133
134 spin_unlock(&rtc->lock);
Angelo Castellob420b1a2008-03-06 12:50:53 +0900135
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900136 return IRQ_HANDLED;
137}
138
Jamie Lenehan31ccb082006-12-07 17:23:50 +0900139static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)
Paul Mundt317a6102006-09-27 17:13:19 +0900140{
Angelo Castellob420b1a2008-03-06 12:50:53 +0900141 struct sh_rtc *rtc = dev_id;
142 struct rtc_device *rtc_dev = rtc->rtc_dev;
143 unsigned int tmp;
Paul Mundt317a6102006-09-27 17:13:19 +0900144
145 spin_lock(&rtc->lock);
146
Angelo Castellob420b1a2008-03-06 12:50:53 +0900147 tmp = readb(rtc->regbase + RCR2);
148 tmp &= ~RCR2_PEF;
149 writeb(tmp, rtc->regbase + RCR2);
150
151 /* Half period enabled than one skipped and the next notified */
152 if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT))
153 rtc->periodic_freq &= ~PF_COUNT;
154 else {
155 if (rtc->periodic_freq & PF_HP)
156 rtc->periodic_freq |= PF_COUNT;
157 if (rtc->periodic_freq & PF_KOU) {
158 spin_lock(&rtc_dev->irq_task_lock);
159 if (rtc_dev->irq_task)
160 rtc_dev->irq_task->func(rtc_dev->irq_task->private_data);
161 spin_unlock(&rtc_dev->irq_task_lock);
162 } else
163 rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF);
164 }
Paul Mundt317a6102006-09-27 17:13:19 +0900165
166 spin_unlock(&rtc->lock);
167
168 return IRQ_HANDLED;
169}
170
171static inline void sh_rtc_setpie(struct device *dev, unsigned int enable)
172{
173 struct sh_rtc *rtc = dev_get_drvdata(dev);
174 unsigned int tmp;
175
176 spin_lock_irq(&rtc->lock);
177
178 tmp = readb(rtc->regbase + RCR2);
179
180 if (enable) {
Angelo Castellob420b1a2008-03-06 12:50:53 +0900181 tmp &= ~RCR2_PEF; /* Clear PES bit */
182 tmp |= (rtc->periodic_freq & ~PF_HP); /* Set PES2-0 */
Paul Mundt317a6102006-09-27 17:13:19 +0900183 } else
184 tmp &= ~(RCR2_PESMASK | RCR2_PEF);
185
186 writeb(tmp, rtc->regbase + RCR2);
187
188 spin_unlock_irq(&rtc->lock);
189}
190
Angelo Castellob420b1a2008-03-06 12:50:53 +0900191static inline int sh_rtc_setfreq(struct device *dev, unsigned int freq)
192{
193 struct sh_rtc *rtc = dev_get_drvdata(dev);
194 int tmp, ret = 0;
195
196 spin_lock_irq(&rtc->lock);
197 tmp = rtc->periodic_freq & PF_MASK;
198
199 switch (freq) {
200 case 0:
201 rtc->periodic_freq = 0x00;
202 break;
203 case 1:
204 rtc->periodic_freq = 0x60;
205 break;
206 case 2:
207 rtc->periodic_freq = 0x50;
208 break;
209 case 4:
210 rtc->periodic_freq = 0x40;
211 break;
212 case 8:
213 rtc->periodic_freq = 0x30 | PF_HP;
214 break;
215 case 16:
216 rtc->periodic_freq = 0x30;
217 break;
218 case 32:
219 rtc->periodic_freq = 0x20 | PF_HP;
220 break;
221 case 64:
222 rtc->periodic_freq = 0x20;
223 break;
224 case 128:
225 rtc->periodic_freq = 0x10 | PF_HP;
226 break;
227 case 256:
228 rtc->periodic_freq = 0x10;
229 break;
230 default:
231 ret = -ENOTSUPP;
232 }
233
234 if (ret == 0) {
235 rtc->periodic_freq |= tmp;
236 rtc->rtc_dev->irq_freq = freq;
237 }
238
239 spin_unlock_irq(&rtc->lock);
240 return ret;
241}
242
Paul Mundt317a6102006-09-27 17:13:19 +0900243static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
244{
245 struct sh_rtc *rtc = dev_get_drvdata(dev);
246 unsigned int tmp;
247
248 spin_lock_irq(&rtc->lock);
249
250 tmp = readb(rtc->regbase + RCR1);
251
Angelo Castellob420b1a2008-03-06 12:50:53 +0900252 if (!enable)
Paul Mundt317a6102006-09-27 17:13:19 +0900253 tmp &= ~RCR1_AIE;
Angelo Castellob420b1a2008-03-06 12:50:53 +0900254 else
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900255 tmp |= RCR1_AIE;
Paul Mundt317a6102006-09-27 17:13:19 +0900256
257 writeb(tmp, rtc->regbase + RCR1);
258
259 spin_unlock_irq(&rtc->lock);
260}
261
Paul Mundt317a6102006-09-27 17:13:19 +0900262static int sh_rtc_proc(struct device *dev, struct seq_file *seq)
263{
264 struct sh_rtc *rtc = dev_get_drvdata(dev);
265 unsigned int tmp;
266
267 tmp = readb(rtc->regbase + RCR1);
Angelo Castellob420b1a2008-03-06 12:50:53 +0900268 seq_printf(seq, "carry_IRQ\t: %s\n", (tmp & RCR1_CIE) ? "yes" : "no");
Paul Mundt317a6102006-09-27 17:13:19 +0900269
270 tmp = readb(rtc->regbase + RCR2);
271 seq_printf(seq, "periodic_IRQ\t: %s\n",
Angelo Castellob420b1a2008-03-06 12:50:53 +0900272 (tmp & RCR2_PESMASK) ? "yes" : "no");
Paul Mundt317a6102006-09-27 17:13:19 +0900273
274 return 0;
275}
276
277static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
278{
Angelo Castellob420b1a2008-03-06 12:50:53 +0900279 struct sh_rtc *rtc = dev_get_drvdata(dev);
280 unsigned int ret = 0;
Paul Mundt317a6102006-09-27 17:13:19 +0900281
282 switch (cmd) {
283 case RTC_PIE_OFF:
284 case RTC_PIE_ON:
285 sh_rtc_setpie(dev, cmd == RTC_PIE_ON);
Paul Mundt317a6102006-09-27 17:13:19 +0900286 break;
287 case RTC_AIE_OFF:
288 case RTC_AIE_ON:
289 sh_rtc_setaie(dev, cmd == RTC_AIE_ON);
Paul Mundt317a6102006-09-27 17:13:19 +0900290 break;
Angelo Castellob420b1a2008-03-06 12:50:53 +0900291 case RTC_UIE_OFF:
292 rtc->periodic_freq &= ~PF_OXS;
293 break;
294 case RTC_UIE_ON:
295 rtc->periodic_freq |= PF_OXS;
296 break;
297 case RTC_IRQP_READ:
298 ret = put_user(rtc->rtc_dev->irq_freq,
299 (unsigned long __user *)arg);
300 break;
301 case RTC_IRQP_SET:
302 ret = sh_rtc_setfreq(dev, arg);
303 break;
304 default:
305 ret = -ENOIOCTLCMD;
Paul Mundt317a6102006-09-27 17:13:19 +0900306 }
307
308 return ret;
309}
310
311static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
312{
313 struct platform_device *pdev = to_platform_device(dev);
314 struct sh_rtc *rtc = platform_get_drvdata(pdev);
315 unsigned int sec128, sec2, yr, yr100, cf_bit;
316
317 do {
318 unsigned int tmp;
319
320 spin_lock_irq(&rtc->lock);
321
322 tmp = readb(rtc->regbase + RCR1);
323 tmp &= ~RCR1_CF; /* Clear CF-bit */
324 tmp |= RCR1_CIE;
325 writeb(tmp, rtc->regbase + RCR1);
326
327 sec128 = readb(rtc->regbase + R64CNT);
328
Adrian Bunkfe20ba72008-10-18 20:28:41 -0700329 tm->tm_sec = bcd2bin(readb(rtc->regbase + RSECCNT));
330 tm->tm_min = bcd2bin(readb(rtc->regbase + RMINCNT));
331 tm->tm_hour = bcd2bin(readb(rtc->regbase + RHRCNT));
332 tm->tm_wday = bcd2bin(readb(rtc->regbase + RWKCNT));
333 tm->tm_mday = bcd2bin(readb(rtc->regbase + RDAYCNT));
334 tm->tm_mon = bcd2bin(readb(rtc->regbase + RMONCNT)) - 1;
Paul Mundt317a6102006-09-27 17:13:19 +0900335
Paul Mundtad89f872007-08-03 14:19:58 +0900336 if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) {
337 yr = readw(rtc->regbase + RYRCNT);
Adrian Bunkfe20ba72008-10-18 20:28:41 -0700338 yr100 = bcd2bin(yr >> 8);
Paul Mundtad89f872007-08-03 14:19:58 +0900339 yr &= 0xff;
340 } else {
341 yr = readb(rtc->regbase + RYRCNT);
Adrian Bunkfe20ba72008-10-18 20:28:41 -0700342 yr100 = bcd2bin((yr == 0x99) ? 0x19 : 0x20);
Paul Mundtad89f872007-08-03 14:19:58 +0900343 }
Paul Mundt317a6102006-09-27 17:13:19 +0900344
Adrian Bunkfe20ba72008-10-18 20:28:41 -0700345 tm->tm_year = (yr100 * 100 + bcd2bin(yr)) - 1900;
Paul Mundt317a6102006-09-27 17:13:19 +0900346
347 sec2 = readb(rtc->regbase + R64CNT);
348 cf_bit = readb(rtc->regbase + RCR1) & RCR1_CF;
349
350 spin_unlock_irq(&rtc->lock);
351 } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0);
352
353#if RTC_BIT_INVERTED != 0
354 if ((sec128 & RTC_BIT_INVERTED))
355 tm->tm_sec--;
356#endif
357
Paul Mundt435c55d2007-05-08 11:56:27 +0900358 dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
Paul Mundt317a6102006-09-27 17:13:19 +0900359 "mday=%d, mon=%d, year=%d, wday=%d\n",
Harvey Harrison2a4e2b82008-04-28 02:12:00 -0700360 __func__,
Paul Mundt317a6102006-09-27 17:13:19 +0900361 tm->tm_sec, tm->tm_min, tm->tm_hour,
Jamie Lenehana1614792006-12-08 14:49:30 +0900362 tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
Paul Mundt317a6102006-09-27 17:13:19 +0900363
Paul Mundt0ac554b2007-11-07 20:13:24 +0900364 if (rtc_valid_tm(tm) < 0) {
Paul Mundt317a6102006-09-27 17:13:19 +0900365 dev_err(dev, "invalid date\n");
Paul Mundt0ac554b2007-11-07 20:13:24 +0900366 rtc_time_to_tm(0, tm);
367 }
Paul Mundt317a6102006-09-27 17:13:19 +0900368
369 return 0;
370}
371
372static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
373{
374 struct platform_device *pdev = to_platform_device(dev);
375 struct sh_rtc *rtc = platform_get_drvdata(pdev);
376 unsigned int tmp;
377 int year;
378
379 spin_lock_irq(&rtc->lock);
380
381 /* Reset pre-scaler & stop RTC */
382 tmp = readb(rtc->regbase + RCR2);
383 tmp |= RCR2_RESET;
Markus Brunner699bc662007-07-26 17:31:28 +0900384 tmp &= ~RCR2_START;
Paul Mundt317a6102006-09-27 17:13:19 +0900385 writeb(tmp, rtc->regbase + RCR2);
386
Adrian Bunkfe20ba72008-10-18 20:28:41 -0700387 writeb(bin2bcd(tm->tm_sec), rtc->regbase + RSECCNT);
388 writeb(bin2bcd(tm->tm_min), rtc->regbase + RMINCNT);
389 writeb(bin2bcd(tm->tm_hour), rtc->regbase + RHRCNT);
390 writeb(bin2bcd(tm->tm_wday), rtc->regbase + RWKCNT);
391 writeb(bin2bcd(tm->tm_mday), rtc->regbase + RDAYCNT);
392 writeb(bin2bcd(tm->tm_mon + 1), rtc->regbase + RMONCNT);
Paul Mundt317a6102006-09-27 17:13:19 +0900393
Paul Mundtad89f872007-08-03 14:19:58 +0900394 if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) {
Adrian Bunkfe20ba72008-10-18 20:28:41 -0700395 year = (bin2bcd((tm->tm_year + 1900) / 100) << 8) |
396 bin2bcd(tm->tm_year % 100);
Paul Mundtad89f872007-08-03 14:19:58 +0900397 writew(year, rtc->regbase + RYRCNT);
398 } else {
399 year = tm->tm_year % 100;
Adrian Bunkfe20ba72008-10-18 20:28:41 -0700400 writeb(bin2bcd(year), rtc->regbase + RYRCNT);
Paul Mundtad89f872007-08-03 14:19:58 +0900401 }
Paul Mundt317a6102006-09-27 17:13:19 +0900402
403 /* Start RTC */
404 tmp = readb(rtc->regbase + RCR2);
405 tmp &= ~RCR2_RESET;
406 tmp |= RCR2_RTCEN | RCR2_START;
407 writeb(tmp, rtc->regbase + RCR2);
408
409 spin_unlock_irq(&rtc->lock);
410
411 return 0;
412}
413
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900414static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off)
415{
416 unsigned int byte;
417 int value = 0xff; /* return 0xff for ignored values */
418
419 byte = readb(rtc->regbase + reg_off);
420 if (byte & AR_ENB) {
421 byte &= ~AR_ENB; /* strip the enable bit */
Adrian Bunkfe20ba72008-10-18 20:28:41 -0700422 value = bcd2bin(byte);
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900423 }
424
425 return value;
426}
427
428static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
429{
430 struct platform_device *pdev = to_platform_device(dev);
431 struct sh_rtc *rtc = platform_get_drvdata(pdev);
Angelo Castellob420b1a2008-03-06 12:50:53 +0900432 struct rtc_time *tm = &wkalrm->time;
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900433
434 spin_lock_irq(&rtc->lock);
435
436 tm->tm_sec = sh_rtc_read_alarm_value(rtc, RSECAR);
437 tm->tm_min = sh_rtc_read_alarm_value(rtc, RMINAR);
438 tm->tm_hour = sh_rtc_read_alarm_value(rtc, RHRAR);
439 tm->tm_wday = sh_rtc_read_alarm_value(rtc, RWKAR);
440 tm->tm_mday = sh_rtc_read_alarm_value(rtc, RDAYAR);
441 tm->tm_mon = sh_rtc_read_alarm_value(rtc, RMONAR);
442 if (tm->tm_mon > 0)
443 tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
444 tm->tm_year = 0xffff;
445
David Brownell0d103e92007-01-10 23:15:32 -0800446 wkalrm->enabled = (readb(rtc->regbase + RCR1) & RCR1_AIE) ? 1 : 0;
447
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900448 spin_unlock_irq(&rtc->lock);
449
450 return 0;
451}
452
453static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc,
454 int value, int reg_off)
455{
456 /* < 0 for a value that is ignored */
457 if (value < 0)
458 writeb(0, rtc->regbase + reg_off);
459 else
Adrian Bunkfe20ba72008-10-18 20:28:41 -0700460 writeb(bin2bcd(value) | AR_ENB, rtc->regbase + reg_off);
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900461}
462
Angelo Castellob420b1a2008-03-06 12:50:53 +0900463static int sh_rtc_check_alarm(struct rtc_time *tm)
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900464{
465 /*
466 * The original rtc says anything > 0xc0 is "don't care" or "match
467 * all" - most users use 0xff but rtc-dev uses -1 for the same thing.
468 * The original rtc doesn't support years - some things use -1 and
469 * some 0xffff. We use -1 to make out tests easier.
470 */
471 if (tm->tm_year == 0xffff)
472 tm->tm_year = -1;
473 if (tm->tm_mon >= 0xff)
474 tm->tm_mon = -1;
475 if (tm->tm_mday >= 0xff)
476 tm->tm_mday = -1;
477 if (tm->tm_wday >= 0xff)
478 tm->tm_wday = -1;
479 if (tm->tm_hour >= 0xff)
480 tm->tm_hour = -1;
481 if (tm->tm_min >= 0xff)
482 tm->tm_min = -1;
483 if (tm->tm_sec >= 0xff)
484 tm->tm_sec = -1;
485
486 if (tm->tm_year > 9999 ||
487 tm->tm_mon >= 12 ||
488 tm->tm_mday == 0 || tm->tm_mday >= 32 ||
489 tm->tm_wday >= 7 ||
490 tm->tm_hour >= 24 ||
491 tm->tm_min >= 60 ||
492 tm->tm_sec >= 60)
493 return -EINVAL;
494
495 return 0;
496}
497
498static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
499{
500 struct platform_device *pdev = to_platform_device(dev);
501 struct sh_rtc *rtc = platform_get_drvdata(pdev);
502 unsigned int rcr1;
503 struct rtc_time *tm = &wkalrm->time;
504 int mon, err;
505
506 err = sh_rtc_check_alarm(tm);
507 if (unlikely(err < 0))
508 return err;
509
510 spin_lock_irq(&rtc->lock);
511
Jamie Lenehan15c945c2007-01-22 20:40:41 -0800512 /* disable alarm interrupt and clear the alarm flag */
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900513 rcr1 = readb(rtc->regbase + RCR1);
Angelo Castellob420b1a2008-03-06 12:50:53 +0900514 rcr1 &= ~(RCR1_AF | RCR1_AIE);
Jamie Lenehan15c945c2007-01-22 20:40:41 -0800515 writeb(rcr1, rtc->regbase + RCR1);
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900516
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900517 /* set alarm time */
518 sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR);
519 sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR);
520 sh_rtc_write_alarm_value(rtc, tm->tm_hour, RHRAR);
521 sh_rtc_write_alarm_value(rtc, tm->tm_wday, RWKAR);
522 sh_rtc_write_alarm_value(rtc, tm->tm_mday, RDAYAR);
523 mon = tm->tm_mon;
524 if (mon >= 0)
525 mon += 1;
526 sh_rtc_write_alarm_value(rtc, mon, RMONAR);
527
Jamie Lenehan15c945c2007-01-22 20:40:41 -0800528 if (wkalrm->enabled) {
529 rcr1 |= RCR1_AIE;
530 writeb(rcr1, rtc->regbase + RCR1);
531 }
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900532
533 spin_unlock_irq(&rtc->lock);
534
535 return 0;
536}
537
Angelo Castellob420b1a2008-03-06 12:50:53 +0900538static int sh_rtc_irq_set_state(struct device *dev, int enabled)
539{
540 struct platform_device *pdev = to_platform_device(dev);
541 struct sh_rtc *rtc = platform_get_drvdata(pdev);
542
543 if (enabled) {
544 rtc->periodic_freq |= PF_KOU;
545 return sh_rtc_ioctl(dev, RTC_PIE_ON, 0);
546 } else {
547 rtc->periodic_freq &= ~PF_KOU;
548 return sh_rtc_ioctl(dev, RTC_PIE_OFF, 0);
549 }
550}
551
552static int sh_rtc_irq_set_freq(struct device *dev, int freq)
553{
554 return sh_rtc_ioctl(dev, RTC_IRQP_SET, freq);
555}
556
Paul Mundt317a6102006-09-27 17:13:19 +0900557static struct rtc_class_ops sh_rtc_ops = {
Paul Mundt317a6102006-09-27 17:13:19 +0900558 .ioctl = sh_rtc_ioctl,
559 .read_time = sh_rtc_read_time,
560 .set_time = sh_rtc_set_time,
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900561 .read_alarm = sh_rtc_read_alarm,
562 .set_alarm = sh_rtc_set_alarm,
Angelo Castellob420b1a2008-03-06 12:50:53 +0900563 .irq_set_state = sh_rtc_irq_set_state,
564 .irq_set_freq = sh_rtc_irq_set_freq,
Paul Mundt317a6102006-09-27 17:13:19 +0900565 .proc = sh_rtc_proc,
566};
567
568static int __devinit sh_rtc_probe(struct platform_device *pdev)
569{
570 struct sh_rtc *rtc;
571 struct resource *res;
Angelo Castellob420b1a2008-03-06 12:50:53 +0900572 unsigned int tmp;
roel kluin2641dc92008-09-10 19:34:44 +0200573 int ret;
Paul Mundt317a6102006-09-27 17:13:19 +0900574
575 rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL);
576 if (unlikely(!rtc))
577 return -ENOMEM;
578
579 spin_lock_init(&rtc->lock);
580
Angelo Castellob420b1a2008-03-06 12:50:53 +0900581 /* get periodic/carry/alarm irqs */
roel kluin2641dc92008-09-10 19:34:44 +0200582 ret = platform_get_irq(pdev, 0);
Anton Vorontsov2fac6672009-01-06 14:42:11 -0800583 if (unlikely(ret <= 0)) {
roel kluin2641dc92008-09-10 19:34:44 +0200584 ret = -ENOENT;
Paul Mundt317a6102006-09-27 17:13:19 +0900585 dev_err(&pdev->dev, "No IRQ for period\n");
586 goto err_badres;
587 }
roel kluin2641dc92008-09-10 19:34:44 +0200588 rtc->periodic_irq = ret;
Paul Mundt317a6102006-09-27 17:13:19 +0900589
roel kluin2641dc92008-09-10 19:34:44 +0200590 ret = platform_get_irq(pdev, 1);
Anton Vorontsov2fac6672009-01-06 14:42:11 -0800591 if (unlikely(ret <= 0)) {
roel kluin2641dc92008-09-10 19:34:44 +0200592 ret = -ENOENT;
Paul Mundt317a6102006-09-27 17:13:19 +0900593 dev_err(&pdev->dev, "No IRQ for carry\n");
594 goto err_badres;
595 }
roel kluin2641dc92008-09-10 19:34:44 +0200596 rtc->carry_irq = ret;
Paul Mundt317a6102006-09-27 17:13:19 +0900597
roel kluin2641dc92008-09-10 19:34:44 +0200598 ret = platform_get_irq(pdev, 2);
Anton Vorontsov2fac6672009-01-06 14:42:11 -0800599 if (unlikely(ret <= 0)) {
roel kluin2641dc92008-09-10 19:34:44 +0200600 ret = -ENOENT;
Paul Mundt317a6102006-09-27 17:13:19 +0900601 dev_err(&pdev->dev, "No IRQ for alarm\n");
602 goto err_badres;
603 }
roel kluin2641dc92008-09-10 19:34:44 +0200604 rtc->alarm_irq = ret;
Paul Mundt317a6102006-09-27 17:13:19 +0900605
606 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
607 if (unlikely(res == NULL)) {
roel kluin2641dc92008-09-10 19:34:44 +0200608 ret = -ENOENT;
Paul Mundt317a6102006-09-27 17:13:19 +0900609 dev_err(&pdev->dev, "No IO resource\n");
610 goto err_badres;
611 }
612
613 rtc->regsize = res->end - res->start + 1;
614
615 rtc->res = request_mem_region(res->start, rtc->regsize, pdev->name);
616 if (unlikely(!rtc->res)) {
617 ret = -EBUSY;
618 goto err_badres;
619 }
620
Paul Mundt03057942008-04-25 17:58:42 +0900621 rtc->regbase = ioremap_nocache(rtc->res->start, rtc->regsize);
Paul Mundt317a6102006-09-27 17:13:19 +0900622 if (unlikely(!rtc->regbase)) {
623 ret = -EINVAL;
624 goto err_badmap;
625 }
626
627 rtc->rtc_dev = rtc_device_register("sh", &pdev->dev,
628 &sh_rtc_ops, THIS_MODULE);
Paul Mundt29dd0da2007-11-07 14:58:09 +0900629 if (IS_ERR(rtc->rtc_dev)) {
Paul Mundt317a6102006-09-27 17:13:19 +0900630 ret = PTR_ERR(rtc->rtc_dev);
Paul Mundt03057942008-04-25 17:58:42 +0900631 goto err_unmap;
Paul Mundt317a6102006-09-27 17:13:19 +0900632 }
633
Paul Mundtad89f872007-08-03 14:19:58 +0900634 rtc->capabilities = RTC_DEF_CAPABILITIES;
635 if (pdev->dev.platform_data) {
636 struct sh_rtc_platform_info *pinfo = pdev->dev.platform_data;
637
638 /*
639 * Some CPUs have special capabilities in addition to the
640 * default set. Add those in here.
641 */
642 rtc->capabilities |= pinfo->capabilities;
643 }
644
Angelo Castellob420b1a2008-03-06 12:50:53 +0900645 rtc->rtc_dev->max_user_freq = 256;
646 rtc->rtc_dev->irq_freq = 1;
647 rtc->periodic_freq = 0x60;
648
Paul Mundt317a6102006-09-27 17:13:19 +0900649 platform_set_drvdata(pdev, rtc);
650
Angelo Castellob420b1a2008-03-06 12:50:53 +0900651 /* register periodic/carry/alarm irqs */
652 ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED,
653 "sh-rtc period", rtc);
654 if (unlikely(ret)) {
655 dev_err(&pdev->dev,
656 "request period IRQ failed with %d, IRQ %d\n", ret,
657 rtc->periodic_irq);
Paul Mundt03057942008-04-25 17:58:42 +0900658 goto err_unmap;
Angelo Castellob420b1a2008-03-06 12:50:53 +0900659 }
660
661 ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED,
662 "sh-rtc carry", rtc);
663 if (unlikely(ret)) {
664 dev_err(&pdev->dev,
665 "request carry IRQ failed with %d, IRQ %d\n", ret,
666 rtc->carry_irq);
667 free_irq(rtc->periodic_irq, rtc);
Paul Mundt03057942008-04-25 17:58:42 +0900668 goto err_unmap;
Angelo Castellob420b1a2008-03-06 12:50:53 +0900669 }
670
671 ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED,
672 "sh-rtc alarm", rtc);
673 if (unlikely(ret)) {
674 dev_err(&pdev->dev,
675 "request alarm IRQ failed with %d, IRQ %d\n", ret,
676 rtc->alarm_irq);
677 free_irq(rtc->carry_irq, rtc);
678 free_irq(rtc->periodic_irq, rtc);
Paul Mundt03057942008-04-25 17:58:42 +0900679 goto err_unmap;
Angelo Castellob420b1a2008-03-06 12:50:53 +0900680 }
681
682 tmp = readb(rtc->regbase + RCR1);
683 tmp &= ~RCR1_CF;
684 tmp |= RCR1_CIE;
685 writeb(tmp, rtc->regbase + RCR1);
686
Paul Mundt317a6102006-09-27 17:13:19 +0900687 return 0;
688
Paul Mundt03057942008-04-25 17:58:42 +0900689err_unmap:
690 iounmap(rtc->regbase);
Paul Mundt317a6102006-09-27 17:13:19 +0900691err_badmap:
692 release_resource(rtc->res);
693err_badres:
694 kfree(rtc);
695
696 return ret;
697}
698
699static int __devexit sh_rtc_remove(struct platform_device *pdev)
700{
701 struct sh_rtc *rtc = platform_get_drvdata(pdev);
702
703 if (likely(rtc->rtc_dev))
704 rtc_device_unregister(rtc->rtc_dev);
705
706 sh_rtc_setpie(&pdev->dev, 0);
707 sh_rtc_setaie(&pdev->dev, 0);
708
Angelo Castellob420b1a2008-03-06 12:50:53 +0900709 free_irq(rtc->carry_irq, rtc);
710 free_irq(rtc->periodic_irq, rtc);
711 free_irq(rtc->alarm_irq, rtc);
712
Paul Mundt317a6102006-09-27 17:13:19 +0900713 release_resource(rtc->res);
714
Paul Mundt03057942008-04-25 17:58:42 +0900715 iounmap(rtc->regbase);
716
Paul Mundt317a6102006-09-27 17:13:19 +0900717 platform_set_drvdata(pdev, NULL);
718
719 kfree(rtc);
720
721 return 0;
722}
723static struct platform_driver sh_rtc_platform_driver = {
724 .driver = {
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900725 .name = DRV_NAME,
Paul Mundt317a6102006-09-27 17:13:19 +0900726 .owner = THIS_MODULE,
727 },
728 .probe = sh_rtc_probe,
729 .remove = __devexit_p(sh_rtc_remove),
730};
731
732static int __init sh_rtc_init(void)
733{
734 return platform_driver_register(&sh_rtc_platform_driver);
735}
736
737static void __exit sh_rtc_exit(void)
738{
739 platform_driver_unregister(&sh_rtc_platform_driver);
740}
741
742module_init(sh_rtc_init);
743module_exit(sh_rtc_exit);
744
745MODULE_DESCRIPTION("SuperH on-chip RTC driver");
Jamie Lenehan1b73e6a2006-12-08 15:26:15 +0900746MODULE_VERSION(DRV_VERSION);
Angelo Castellob420b1a2008-03-06 12:50:53 +0900747MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, "
748 "Jamie Lenehan <lenehan@twibble.org>, "
749 "Angelo Castello <angelo.castello@st.com>");
Paul Mundt317a6102006-09-27 17:13:19 +0900750MODULE_LICENSE("GPL");
Kay Sieversad28a072008-04-10 21:29:25 -0700751MODULE_ALIAS("platform:" DRV_NAME);