Commit | Line | Data |
---|---|---|
317a6104 PM |
1 | /* |
2 | * SuperH On-Chip RTC Support | |
3 | * | |
ad89f87a | 4 | * Copyright (C) 2006, 2007 Paul Mundt |
1b73e6ae | 5 | * Copyright (C) 2006 Jamie Lenehan |
317a6104 PM |
6 | * |
7 | * Based on the old arch/sh/kernel/cpu/rtc.c by: | |
8 | * | |
9 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | |
10 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | |
11 | * | |
12 | * This file is subject to the terms and conditions of the GNU General Public | |
13 | * License. See the file "COPYING" in the main directory of this archive | |
14 | * for more details. | |
15 | */ | |
16 | #include <linux/module.h> | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/bcd.h> | |
19 | #include <linux/rtc.h> | |
20 | #include <linux/init.h> | |
21 | #include <linux/platform_device.h> | |
22 | #include <linux/seq_file.h> | |
23 | #include <linux/interrupt.h> | |
24 | #include <linux/spinlock.h> | |
31ccb081 | 25 | #include <linux/io.h> |
ad89f87a | 26 | #include <asm/rtc.h> |
317a6104 | 27 | |
1b73e6ae | 28 | #define DRV_NAME "sh-rtc" |
c019fd88 | 29 | #define DRV_VERSION "0.1.6" |
317a6104 PM |
30 | |
31 | #define RTC_REG(r) ((r) * rtc_reg_size) | |
32 | ||
31ccb081 | 33 | #define R64CNT RTC_REG(0) |
1b73e6ae JL |
34 | |
35 | #define RSECCNT RTC_REG(1) /* RTC sec */ | |
36 | #define RMINCNT RTC_REG(2) /* RTC min */ | |
37 | #define RHRCNT RTC_REG(3) /* RTC hour */ | |
38 | #define RWKCNT RTC_REG(4) /* RTC week */ | |
39 | #define RDAYCNT RTC_REG(5) /* RTC day */ | |
40 | #define RMONCNT RTC_REG(6) /* RTC month */ | |
41 | #define RYRCNT RTC_REG(7) /* RTC year */ | |
42 | #define RSECAR RTC_REG(8) /* ALARM sec */ | |
43 | #define RMINAR RTC_REG(9) /* ALARM min */ | |
44 | #define RHRAR RTC_REG(10) /* ALARM hour */ | |
45 | #define RWKAR RTC_REG(11) /* ALARM week */ | |
46 | #define RDAYAR RTC_REG(12) /* ALARM day */ | |
47 | #define RMONAR RTC_REG(13) /* ALARM month */ | |
48 | #define RCR1 RTC_REG(14) /* Control */ | |
49 | #define RCR2 RTC_REG(15) /* Control */ | |
50 | ||
ff1b7506 PM |
51 | /* |
52 | * Note on RYRAR and RCR3: Up until this point most of the register | |
53 | * definitions are consistent across all of the available parts. However, | |
54 | * the placement of the optional RYRAR and RCR3 (the RYRAR control | |
55 | * register used to control RYRCNT/RYRAR compare) varies considerably | |
56 | * across various parts, occasionally being mapped in to a completely | |
57 | * unrelated address space. For proper RYRAR support a separate resource | |
58 | * would have to be handed off, but as this is purely optional in | |
59 | * practice, we simply opt not to support it, thereby keeping the code | |
60 | * quite a bit more simplified. | |
61 | */ | |
62 | ||
1b73e6ae JL |
63 | /* ALARM Bits - or with BCD encoded value */ |
64 | #define AR_ENB 0x80 /* Enable for alarm cmp */ | |
317a6104 PM |
65 | |
66 | /* RCR1 Bits */ | |
67 | #define RCR1_CF 0x80 /* Carry Flag */ | |
68 | #define RCR1_CIE 0x10 /* Carry Interrupt Enable */ | |
69 | #define RCR1_AIE 0x08 /* Alarm Interrupt Enable */ | |
70 | #define RCR1_AF 0x01 /* Alarm Flag */ | |
71 | ||
72 | /* RCR2 Bits */ | |
73 | #define RCR2_PEF 0x80 /* PEriodic interrupt Flag */ | |
74 | #define RCR2_PESMASK 0x70 /* Periodic interrupt Set */ | |
75 | #define RCR2_RTCEN 0x08 /* ENable RTC */ | |
76 | #define RCR2_ADJ 0x04 /* ADJustment (30-second) */ | |
77 | #define RCR2_RESET 0x02 /* Reset bit */ | |
78 | #define RCR2_START 0x01 /* Start bit */ | |
79 | ||
80 | struct sh_rtc { | |
81 | void __iomem *regbase; | |
82 | unsigned long regsize; | |
83 | struct resource *res; | |
84 | unsigned int alarm_irq, periodic_irq, carry_irq; | |
85 | struct rtc_device *rtc_dev; | |
86 | spinlock_t lock; | |
1b73e6ae | 87 | int rearm_aie; |
ad89f87a | 88 | unsigned long capabilities; /* See asm-sh/rtc.h for cap bits */ |
317a6104 PM |
89 | }; |
90 | ||
31ccb081 | 91 | static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) |
317a6104 | 92 | { |
31ccb081 | 93 | struct platform_device *pdev = to_platform_device(dev_id); |
317a6104 PM |
94 | struct sh_rtc *rtc = platform_get_drvdata(pdev); |
95 | unsigned int tmp, events = 0; | |
96 | ||
97 | spin_lock(&rtc->lock); | |
98 | ||
99 | tmp = readb(rtc->regbase + RCR1); | |
1b73e6ae | 100 | tmp &= ~RCR1_CF; |
317a6104 | 101 | |
1b73e6ae JL |
102 | if (rtc->rearm_aie) { |
103 | if (tmp & RCR1_AF) | |
104 | tmp &= ~RCR1_AF; /* try to clear AF again */ | |
105 | else { | |
106 | tmp |= RCR1_AIE; /* AF has cleared, rearm IRQ */ | |
107 | rtc->rearm_aie = 0; | |
108 | } | |
109 | } | |
317a6104 PM |
110 | |
111 | writeb(tmp, rtc->regbase + RCR1); | |
112 | ||
a361a68b | 113 | rtc_update_irq(rtc->rtc_dev, 1, events); |
317a6104 PM |
114 | |
115 | spin_unlock(&rtc->lock); | |
116 | ||
117 | return IRQ_HANDLED; | |
118 | } | |
119 | ||
1b73e6ae JL |
120 | static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) |
121 | { | |
122 | struct platform_device *pdev = to_platform_device(dev_id); | |
123 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | |
124 | unsigned int tmp, events = 0; | |
125 | ||
126 | spin_lock(&rtc->lock); | |
127 | ||
128 | tmp = readb(rtc->regbase + RCR1); | |
129 | ||
130 | /* | |
131 | * If AF is set then the alarm has triggered. If we clear AF while | |
132 | * the alarm time still matches the RTC time then AF will | |
133 | * immediately be set again, and if AIE is enabled then the alarm | |
134 | * interrupt will immediately be retrigger. So we clear AIE here | |
135 | * and use rtc->rearm_aie so that the carry interrupt will keep | |
136 | * trying to clear AF and once it stays cleared it'll re-enable | |
137 | * AIE. | |
138 | */ | |
139 | if (tmp & RCR1_AF) { | |
140 | events |= RTC_AF | RTC_IRQF; | |
141 | ||
142 | tmp &= ~(RCR1_AF|RCR1_AIE); | |
143 | ||
144 | writeb(tmp, rtc->regbase + RCR1); | |
145 | ||
146 | rtc->rearm_aie = 1; | |
147 | ||
a361a68b | 148 | rtc_update_irq(rtc->rtc_dev, 1, events); |
1b73e6ae JL |
149 | } |
150 | ||
151 | spin_unlock(&rtc->lock); | |
152 | return IRQ_HANDLED; | |
153 | } | |
154 | ||
31ccb081 | 155 | static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) |
317a6104 | 156 | { |
31ccb081 JL |
157 | struct platform_device *pdev = to_platform_device(dev_id); |
158 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | |
317a6104 PM |
159 | |
160 | spin_lock(&rtc->lock); | |
161 | ||
a361a68b | 162 | rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); |
317a6104 PM |
163 | |
164 | spin_unlock(&rtc->lock); | |
165 | ||
166 | return IRQ_HANDLED; | |
167 | } | |
168 | ||
169 | static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) | |
170 | { | |
171 | struct sh_rtc *rtc = dev_get_drvdata(dev); | |
172 | unsigned int tmp; | |
173 | ||
174 | spin_lock_irq(&rtc->lock); | |
175 | ||
176 | tmp = readb(rtc->regbase + RCR2); | |
177 | ||
178 | if (enable) { | |
179 | tmp &= ~RCR2_PESMASK; | |
180 | tmp |= RCR2_PEF | (2 << 4); | |
181 | } else | |
182 | tmp &= ~(RCR2_PESMASK | RCR2_PEF); | |
183 | ||
184 | writeb(tmp, rtc->regbase + RCR2); | |
185 | ||
186 | spin_unlock_irq(&rtc->lock); | |
187 | } | |
188 | ||
189 | static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) | |
190 | { | |
191 | struct sh_rtc *rtc = dev_get_drvdata(dev); | |
192 | unsigned int tmp; | |
193 | ||
194 | spin_lock_irq(&rtc->lock); | |
195 | ||
196 | tmp = readb(rtc->regbase + RCR1); | |
197 | ||
1b73e6ae | 198 | if (!enable) { |
317a6104 | 199 | tmp &= ~RCR1_AIE; |
1b73e6ae JL |
200 | rtc->rearm_aie = 0; |
201 | } else if (rtc->rearm_aie == 0) | |
202 | tmp |= RCR1_AIE; | |
317a6104 PM |
203 | |
204 | writeb(tmp, rtc->regbase + RCR1); | |
205 | ||
206 | spin_unlock_irq(&rtc->lock); | |
207 | } | |
208 | ||
209 | static int sh_rtc_open(struct device *dev) | |
210 | { | |
211 | struct sh_rtc *rtc = dev_get_drvdata(dev); | |
212 | unsigned int tmp; | |
213 | int ret; | |
214 | ||
215 | tmp = readb(rtc->regbase + RCR1); | |
216 | tmp &= ~RCR1_CF; | |
217 | tmp |= RCR1_CIE; | |
218 | writeb(tmp, rtc->regbase + RCR1); | |
219 | ||
35f3c518 | 220 | ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED, |
317a6104 PM |
221 | "sh-rtc period", dev); |
222 | if (unlikely(ret)) { | |
223 | dev_err(dev, "request period IRQ failed with %d, IRQ %d\n", | |
224 | ret, rtc->periodic_irq); | |
225 | return ret; | |
226 | } | |
227 | ||
35f3c518 | 228 | ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED, |
317a6104 PM |
229 | "sh-rtc carry", dev); |
230 | if (unlikely(ret)) { | |
231 | dev_err(dev, "request carry IRQ failed with %d, IRQ %d\n", | |
232 | ret, rtc->carry_irq); | |
233 | free_irq(rtc->periodic_irq, dev); | |
234 | goto err_bad_carry; | |
235 | } | |
236 | ||
1b73e6ae | 237 | ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED, |
317a6104 PM |
238 | "sh-rtc alarm", dev); |
239 | if (unlikely(ret)) { | |
240 | dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n", | |
241 | ret, rtc->alarm_irq); | |
242 | goto err_bad_alarm; | |
243 | } | |
244 | ||
245 | return 0; | |
246 | ||
247 | err_bad_alarm: | |
248 | free_irq(rtc->carry_irq, dev); | |
249 | err_bad_carry: | |
250 | free_irq(rtc->periodic_irq, dev); | |
251 | ||
252 | return ret; | |
253 | } | |
254 | ||
255 | static void sh_rtc_release(struct device *dev) | |
256 | { | |
257 | struct sh_rtc *rtc = dev_get_drvdata(dev); | |
258 | ||
259 | sh_rtc_setpie(dev, 0); | |
1b73e6ae | 260 | sh_rtc_setaie(dev, 0); |
317a6104 PM |
261 | |
262 | free_irq(rtc->periodic_irq, dev); | |
263 | free_irq(rtc->carry_irq, dev); | |
264 | free_irq(rtc->alarm_irq, dev); | |
265 | } | |
266 | ||
267 | static int sh_rtc_proc(struct device *dev, struct seq_file *seq) | |
268 | { | |
269 | struct sh_rtc *rtc = dev_get_drvdata(dev); | |
270 | unsigned int tmp; | |
271 | ||
272 | tmp = readb(rtc->regbase + RCR1); | |
317a6104 PM |
273 | seq_printf(seq, "carry_IRQ\t: %s\n", |
274 | (tmp & RCR1_CIE) ? "yes" : "no"); | |
275 | ||
276 | tmp = readb(rtc->regbase + RCR2); | |
277 | seq_printf(seq, "periodic_IRQ\t: %s\n", | |
278 | (tmp & RCR2_PEF) ? "yes" : "no"); | |
279 | ||
280 | return 0; | |
281 | } | |
282 | ||
283 | static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | |
284 | { | |
285 | unsigned int ret = -ENOIOCTLCMD; | |
286 | ||
287 | switch (cmd) { | |
288 | case RTC_PIE_OFF: | |
289 | case RTC_PIE_ON: | |
290 | sh_rtc_setpie(dev, cmd == RTC_PIE_ON); | |
291 | ret = 0; | |
292 | break; | |
293 | case RTC_AIE_OFF: | |
294 | case RTC_AIE_ON: | |
295 | sh_rtc_setaie(dev, cmd == RTC_AIE_ON); | |
296 | ret = 0; | |
297 | break; | |
298 | } | |
299 | ||
300 | return ret; | |
301 | } | |
302 | ||
303 | static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) | |
304 | { | |
305 | struct platform_device *pdev = to_platform_device(dev); | |
306 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | |
307 | unsigned int sec128, sec2, yr, yr100, cf_bit; | |
308 | ||
309 | do { | |
310 | unsigned int tmp; | |
311 | ||
312 | spin_lock_irq(&rtc->lock); | |
313 | ||
314 | tmp = readb(rtc->regbase + RCR1); | |
315 | tmp &= ~RCR1_CF; /* Clear CF-bit */ | |
316 | tmp |= RCR1_CIE; | |
317 | writeb(tmp, rtc->regbase + RCR1); | |
318 | ||
319 | sec128 = readb(rtc->regbase + R64CNT); | |
320 | ||
321 | tm->tm_sec = BCD2BIN(readb(rtc->regbase + RSECCNT)); | |
322 | tm->tm_min = BCD2BIN(readb(rtc->regbase + RMINCNT)); | |
323 | tm->tm_hour = BCD2BIN(readb(rtc->regbase + RHRCNT)); | |
324 | tm->tm_wday = BCD2BIN(readb(rtc->regbase + RWKCNT)); | |
325 | tm->tm_mday = BCD2BIN(readb(rtc->regbase + RDAYCNT)); | |
a1614796 | 326 | tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT)) - 1; |
317a6104 | 327 | |
ad89f87a PM |
328 | if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) { |
329 | yr = readw(rtc->regbase + RYRCNT); | |
330 | yr100 = BCD2BIN(yr >> 8); | |
331 | yr &= 0xff; | |
332 | } else { | |
333 | yr = readb(rtc->regbase + RYRCNT); | |
334 | yr100 = BCD2BIN((yr == 0x99) ? 0x19 : 0x20); | |
335 | } | |
317a6104 PM |
336 | |
337 | tm->tm_year = (yr100 * 100 + BCD2BIN(yr)) - 1900; | |
338 | ||
339 | sec2 = readb(rtc->regbase + R64CNT); | |
340 | cf_bit = readb(rtc->regbase + RCR1) & RCR1_CF; | |
341 | ||
342 | spin_unlock_irq(&rtc->lock); | |
343 | } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0); | |
344 | ||
345 | #if RTC_BIT_INVERTED != 0 | |
346 | if ((sec128 & RTC_BIT_INVERTED)) | |
347 | tm->tm_sec--; | |
348 | #endif | |
349 | ||
435c55d1 | 350 | dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " |
317a6104 PM |
351 | "mday=%d, mon=%d, year=%d, wday=%d\n", |
352 | __FUNCTION__, | |
353 | tm->tm_sec, tm->tm_min, tm->tm_hour, | |
a1614796 | 354 | tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); |
317a6104 | 355 | |
0ac554b9 | 356 | if (rtc_valid_tm(tm) < 0) { |
317a6104 | 357 | dev_err(dev, "invalid date\n"); |
0ac554b9 PM |
358 | rtc_time_to_tm(0, tm); |
359 | } | |
317a6104 PM |
360 | |
361 | return 0; | |
362 | } | |
363 | ||
364 | static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) | |
365 | { | |
366 | struct platform_device *pdev = to_platform_device(dev); | |
367 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | |
368 | unsigned int tmp; | |
369 | int year; | |
370 | ||
371 | spin_lock_irq(&rtc->lock); | |
372 | ||
373 | /* Reset pre-scaler & stop RTC */ | |
374 | tmp = readb(rtc->regbase + RCR2); | |
375 | tmp |= RCR2_RESET; | |
699bc661 | 376 | tmp &= ~RCR2_START; |
317a6104 PM |
377 | writeb(tmp, rtc->regbase + RCR2); |
378 | ||
379 | writeb(BIN2BCD(tm->tm_sec), rtc->regbase + RSECCNT); | |
380 | writeb(BIN2BCD(tm->tm_min), rtc->regbase + RMINCNT); | |
381 | writeb(BIN2BCD(tm->tm_hour), rtc->regbase + RHRCNT); | |
382 | writeb(BIN2BCD(tm->tm_wday), rtc->regbase + RWKCNT); | |
383 | writeb(BIN2BCD(tm->tm_mday), rtc->regbase + RDAYCNT); | |
a1614796 | 384 | writeb(BIN2BCD(tm->tm_mon + 1), rtc->regbase + RMONCNT); |
317a6104 | 385 | |
ad89f87a PM |
386 | if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) { |
387 | year = (BIN2BCD((tm->tm_year + 1900) / 100) << 8) | | |
388 | BIN2BCD(tm->tm_year % 100); | |
389 | writew(year, rtc->regbase + RYRCNT); | |
390 | } else { | |
391 | year = tm->tm_year % 100; | |
392 | writeb(BIN2BCD(year), rtc->regbase + RYRCNT); | |
393 | } | |
317a6104 PM |
394 | |
395 | /* Start RTC */ | |
396 | tmp = readb(rtc->regbase + RCR2); | |
397 | tmp &= ~RCR2_RESET; | |
398 | tmp |= RCR2_RTCEN | RCR2_START; | |
399 | writeb(tmp, rtc->regbase + RCR2); | |
400 | ||
401 | spin_unlock_irq(&rtc->lock); | |
402 | ||
403 | return 0; | |
404 | } | |
405 | ||
1b73e6ae JL |
406 | static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off) |
407 | { | |
408 | unsigned int byte; | |
409 | int value = 0xff; /* return 0xff for ignored values */ | |
410 | ||
411 | byte = readb(rtc->regbase + reg_off); | |
412 | if (byte & AR_ENB) { | |
413 | byte &= ~AR_ENB; /* strip the enable bit */ | |
414 | value = BCD2BIN(byte); | |
415 | } | |
416 | ||
417 | return value; | |
418 | } | |
419 | ||
420 | static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) | |
421 | { | |
422 | struct platform_device *pdev = to_platform_device(dev); | |
423 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | |
424 | struct rtc_time* tm = &wkalrm->time; | |
425 | ||
426 | spin_lock_irq(&rtc->lock); | |
427 | ||
428 | tm->tm_sec = sh_rtc_read_alarm_value(rtc, RSECAR); | |
429 | tm->tm_min = sh_rtc_read_alarm_value(rtc, RMINAR); | |
430 | tm->tm_hour = sh_rtc_read_alarm_value(rtc, RHRAR); | |
431 | tm->tm_wday = sh_rtc_read_alarm_value(rtc, RWKAR); | |
432 | tm->tm_mday = sh_rtc_read_alarm_value(rtc, RDAYAR); | |
433 | tm->tm_mon = sh_rtc_read_alarm_value(rtc, RMONAR); | |
434 | if (tm->tm_mon > 0) | |
435 | tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ | |
436 | tm->tm_year = 0xffff; | |
437 | ||
0d103e90 DB |
438 | wkalrm->enabled = (readb(rtc->regbase + RCR1) & RCR1_AIE) ? 1 : 0; |
439 | ||
1b73e6ae JL |
440 | spin_unlock_irq(&rtc->lock); |
441 | ||
442 | return 0; | |
443 | } | |
444 | ||
445 | static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc, | |
446 | int value, int reg_off) | |
447 | { | |
448 | /* < 0 for a value that is ignored */ | |
449 | if (value < 0) | |
450 | writeb(0, rtc->regbase + reg_off); | |
451 | else | |
452 | writeb(BIN2BCD(value) | AR_ENB, rtc->regbase + reg_off); | |
453 | } | |
454 | ||
455 | static int sh_rtc_check_alarm(struct rtc_time* tm) | |
456 | { | |
457 | /* | |
458 | * The original rtc says anything > 0xc0 is "don't care" or "match | |
459 | * all" - most users use 0xff but rtc-dev uses -1 for the same thing. | |
460 | * The original rtc doesn't support years - some things use -1 and | |
461 | * some 0xffff. We use -1 to make out tests easier. | |
462 | */ | |
463 | if (tm->tm_year == 0xffff) | |
464 | tm->tm_year = -1; | |
465 | if (tm->tm_mon >= 0xff) | |
466 | tm->tm_mon = -1; | |
467 | if (tm->tm_mday >= 0xff) | |
468 | tm->tm_mday = -1; | |
469 | if (tm->tm_wday >= 0xff) | |
470 | tm->tm_wday = -1; | |
471 | if (tm->tm_hour >= 0xff) | |
472 | tm->tm_hour = -1; | |
473 | if (tm->tm_min >= 0xff) | |
474 | tm->tm_min = -1; | |
475 | if (tm->tm_sec >= 0xff) | |
476 | tm->tm_sec = -1; | |
477 | ||
478 | if (tm->tm_year > 9999 || | |
479 | tm->tm_mon >= 12 || | |
480 | tm->tm_mday == 0 || tm->tm_mday >= 32 || | |
481 | tm->tm_wday >= 7 || | |
482 | tm->tm_hour >= 24 || | |
483 | tm->tm_min >= 60 || | |
484 | tm->tm_sec >= 60) | |
485 | return -EINVAL; | |
486 | ||
487 | return 0; | |
488 | } | |
489 | ||
490 | static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) | |
491 | { | |
492 | struct platform_device *pdev = to_platform_device(dev); | |
493 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | |
494 | unsigned int rcr1; | |
495 | struct rtc_time *tm = &wkalrm->time; | |
496 | int mon, err; | |
497 | ||
498 | err = sh_rtc_check_alarm(tm); | |
499 | if (unlikely(err < 0)) | |
500 | return err; | |
501 | ||
502 | spin_lock_irq(&rtc->lock); | |
503 | ||
15c945c3 | 504 | /* disable alarm interrupt and clear the alarm flag */ |
1b73e6ae | 505 | rcr1 = readb(rtc->regbase + RCR1); |
15c945c3 JL |
506 | rcr1 &= ~(RCR1_AF|RCR1_AIE); |
507 | writeb(rcr1, rtc->regbase + RCR1); | |
1b73e6ae JL |
508 | |
509 | rtc->rearm_aie = 0; | |
510 | ||
511 | /* set alarm time */ | |
512 | sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR); | |
513 | sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR); | |
514 | sh_rtc_write_alarm_value(rtc, tm->tm_hour, RHRAR); | |
515 | sh_rtc_write_alarm_value(rtc, tm->tm_wday, RWKAR); | |
516 | sh_rtc_write_alarm_value(rtc, tm->tm_mday, RDAYAR); | |
517 | mon = tm->tm_mon; | |
518 | if (mon >= 0) | |
519 | mon += 1; | |
520 | sh_rtc_write_alarm_value(rtc, mon, RMONAR); | |
521 | ||
15c945c3 JL |
522 | if (wkalrm->enabled) { |
523 | rcr1 |= RCR1_AIE; | |
524 | writeb(rcr1, rtc->regbase + RCR1); | |
525 | } | |
1b73e6ae JL |
526 | |
527 | spin_unlock_irq(&rtc->lock); | |
528 | ||
529 | return 0; | |
530 | } | |
531 | ||
317a6104 PM |
532 | static struct rtc_class_ops sh_rtc_ops = { |
533 | .open = sh_rtc_open, | |
534 | .release = sh_rtc_release, | |
535 | .ioctl = sh_rtc_ioctl, | |
536 | .read_time = sh_rtc_read_time, | |
537 | .set_time = sh_rtc_set_time, | |
1b73e6ae JL |
538 | .read_alarm = sh_rtc_read_alarm, |
539 | .set_alarm = sh_rtc_set_alarm, | |
317a6104 PM |
540 | .proc = sh_rtc_proc, |
541 | }; | |
542 | ||
543 | static int __devinit sh_rtc_probe(struct platform_device *pdev) | |
544 | { | |
545 | struct sh_rtc *rtc; | |
546 | struct resource *res; | |
547 | int ret = -ENOENT; | |
548 | ||
549 | rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL); | |
550 | if (unlikely(!rtc)) | |
551 | return -ENOMEM; | |
552 | ||
553 | spin_lock_init(&rtc->lock); | |
554 | ||
555 | rtc->periodic_irq = platform_get_irq(pdev, 0); | |
556 | if (unlikely(rtc->periodic_irq < 0)) { | |
557 | dev_err(&pdev->dev, "No IRQ for period\n"); | |
558 | goto err_badres; | |
559 | } | |
560 | ||
561 | rtc->carry_irq = platform_get_irq(pdev, 1); | |
562 | if (unlikely(rtc->carry_irq < 0)) { | |
563 | dev_err(&pdev->dev, "No IRQ for carry\n"); | |
564 | goto err_badres; | |
565 | } | |
566 | ||
567 | rtc->alarm_irq = platform_get_irq(pdev, 2); | |
568 | if (unlikely(rtc->alarm_irq < 0)) { | |
569 | dev_err(&pdev->dev, "No IRQ for alarm\n"); | |
570 | goto err_badres; | |
571 | } | |
572 | ||
573 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | |
574 | if (unlikely(res == NULL)) { | |
575 | dev_err(&pdev->dev, "No IO resource\n"); | |
576 | goto err_badres; | |
577 | } | |
578 | ||
579 | rtc->regsize = res->end - res->start + 1; | |
580 | ||
581 | rtc->res = request_mem_region(res->start, rtc->regsize, pdev->name); | |
582 | if (unlikely(!rtc->res)) { | |
583 | ret = -EBUSY; | |
584 | goto err_badres; | |
585 | } | |
586 | ||
587 | rtc->regbase = (void __iomem *)rtc->res->start; | |
588 | if (unlikely(!rtc->regbase)) { | |
589 | ret = -EINVAL; | |
590 | goto err_badmap; | |
591 | } | |
592 | ||
593 | rtc->rtc_dev = rtc_device_register("sh", &pdev->dev, | |
594 | &sh_rtc_ops, THIS_MODULE); | |
29dd0dae | 595 | if (IS_ERR(rtc->rtc_dev)) { |
317a6104 PM |
596 | ret = PTR_ERR(rtc->rtc_dev); |
597 | goto err_badmap; | |
598 | } | |
599 | ||
ad89f87a PM |
600 | rtc->capabilities = RTC_DEF_CAPABILITIES; |
601 | if (pdev->dev.platform_data) { | |
602 | struct sh_rtc_platform_info *pinfo = pdev->dev.platform_data; | |
603 | ||
604 | /* | |
605 | * Some CPUs have special capabilities in addition to the | |
606 | * default set. Add those in here. | |
607 | */ | |
608 | rtc->capabilities |= pinfo->capabilities; | |
609 | } | |
610 | ||
317a6104 PM |
611 | platform_set_drvdata(pdev, rtc); |
612 | ||
613 | return 0; | |
614 | ||
615 | err_badmap: | |
616 | release_resource(rtc->res); | |
617 | err_badres: | |
618 | kfree(rtc); | |
619 | ||
620 | return ret; | |
621 | } | |
622 | ||
623 | static int __devexit sh_rtc_remove(struct platform_device *pdev) | |
624 | { | |
625 | struct sh_rtc *rtc = platform_get_drvdata(pdev); | |
626 | ||
627 | if (likely(rtc->rtc_dev)) | |
628 | rtc_device_unregister(rtc->rtc_dev); | |
629 | ||
630 | sh_rtc_setpie(&pdev->dev, 0); | |
631 | sh_rtc_setaie(&pdev->dev, 0); | |
632 | ||
633 | release_resource(rtc->res); | |
634 | ||
635 | platform_set_drvdata(pdev, NULL); | |
636 | ||
637 | kfree(rtc); | |
638 | ||
639 | return 0; | |
640 | } | |
641 | static struct platform_driver sh_rtc_platform_driver = { | |
642 | .driver = { | |
1b73e6ae | 643 | .name = DRV_NAME, |
317a6104 PM |
644 | .owner = THIS_MODULE, |
645 | }, | |
646 | .probe = sh_rtc_probe, | |
647 | .remove = __devexit_p(sh_rtc_remove), | |
648 | }; | |
649 | ||
650 | static int __init sh_rtc_init(void) | |
651 | { | |
652 | return platform_driver_register(&sh_rtc_platform_driver); | |
653 | } | |
654 | ||
655 | static void __exit sh_rtc_exit(void) | |
656 | { | |
657 | platform_driver_unregister(&sh_rtc_platform_driver); | |
658 | } | |
659 | ||
660 | module_init(sh_rtc_init); | |
661 | module_exit(sh_rtc_exit); | |
662 | ||
663 | MODULE_DESCRIPTION("SuperH on-chip RTC driver"); | |
1b73e6ae JL |
664 | MODULE_VERSION(DRV_VERSION); |
665 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, Jamie Lenehan <lenehan@twibble.org>"); | |
317a6104 | 666 | MODULE_LICENSE("GPL"); |
ad28a07b | 667 | MODULE_ALIAS("platform:" DRV_NAME); |