Linux-2.6.12-rc2
[linux-flexiantxendom0-natty.git] / drivers / char / s3c2410-rtc.c
1 /* drivers/char/s3c2410_rtc.c
2  *
3  * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk>
4  *                    http://www.simtec.co.uk/products/SWLINUX/
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * S3C2410 Internal RTC Driver
11  *
12  *  Changelog:
13  *      08-Nov-2004     BJD     Initial creation
14  *      12-Nov-2004     BJD     Added periodic IRQ and PM code
15  *      22-Nov-2004     BJD     Sign-test on alarm code to check for <0
16  *      10-Mar-2005     LCVR    Changed S3C2410_VA_RTC to S3C24XX_VA_RTC
17 */
18
19 #include <linux/module.h>
20 #include <linux/fs.h>
21 #include <linux/string.h>
22 #include <linux/init.h>
23 #include <linux/device.h>
24 #include <linux/interrupt.h>
25 #include <linux/rtc.h>
26 #include <linux/bcd.h>
27
28 #include <asm/hardware.h>
29 #include <asm/uaccess.h>
30 #include <asm/io.h>
31 #include <asm/irq.h>
32 #include <asm/rtc.h>
33
34 #include <asm/mach/time.h>
35
36 #include <asm/hardware/clock.h>
37 #include <asm/arch/regs-rtc.h>
38
39 /* need this for the RTC_AF definitions */
40 #include <linux/mc146818rtc.h>
41
42 #undef S3C24XX_VA_RTC
43 #define S3C24XX_VA_RTC s3c2410_rtc_base
44
45 static struct resource *s3c2410_rtc_mem;
46
47 static void __iomem *s3c2410_rtc_base;
48 static int s3c2410_rtc_alarmno = NO_IRQ;
49 static int s3c2410_rtc_tickno  = NO_IRQ;
50 static int s3c2410_rtc_freq    = 1;
51
52 static DEFINE_SPINLOCK(s3c2410_rtc_pie_lock);
53
54 /* IRQ Handlers */
55
56 static irqreturn_t s3c2410_rtc_alarmirq(int irq, void *id, struct pt_regs *r)
57 {
58         rtc_update(1, RTC_AF | RTC_IRQF);
59         return IRQ_HANDLED;
60 }
61
62 static irqreturn_t s3c2410_rtc_tickirq(int irq, void *id, struct pt_regs *r)
63 {
64         rtc_update(1, RTC_PF | RTC_IRQF);
65         return IRQ_HANDLED;
66 }
67
68 /* Update control registers */
69 static void s3c2410_rtc_setaie(int to)
70 {
71         unsigned int tmp;
72
73         pr_debug("%s: aie=%d\n", __FUNCTION__, to);
74
75         tmp = readb(S3C2410_RTCALM);
76
77         if (to)
78                 tmp |= S3C2410_RTCALM_ALMEN;
79         else
80                 tmp &= ~S3C2410_RTCALM_ALMEN;
81
82
83         writeb(tmp, S3C2410_RTCALM);
84 }
85
86 static void s3c2410_rtc_setpie(int to)
87 {
88         unsigned int tmp;
89
90         pr_debug("%s: pie=%d\n", __FUNCTION__, to);
91
92         spin_lock_irq(&s3c2410_rtc_pie_lock);
93         tmp = readb(S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
94
95         if (to)
96                 tmp |= S3C2410_TICNT_ENABLE;
97
98         writeb(tmp, S3C2410_TICNT);
99         spin_unlock_irq(&s3c2410_rtc_pie_lock);
100 }
101
102 static void s3c2410_rtc_setfreq(int freq)
103 {
104         unsigned int tmp;
105
106         spin_lock_irq(&s3c2410_rtc_pie_lock);
107         tmp = readb(S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
108
109         s3c2410_rtc_freq = freq;
110
111         tmp |= (128 / freq)-1;
112
113         writeb(tmp, S3C2410_TICNT);
114         spin_unlock_irq(&s3c2410_rtc_pie_lock);
115 }
116
117 /* Time read/write */
118
119 static void s3c2410_rtc_gettime(struct rtc_time *rtc_tm)
120 {
121         unsigned int have_retried = 0;
122
123  retry_get_time:
124         rtc_tm->tm_min  = readb(S3C2410_RTCMIN);
125         rtc_tm->tm_hour = readb(S3C2410_RTCHOUR);
126         rtc_tm->tm_mday = readb(S3C2410_RTCDATE);
127         rtc_tm->tm_mon  = readb(S3C2410_RTCMON);
128         rtc_tm->tm_year = readb(S3C2410_RTCYEAR);
129         rtc_tm->tm_sec  = readb(S3C2410_RTCSEC);
130
131         /* the only way to work out wether the system was mid-update
132          * when we read it is to check the second counter, and if it
133          * is zero, then we re-try the entire read
134          */
135
136         if (rtc_tm->tm_sec == 0 && !have_retried) {
137                 have_retried = 1;
138                 goto retry_get_time;
139         }
140
141         pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
142                  rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
143                  rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
144
145         BCD_TO_BIN(rtc_tm->tm_sec);
146         BCD_TO_BIN(rtc_tm->tm_min);
147         BCD_TO_BIN(rtc_tm->tm_hour);
148         BCD_TO_BIN(rtc_tm->tm_mday);
149         BCD_TO_BIN(rtc_tm->tm_mon);
150         BCD_TO_BIN(rtc_tm->tm_year);
151
152         rtc_tm->tm_year += 100;
153         rtc_tm->tm_mon -= 1;
154 }
155
156
157 static int s3c2410_rtc_settime(struct rtc_time *tm)
158 {
159         /* the rtc gets round the y2k problem by just not supporting it */
160
161         if (tm->tm_year < 100)
162                 return -EINVAL;
163
164         writeb(BIN2BCD(tm->tm_sec),  S3C2410_RTCSEC);
165         writeb(BIN2BCD(tm->tm_min),  S3C2410_RTCMIN);
166         writeb(BIN2BCD(tm->tm_hour), S3C2410_RTCHOUR);
167         writeb(BIN2BCD(tm->tm_mday), S3C2410_RTCDATE);
168         writeb(BIN2BCD(tm->tm_mon + 1), S3C2410_RTCMON);
169         writeb(BIN2BCD(tm->tm_year - 100), S3C2410_RTCYEAR);
170
171         return 0;
172 }
173
174 static void s3c2410_rtc_getalarm(struct rtc_wkalrm *alrm)
175 {
176         struct rtc_time *alm_tm = &alrm->time;
177         unsigned int alm_en;
178
179         alm_tm->tm_sec  = readb(S3C2410_ALMSEC);
180         alm_tm->tm_min  = readb(S3C2410_ALMMIN);
181         alm_tm->tm_hour = readb(S3C2410_ALMHOUR);
182         alm_tm->tm_mon  = readb(S3C2410_ALMMON);
183         alm_tm->tm_mday = readb(S3C2410_ALMDATE);
184         alm_tm->tm_year = readb(S3C2410_ALMYEAR);
185
186         alm_en = readb(S3C2410_RTCALM);
187
188         pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
189                  alm_en,
190                  alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
191                  alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
192
193
194         /* decode the alarm enable field */
195
196         if (alm_en & S3C2410_RTCALM_SECEN) {
197                 BCD_TO_BIN(alm_tm->tm_sec);
198         } else {
199                 alm_tm->tm_sec = 0xff;
200         }
201
202         if (alm_en & S3C2410_RTCALM_MINEN) {
203                 BCD_TO_BIN(alm_tm->tm_min);
204         } else {
205                 alm_tm->tm_min = 0xff;
206         }
207
208         if (alm_en & S3C2410_RTCALM_HOUREN) {
209                 BCD_TO_BIN(alm_tm->tm_hour);
210         } else {
211                 alm_tm->tm_hour = 0xff;
212         }
213
214         if (alm_en & S3C2410_RTCALM_DAYEN) {
215                 BCD_TO_BIN(alm_tm->tm_mday);
216         } else {
217                 alm_tm->tm_mday = 0xff;
218         }
219
220         if (alm_en & S3C2410_RTCALM_MONEN) {
221                 BCD_TO_BIN(alm_tm->tm_mon);
222                 alm_tm->tm_mon -= 1;
223         } else {
224                 alm_tm->tm_mon = 0xff;
225         }
226
227         if (alm_en & S3C2410_RTCALM_YEAREN) {
228                 BCD_TO_BIN(alm_tm->tm_year);
229         } else {
230                 alm_tm->tm_year = 0xffff;
231         }
232
233         /* todo - set alrm->enabled ? */
234 }
235
236 static int s3c2410_rtc_setalarm(struct rtc_wkalrm *alrm)
237 {
238         struct rtc_time *tm = &alrm->time;
239         unsigned int alrm_en;
240
241         pr_debug("s3c2410_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
242                  alrm->enabled,
243                  tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
244                  tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
245
246         if (alrm->enabled || 1) {
247                 alrm_en = readb(S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
248                 writeb(0x00, S3C2410_RTCALM);
249
250                 if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
251                         alrm_en |= S3C2410_RTCALM_SECEN;
252                         writeb(BIN2BCD(tm->tm_sec), S3C2410_ALMSEC);
253                 }
254
255                 if (tm->tm_min < 60 && tm->tm_min >= 0) {
256                         alrm_en |= S3C2410_RTCALM_MINEN;
257                         writeb(BIN2BCD(tm->tm_min), S3C2410_ALMMIN);
258                 }
259
260                 if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
261                         alrm_en |= S3C2410_RTCALM_HOUREN;
262                         writeb(BIN2BCD(tm->tm_hour), S3C2410_ALMHOUR);
263                 }
264
265                 pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en);
266
267                 writeb(alrm_en, S3C2410_RTCALM);
268                 enable_irq_wake(s3c2410_rtc_alarmno);
269         } else {
270                 alrm_en = readb(S3C2410_RTCALM);
271                 alrm_en &= ~S3C2410_RTCALM_ALMEN;
272                 writeb(alrm_en, S3C2410_RTCALM);
273                 disable_irq_wake(s3c2410_rtc_alarmno);
274         }
275
276         return 0;
277 }
278
279 static int s3c2410_rtc_ioctl(unsigned int cmd, unsigned long arg)
280 {
281         switch (cmd) {
282         case RTC_AIE_OFF:
283         case RTC_AIE_ON:
284                 s3c2410_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0);
285                 return 0;
286
287         case RTC_PIE_OFF:
288         case RTC_PIE_ON:
289                 s3c2410_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0);
290                 return 0;
291
292         case RTC_IRQP_READ:
293                 return put_user(s3c2410_rtc_freq, (unsigned long __user *)arg);
294
295         case RTC_IRQP_SET:
296                 if (arg < 1 || arg > 64)
297                         return -EINVAL;
298
299                 if (!capable(CAP_SYS_RESOURCE))
300                         return -EACCES;
301
302                 /* check for power of 2 */
303
304                 if ((arg & (arg-1)) != 0)
305                         return -EINVAL;
306
307                 pr_debug("s3c2410_rtc: setting frequency %ld\n", arg);
308
309                 s3c2410_rtc_setfreq(arg);
310                 return 0;
311
312         case RTC_UIE_ON:
313         case RTC_UIE_OFF:
314                 return -EINVAL;
315         }
316
317         return -EINVAL;
318 }
319
320 static int s3c2410_rtc_proc(char *buf)
321 {
322         unsigned int rtcalm = readb(S3C2410_RTCALM);
323         unsigned int ticnt = readb (S3C2410_TICNT);
324         char *p = buf;
325
326         p += sprintf(p, "alarm_IRQ\t: %s\n",
327                      (rtcalm & S3C2410_RTCALM_ALMEN) ? "yes" : "no" );
328         p += sprintf(p, "periodic_IRQ\t: %s\n",
329                      (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
330         p += sprintf(p, "periodic_freq\t: %d\n", s3c2410_rtc_freq);
331
332         return p - buf;
333 }
334
335 static int s3c2410_rtc_open(void)
336 {
337         int ret;
338
339         ret = request_irq(s3c2410_rtc_alarmno, s3c2410_rtc_alarmirq,
340                           SA_INTERRUPT,  "s3c2410-rtc alarm", NULL);
341
342         if (ret)
343                 printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_alarmno);
344
345         ret = request_irq(s3c2410_rtc_tickno, s3c2410_rtc_tickirq,
346                           SA_INTERRUPT,  "s3c2410-rtc tick", NULL);
347
348         if (ret) {
349                 printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_tickno);
350                 goto tick_err;
351         }
352
353         return ret;
354
355  tick_err:
356         free_irq(s3c2410_rtc_alarmno, NULL);
357         return ret;
358 }
359
360 static void s3c2410_rtc_release(void)
361 {
362         /* do not clear AIE here, it may be needed for wake */
363
364         s3c2410_rtc_setpie(0);
365         free_irq(s3c2410_rtc_alarmno, NULL);
366         free_irq(s3c2410_rtc_tickno, NULL);
367 }
368
369 static struct rtc_ops s3c2410_rtcops = {
370         .owner          = THIS_MODULE,
371         .open           = s3c2410_rtc_open,
372         .release        = s3c2410_rtc_release,
373         .ioctl          = s3c2410_rtc_ioctl,
374         .read_time      = s3c2410_rtc_gettime,
375         .set_time       = s3c2410_rtc_settime,
376         .read_alarm     = s3c2410_rtc_getalarm,
377         .set_alarm      = s3c2410_rtc_setalarm,
378         .proc           = s3c2410_rtc_proc,
379 };
380
381 static void s3c2410_rtc_enable(struct device *dev, int en)
382 {
383         unsigned int tmp;
384
385         if (s3c2410_rtc_base == NULL)
386                 return;
387
388         if (!en) {
389                 tmp = readb(S3C2410_RTCCON);
390                 writeb(tmp & ~S3C2410_RTCCON_RTCEN, S3C2410_RTCCON);
391
392                 tmp = readb(S3C2410_TICNT);
393                 writeb(tmp & ~S3C2410_TICNT_ENABLE, S3C2410_TICNT);
394         } else {
395                 /* re-enable the device, and check it is ok */
396
397                 if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
398                         dev_info(dev, "rtc disabled, re-enabling\n");
399
400                         tmp = readb(S3C2410_RTCCON);
401                         writeb(tmp | S3C2410_RTCCON_RTCEN , S3C2410_RTCCON);
402                 }
403
404                 if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
405                         dev_info(dev, "removing S3C2410_RTCCON_CNTSEL\n");
406
407                         tmp = readb(S3C2410_RTCCON);
408                         writeb(tmp& ~S3C2410_RTCCON_CNTSEL , S3C2410_RTCCON);
409                 }
410
411                 if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
412                         dev_info(dev, "removing S3C2410_RTCCON_CLKRST\n");
413
414                         tmp = readb(S3C2410_RTCCON);
415                         writeb(tmp & ~S3C2410_RTCCON_CLKRST, S3C2410_RTCCON);
416                 }
417         }
418 }
419
420 static int s3c2410_rtc_remove(struct device *dev)
421 {
422         unregister_rtc(&s3c2410_rtcops);
423
424         s3c2410_rtc_setpie(0);
425         s3c2410_rtc_setaie(0);
426
427         if (s3c2410_rtc_mem != NULL) {
428                 pr_debug("s3c2410_rtc: releasing s3c2410_rtc_mem\n");
429                 iounmap(s3c2410_rtc_base);
430                 release_resource(s3c2410_rtc_mem);
431                 kfree(s3c2410_rtc_mem);
432         }
433
434         return 0;
435 }
436
437 static int s3c2410_rtc_probe(struct device *dev)
438 {
439         struct platform_device *pdev = to_platform_device(dev);
440         struct resource *res;
441         int ret;
442
443         pr_debug("%s: probe=%p, device=%p\n", __FUNCTION__, pdev, dev);
444
445         /* find the IRQs */
446
447         s3c2410_rtc_tickno = platform_get_irq(pdev, 1);
448         if (s3c2410_rtc_tickno <= 0) {
449                 dev_err(dev, "no irq for rtc tick\n");
450                 return -ENOENT;
451         }
452
453         s3c2410_rtc_alarmno = platform_get_irq(pdev, 0);
454         if (s3c2410_rtc_alarmno <= 0) {
455                 dev_err(dev, "no irq for alarm\n");
456                 return -ENOENT;
457         }
458
459         pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
460                  s3c2410_rtc_tickno, s3c2410_rtc_alarmno);
461
462         /* get the memory region */
463
464         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
465         if (res == NULL) {
466                 dev_err(dev, "failed to get memory region resource\n");
467                 return -ENOENT;
468         }
469
470         s3c2410_rtc_mem = request_mem_region(res->start, res->end-res->start+1,
471                                      pdev->name);
472
473         if (s3c2410_rtc_mem == NULL) {
474                 dev_err(dev, "failed to reserve memory region\n");
475                 ret = -ENOENT;
476                 goto exit_err;
477         }
478
479         s3c2410_rtc_base = ioremap(res->start, res->end - res->start + 1);
480         if (s3c2410_rtc_base == NULL) {
481                 dev_err(dev, "failed ioremap()\n");
482                 ret = -EINVAL;
483                 goto exit_err;
484         }
485
486         s3c2410_rtc_mem = res;
487         pr_debug("s3c2410_rtc_base=%p\n", s3c2410_rtc_base);
488
489         pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
490
491         /* check to see if everything is setup correctly */
492
493         s3c2410_rtc_enable(dev, 1);
494
495         pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
496
497         s3c2410_rtc_setfreq(s3c2410_rtc_freq);
498
499         /* register RTC and exit */
500
501         register_rtc(&s3c2410_rtcops);
502         return 0;
503
504  exit_err:
505         dev_err(dev, "error %d during initialisation\n", ret);
506
507         return ret;
508 }
509
510 #ifdef CONFIG_PM
511
512 /* S3C2410 RTC Power management control */
513
514 static struct timespec s3c2410_rtc_delta;
515
516 static int ticnt_save;
517
518 static int s3c2410_rtc_suspend(struct device *dev, u32 state, u32 level)
519 {
520         struct rtc_time tm;
521         struct timespec time;
522
523         time.tv_nsec = 0;
524
525         if (level == SUSPEND_POWER_DOWN) {
526                 /* save TICNT for anyone using periodic interrupts */
527
528                 ticnt_save = readb(S3C2410_TICNT);
529
530                 /* calculate time delta for suspend */
531
532                 s3c2410_rtc_gettime(&tm);
533                 rtc_tm_to_time(&tm, &time.tv_sec);
534                 save_time_delta(&s3c2410_rtc_delta, &time);
535                 s3c2410_rtc_enable(dev, 0);
536         }
537
538         return 0;
539 }
540
541 static int s3c2410_rtc_resume(struct device *dev, u32 level)
542 {
543         struct rtc_time tm;
544         struct timespec time;
545
546         time.tv_nsec = 0;
547
548         s3c2410_rtc_enable(dev, 1);
549         s3c2410_rtc_gettime(&tm);
550         rtc_tm_to_time(&tm, &time.tv_sec);
551         restore_time_delta(&s3c2410_rtc_delta, &time);
552
553         writeb(ticnt_save, S3C2410_TICNT);
554         return 0;
555 }
556 #else
557 #define s3c2410_rtc_suspend NULL
558 #define s3c2410_rtc_resume  NULL
559 #endif
560
561 static struct device_driver s3c2410_rtcdrv = {
562         .name           = "s3c2410-rtc",
563         .bus            = &platform_bus_type,
564         .probe          = s3c2410_rtc_probe,
565         .remove         = s3c2410_rtc_remove,
566         .suspend        = s3c2410_rtc_suspend,
567         .resume         = s3c2410_rtc_resume,
568 };
569
570 static char __initdata banner[] = "S3C2410 RTC, (c) 2004 Simtec Electronics\n";
571
572 static int __init s3c2410_rtc_init(void)
573 {
574         printk(banner);
575         return driver_register(&s3c2410_rtcdrv);
576 }
577
578 static void __exit s3c2410_rtc_exit(void)
579 {
580         driver_unregister(&s3c2410_rtcdrv);
581 }
582
583 module_init(s3c2410_rtc_init);
584 module_exit(s3c2410_rtc_exit);
585
586 MODULE_DESCRIPTION("S3C24XX RTC Driver");
587 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
588 MODULE_LICENSE("GPL");