Merge master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb
[sfrench/cifs-2.6.git] / arch / sh / boards / snapgear / rtc.c
1 /****************************************************************************/
2 /*
3  * linux/arch/sh/boards/snapgear/rtc.c -- Secureedge5410 RTC code
4  *
5  *  Copyright (C) 2002  David McCullough <davidm@snapgear.com>
6  *  Copyright (C) 2003  Paul Mundt <lethal@linux-sh.org>
7  *
8  * The SecureEdge5410 can have one of 2 real time clocks, the SH
9  * built in version or the preferred external DS1302.  Here we work out
10  * each to see what we have and then run with it.
11  */
12 /****************************************************************************/
13
14 #include <linux/init.h>
15 #include <linux/kernel.h>
16 #include <linux/sched.h>
17 #include <linux/time.h>
18 #include <linux/rtc.h>
19 #include <linux/mc146818rtc.h>
20 #include <asm/io.h>
21
22 static int use_ds1302;
23
24 /****************************************************************************/
25 /*
26  *      we need to implement a DS1302 driver here that can operate in
27  *      conjunction with the builtin rtc driver which is already quite friendly
28  */
29 /*****************************************************************************/
30
31 #define RTC_CMD_READ    0x81            /* Read command */
32 #define RTC_CMD_WRITE   0x80            /* Write command */
33
34 #define RTC_ADDR_YEAR   0x06            /* Address of year register */
35 #define RTC_ADDR_DAY    0x05            /* Address of day of week register */
36 #define RTC_ADDR_MON    0x04            /* Address of month register */
37 #define RTC_ADDR_DATE   0x03            /* Address of day of month register */
38 #define RTC_ADDR_HOUR   0x02            /* Address of hour register */
39 #define RTC_ADDR_MIN    0x01            /* Address of minute register */
40 #define RTC_ADDR_SEC    0x00            /* Address of second register */
41
42 #define RTC_RESET       0x1000
43 #define RTC_IODATA      0x0800
44 #define RTC_SCLK        0x0400
45
46 #define set_dirp(x)
47 #define get_dirp(x) 0
48 #define set_dp(x)       SECUREEDGE_WRITE_IOPORT(x, 0x1c00)
49 #define get_dp(x)       SECUREEDGE_READ_IOPORT()
50
51 static void ds1302_sendbits(unsigned int val)
52 {
53         int     i;
54
55         for (i = 8; (i); i--, val >>= 1) {
56                 set_dp((get_dp() & ~RTC_IODATA) | ((val & 0x1) ? RTC_IODATA : 0));
57                 set_dp(get_dp() | RTC_SCLK);    // clock high
58                 set_dp(get_dp() & ~RTC_SCLK);   // clock low
59         }
60 }
61
62 static unsigned int ds1302_recvbits(void)
63 {
64         unsigned int    val;
65         int             i;
66
67         for (i = 0, val = 0; (i < 8); i++) {
68                 val |= (((get_dp() & RTC_IODATA) ? 1 : 0) << i);
69                 set_dp(get_dp() | RTC_SCLK);    // clock high
70                 set_dp(get_dp() & ~RTC_SCLK);   // clock low
71         }
72         return(val);
73 }
74
75 static unsigned int ds1302_readbyte(unsigned int addr)
76 {
77         unsigned int    val;
78         unsigned long   flags;
79
80         local_irq_save(flags);
81         set_dirp(get_dirp() | RTC_RESET | RTC_IODATA | RTC_SCLK);
82         set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
83
84         set_dp(get_dp() | RTC_RESET);
85         ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_READ);
86         set_dirp(get_dirp() & ~RTC_IODATA);
87         val = ds1302_recvbits();
88         set_dp(get_dp() & ~RTC_RESET);
89         local_irq_restore(flags);
90
91         return(val);
92 }
93
94 static void ds1302_writebyte(unsigned int addr, unsigned int val)
95 {
96         unsigned long   flags;
97
98         local_irq_save(flags);
99         set_dirp(get_dirp() | RTC_RESET | RTC_IODATA | RTC_SCLK);
100         set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
101         set_dp(get_dp() | RTC_RESET);
102         ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_WRITE);
103         ds1302_sendbits(val);
104         set_dp(get_dp() & ~RTC_RESET);
105         local_irq_restore(flags);
106 }
107
108 static void ds1302_reset(void)
109 {
110         unsigned long   flags;
111         /* Hardware dependant reset/init */
112         local_irq_save(flags);
113         set_dirp(get_dirp() | RTC_RESET | RTC_IODATA | RTC_SCLK);
114         set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
115         local_irq_restore(flags);
116 }
117
118 /*****************************************************************************/
119
120 static inline int bcd2int(int val)
121 {
122         return((((val & 0xf0) >> 4) * 10) + (val & 0xf));
123 }
124
125 static inline int int2bcd(int val)
126 {
127         return(((val / 10) << 4) + (val % 10));
128 }
129
130 /*****************************************************************************/
131 /*
132  *      Write and Read some RAM in the DS1302,  if it works assume it's there
133  *      Otherwise use the SH4 internal RTC
134  */
135
136 void snapgear_rtc_gettimeofday(struct timespec *);
137 int snapgear_rtc_settimeofday(const time_t);
138
139 void __init secureedge5410_rtc_init(void)
140 {
141         unsigned char *test = "snapgear";
142         int i;
143
144         ds1302_reset();
145
146         use_ds1302 = 1;
147
148         for (i = 0; test[i]; i++)
149                 ds1302_writebyte(32 + i, test[i]);
150
151         for (i = 0; test[i]; i++)
152                 if (ds1302_readbyte(32 + i) != test[i]) {
153                         use_ds1302 = 0;
154                         break;
155                 }
156
157         if (use_ds1302) {
158                 rtc_sh_get_time = snapgear_rtc_gettimeofday;
159                 rtc_sh_set_time = snapgear_rtc_settimeofday;
160         }
161                 
162         printk("SnapGear RTC: using %s rtc.\n", use_ds1302 ? "ds1302" : "internal");
163 }
164
165 /****************************************************************************/
166 /*
167  *      our generic interface that chooses the correct code to use
168  */
169
170 void snapgear_rtc_gettimeofday(struct timespec *ts)
171 {
172         unsigned int sec, min, hr, day, mon, yr;
173
174         if (!use_ds1302)
175                 return;
176
177         sec = bcd2int(ds1302_readbyte(RTC_ADDR_SEC));
178         min = bcd2int(ds1302_readbyte(RTC_ADDR_MIN));
179         hr  = bcd2int(ds1302_readbyte(RTC_ADDR_HOUR));
180         day = bcd2int(ds1302_readbyte(RTC_ADDR_DATE));
181         mon = bcd2int(ds1302_readbyte(RTC_ADDR_MON));
182         yr  = bcd2int(ds1302_readbyte(RTC_ADDR_YEAR));
183
184 bad_time:
185         if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
186             hr > 23 || min > 59 || sec > 59) {
187                 printk(KERN_ERR
188                        "SnapGear RTC: invalid value, resetting to 1 Jan 2000\n");
189                 ds1302_writebyte(RTC_ADDR_MIN,  min = 0);
190                 ds1302_writebyte(RTC_ADDR_HOUR, hr  = 0);
191                 ds1302_writebyte(RTC_ADDR_DAY,        7);
192                 ds1302_writebyte(RTC_ADDR_DATE, day = 1);
193                 ds1302_writebyte(RTC_ADDR_MON,  mon = 1);
194                 ds1302_writebyte(RTC_ADDR_YEAR, yr  = 0);
195                 ds1302_writebyte(RTC_ADDR_SEC,  sec = 0);
196         }
197
198         ts->tv_sec = mktime(2000 + yr, mon, day, hr, min, sec);
199         if (ts->tv_sec < 0) {
200 #if 0
201                 printk("BAD TIME %d %d %d %d %d %d\n", yr, mon, day, hr, min, sec);
202 #endif
203                 yr = 100;
204                 goto bad_time;
205         }
206         ts->tv_nsec = 0;
207 }
208
209 int snapgear_rtc_settimeofday(const time_t secs)
210 {
211         int retval = 0;
212         int real_seconds, real_minutes, cmos_minutes;
213         unsigned long nowtime;
214
215         if (!use_ds1302)
216                 return 0;
217
218 /*
219  *      This is called direct from the kernel timer handling code.
220  *      It is supposed to synchronize the kernel clock to the RTC.
221  */
222
223         nowtime = secs;
224
225         /* STOP RTC */
226         ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) | 0x80);
227
228         cmos_minutes = bcd2int(ds1302_readbyte(RTC_ADDR_MIN));
229
230         /*
231          * since we're only adjusting minutes and seconds,
232          * don't interfere with hour overflow. This avoids
233          * messing with unknown time zones but requires your
234          * RTC not to be off by more than 15 minutes
235          */
236         real_seconds = nowtime % 60;
237         real_minutes = nowtime / 60;
238         if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
239                 real_minutes += 30;     /* correct for half hour time zone */
240         real_minutes %= 60;
241
242         if (abs(real_minutes - cmos_minutes) < 30) {
243                 ds1302_writebyte(RTC_ADDR_MIN, int2bcd(real_minutes));
244                 ds1302_writebyte(RTC_ADDR_SEC, int2bcd(real_seconds));
245         } else {
246                 printk(KERN_WARNING
247                        "SnapGear RTC: can't update from %d to %d\n",
248                        cmos_minutes, real_minutes);
249                 retval = -1;
250         }
251
252         /* START RTC */
253         ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) & ~0x80);
254         return(0);
255 }
256
257 unsigned char secureedge5410_cmos_read(int addr)
258 {
259         unsigned char val = 0;
260
261         if (!use_ds1302)
262                 return(__CMOS_READ(addr, w));
263
264         switch(addr) {
265         case RTC_SECONDS:       val = ds1302_readbyte(RTC_ADDR_SEC);  break;
266         case RTC_SECONDS_ALARM:                                       break;
267         case RTC_MINUTES:       val = ds1302_readbyte(RTC_ADDR_MIN);  break;
268         case RTC_MINUTES_ALARM:                                       break;
269         case RTC_HOURS:         val = ds1302_readbyte(RTC_ADDR_HOUR); break;
270         case RTC_HOURS_ALARM:                                         break;
271         case RTC_DAY_OF_WEEK:   val = ds1302_readbyte(RTC_ADDR_DAY);  break;
272         case RTC_DAY_OF_MONTH:  val = ds1302_readbyte(RTC_ADDR_DATE); break;
273         case RTC_MONTH:         val = ds1302_readbyte(RTC_ADDR_MON);  break;
274         case RTC_YEAR:          val = ds1302_readbyte(RTC_ADDR_YEAR); break;
275         case RTC_REG_A:         /* RTC_FREQ_SELECT */                 break;
276         case RTC_REG_B:         /* RTC_CONTROL */                     break;
277         case RTC_REG_C:         /* RTC_INTR_FLAGS */                  break;
278         case RTC_REG_D:         val = RTC_VRT /* RTC_VALID */;        break;
279         default:                                                      break;
280         }
281
282         return(val);
283 }
284
285 void secureedge5410_cmos_write(unsigned char val, int addr)
286 {
287         if (!use_ds1302) {
288                 __CMOS_WRITE(val, addr, w);
289                 return;
290         }
291
292         switch(addr) {
293         case RTC_SECONDS:       ds1302_writebyte(RTC_ADDR_SEC, val);  break;
294         case RTC_SECONDS_ALARM:                                       break;
295         case RTC_MINUTES:       ds1302_writebyte(RTC_ADDR_MIN, val);  break;
296         case RTC_MINUTES_ALARM:                                       break;
297         case RTC_HOURS:         ds1302_writebyte(RTC_ADDR_HOUR, val); break;
298         case RTC_HOURS_ALARM:                                         break;
299         case RTC_DAY_OF_WEEK:   ds1302_writebyte(RTC_ADDR_DAY, val);  break;
300         case RTC_DAY_OF_MONTH:  ds1302_writebyte(RTC_ADDR_DATE, val); break;
301         case RTC_MONTH:         ds1302_writebyte(RTC_ADDR_MON, val);  break;
302         case RTC_YEAR:          ds1302_writebyte(RTC_ADDR_YEAR, val); break;
303         case RTC_REG_A:         /* RTC_FREQ_SELECT */                 break;
304         case RTC_REG_B:         /* RTC_CONTROL */                     break;
305         case RTC_REG_C:         /* RTC_INTR_FLAGS */                  break;
306         case RTC_REG_D:         /* RTC_VALID */                       break;
307         default:                                                      break;
308         }
309 }