[MTD ONENAND] Check OneNAND lock scheme & all block unlock command support
[pandora-kernel.git] / arch / sh / kernel / cpu / rtc.c
1 /*
2  * linux/arch/sh/kernel/rtc.c -- SH3 / SH4 on-chip RTC support
3  *
4  *  Copyright (C) 2000  Philipp Rumpf <prumpf@tux.org>
5  *  Copyright (C) 1999  Tetsuya Okada & Niibe Yutaka
6  */
7
8 #include <linux/init.h>
9 #include <linux/kernel.h>
10 #include <linux/sched.h>
11 #include <linux/time.h>
12 #include <linux/bcd.h>
13 #include <asm/io.h>
14 #include <asm/rtc.h>
15
16 void sh_rtc_gettimeofday(struct timespec *ts)
17 {
18         unsigned int sec128, sec, sec2, min, hr, wk, day, mon, yr, yr100, cf_bit;
19         unsigned long flags;
20
21  again:
22         do {
23                 local_irq_save(flags);
24                 ctrl_outb(0, RCR1);  /* Clear CF-bit */
25                 sec128 = ctrl_inb(R64CNT);
26                 sec = ctrl_inb(RSECCNT);
27                 min = ctrl_inb(RMINCNT);
28                 hr  = ctrl_inb(RHRCNT);
29                 wk  = ctrl_inb(RWKCNT);
30                 day = ctrl_inb(RDAYCNT);
31                 mon = ctrl_inb(RMONCNT);
32 #if defined(CONFIG_CPU_SH4)
33                 yr  = ctrl_inw(RYRCNT);
34                 yr100 = (yr >> 8);
35                 yr &= 0xff;
36 #else
37                 yr  = ctrl_inb(RYRCNT);
38                 yr100 = (yr == 0x99) ? 0x19 : 0x20;
39 #endif
40                 sec2 = ctrl_inb(R64CNT);
41                 cf_bit = ctrl_inb(RCR1) & RCR1_CF;
42                 local_irq_restore(flags);
43         } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0);
44
45         BCD_TO_BIN(yr100);
46         BCD_TO_BIN(yr);
47         BCD_TO_BIN(mon);
48         BCD_TO_BIN(day);
49         BCD_TO_BIN(hr);
50         BCD_TO_BIN(min);
51         BCD_TO_BIN(sec);
52
53         if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
54             hr > 23 || min > 59 || sec > 59) {
55                 printk(KERN_ERR
56                        "SH RTC: invalid value, resetting to 1 Jan 2000\n");
57                 local_irq_save(flags);
58                 ctrl_outb(RCR2_RESET, RCR2);  /* Reset & Stop */
59                 ctrl_outb(0, RSECCNT);
60                 ctrl_outb(0, RMINCNT);
61                 ctrl_outb(0, RHRCNT);
62                 ctrl_outb(6, RWKCNT);
63                 ctrl_outb(1, RDAYCNT);
64                 ctrl_outb(1, RMONCNT);
65 #if defined(CONFIG_CPU_SH4)
66                 ctrl_outw(0x2000, RYRCNT);
67 #else
68                 ctrl_outb(0, RYRCNT);
69 #endif
70                 ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2);  /* Start */
71                 goto again;
72         }
73
74 #if RTC_BIT_INVERTED != 0
75         if ((sec128 & RTC_BIT_INVERTED))
76                 sec--;
77 #endif
78
79         ts->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
80         ts->tv_nsec = ((sec128 * 1000000) / 128) * 1000;
81 }
82
83 /*
84  * Changed to only care about tv_sec, and not the full timespec struct
85  * (i.e. tv_nsec).  It can easily be switched to timespec for future cpus
86  * that support setting usec or nsec RTC values.
87  */
88 int sh_rtc_settimeofday(const time_t secs)
89 {
90         int retval = 0;
91         int real_seconds, real_minutes, cmos_minutes;
92         unsigned long flags;
93
94         local_irq_save(flags);
95         ctrl_outb(RCR2_RESET, RCR2);  /* Reset pre-scaler & stop RTC */
96
97         cmos_minutes = ctrl_inb(RMINCNT);
98         BCD_TO_BIN(cmos_minutes);
99
100         /*
101          * since we're only adjusting minutes and seconds,
102          * don't interfere with hour overflow. This avoids
103          * messing with unknown time zones but requires your
104          * RTC not to be off by more than 15 minutes
105          */
106         real_seconds = secs % 60;
107         real_minutes = secs / 60;
108         if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
109                 real_minutes += 30;     /* correct for half hour time zone */
110         real_minutes %= 60;
111
112         if (abs(real_minutes - cmos_minutes) < 30) {
113                 BIN_TO_BCD(real_seconds);
114                 BIN_TO_BCD(real_minutes);
115                 ctrl_outb(real_seconds, RSECCNT);
116                 ctrl_outb(real_minutes, RMINCNT);
117         } else {
118                 printk(KERN_WARNING
119                        "set_rtc_time: can't update from %d to %d\n",
120                        cmos_minutes, real_minutes);
121                 retval = -1;
122         }
123
124         ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2);  /* Start RTC */
125         local_irq_restore(flags);
126
127         return retval;
128 }