e2df4a13d2bbde9d4f88627697dc619c5eb3a941
[linux-2.6.git] / arch / m68k / atari / time.c
1 /*
2  * linux/arch/m68k/atari/time.c
3  *
4  * Atari time and real time clock stuff
5  *
6  * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek
7  *
8  * This file is subject to the terms and conditions of the GNU General Public
9  * License.  See the file COPYING in the main directory of this archive
10  * for more details.
11  */
12
13 #include <linux/types.h>
14 #include <linux/mc146818rtc.h>
15 #include <linux/interrupt.h>
16 #include <linux/init.h>
17 #include <linux/rtc.h>
18 #include <linux/bcd.h>
19 #include <linux/delay.h>
20
21 #include <asm/atariints.h>
22
23 void __init
24 atari_sched_init(irq_handler_t timer_routine)
25 {
26     /* set Timer C data Register */
27     mfp.tim_dt_c = INT_TICKS;
28     /* start timer C, div = 1:100 */
29     mfp.tim_ct_cd = (mfp.tim_ct_cd & 15) | 0x60;
30     /* install interrupt service routine for MFP Timer C */
31     request_irq(IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW,
32                 "timer", timer_routine);
33 }
34
35 /* ++andreas: gettimeoffset fixed to check for pending interrupt */
36
37 #define TICK_SIZE 10000
38
39 /* This is always executed with interrupts disabled.  */
40 unsigned long atari_gettimeoffset (void)
41 {
42   unsigned long ticks, offset = 0;
43
44   /* read MFP timer C current value */
45   ticks = mfp.tim_dt_c;
46   /* The probability of underflow is less than 2% */
47   if (ticks > INT_TICKS - INT_TICKS / 50)
48     /* Check for pending timer interrupt */
49     if (mfp.int_pn_b & (1 << 5))
50       offset = TICK_SIZE;
51
52   ticks = INT_TICKS - ticks;
53   ticks = ticks * 10000L / INT_TICKS;
54
55   return ticks + offset;
56 }
57
58
59 static void mste_read(struct MSTE_RTC *val)
60 {
61 #define COPY(v) val->v=(mste_rtc.v & 0xf)
62         do {
63                 COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
64                 COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
65                 COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
66                 COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
67                 COPY(year_tens) ;
68         /* prevent from reading the clock while it changed */
69         } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
70 #undef COPY
71 }
72
73 static void mste_write(struct MSTE_RTC *val)
74 {
75 #define COPY(v) mste_rtc.v=val->v
76         do {
77                 COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
78                 COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
79                 COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
80                 COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
81                 COPY(year_tens) ;
82         /* prevent from writing the clock while it changed */
83         } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
84 #undef COPY
85 }
86
87 #define RTC_READ(reg)                           \
88     ({  unsigned char   __val;                  \
89                 (void) atari_writeb(reg,&tt_rtc.regsel);        \
90                 __val = tt_rtc.data;            \
91                 __val;                          \
92         })
93
94 #define RTC_WRITE(reg,val)                      \
95     do {                                        \
96                 atari_writeb(reg,&tt_rtc.regsel);       \
97                 tt_rtc.data = (val);            \
98         } while(0)
99
100
101 #define HWCLK_POLL_INTERVAL     5
102
103 int atari_mste_hwclk( int op, struct rtc_time *t )
104 {
105     int hour, year;
106     int hr24=0;
107     struct MSTE_RTC val;
108
109     mste_rtc.mode=(mste_rtc.mode | 1);
110     hr24=mste_rtc.mon_tens & 1;
111     mste_rtc.mode=(mste_rtc.mode & ~1);
112
113     if (op) {
114         /* write: prepare values */
115
116         val.sec_ones = t->tm_sec % 10;
117         val.sec_tens = t->tm_sec / 10;
118         val.min_ones = t->tm_min % 10;
119         val.min_tens = t->tm_min / 10;
120         hour = t->tm_hour;
121         if (!hr24) {
122             if (hour > 11)
123                 hour += 20 - 12;
124             if (hour == 0 || hour == 20)
125                 hour += 12;
126         }
127         val.hr_ones = hour % 10;
128         val.hr_tens = hour / 10;
129         val.day_ones = t->tm_mday % 10;
130         val.day_tens = t->tm_mday / 10;
131         val.mon_ones = (t->tm_mon+1) % 10;
132         val.mon_tens = (t->tm_mon+1) / 10;
133         year = t->tm_year - 80;
134         val.year_ones = year % 10;
135         val.year_tens = year / 10;
136         val.weekday = t->tm_wday;
137         mste_write(&val);
138         mste_rtc.mode=(mste_rtc.mode | 1);
139         val.year_ones = (year % 4);     /* leap year register */
140         mste_rtc.mode=(mste_rtc.mode & ~1);
141     }
142     else {
143         mste_read(&val);
144         t->tm_sec = val.sec_ones + val.sec_tens * 10;
145         t->tm_min = val.min_ones + val.min_tens * 10;
146         hour = val.hr_ones + val.hr_tens * 10;
147         if (!hr24) {
148             if (hour == 12 || hour == 12 + 20)
149                 hour -= 12;
150             if (hour >= 20)
151                 hour += 12 - 20;
152         }
153         t->tm_hour = hour;
154         t->tm_mday = val.day_ones + val.day_tens * 10;
155         t->tm_mon  = val.mon_ones + val.mon_tens * 10 - 1;
156         t->tm_year = val.year_ones + val.year_tens * 10 + 80;
157         t->tm_wday = val.weekday;
158     }
159     return 0;
160 }
161
162 int atari_tt_hwclk( int op, struct rtc_time *t )
163 {
164     int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0;
165     unsigned long       flags;
166     unsigned char       ctrl;
167     int pm = 0;
168
169     ctrl = RTC_READ(RTC_CONTROL); /* control registers are
170                                    * independent from the UIP */
171
172     if (op) {
173         /* write: prepare values */
174
175         sec  = t->tm_sec;
176         min  = t->tm_min;
177         hour = t->tm_hour;
178         day  = t->tm_mday;
179         mon  = t->tm_mon + 1;
180         year = t->tm_year - atari_rtc_year_offset;
181         wday = t->tm_wday + (t->tm_wday >= 0);
182
183         if (!(ctrl & RTC_24H)) {
184             if (hour > 11) {
185                 pm = 0x80;
186                 if (hour != 12)
187                     hour -= 12;
188             }
189             else if (hour == 0)
190                 hour = 12;
191         }
192
193         if (!(ctrl & RTC_DM_BINARY)) {
194             sec = bin2bcd(sec);
195             min = bin2bcd(min);
196             hour = bin2bcd(hour);
197             day = bin2bcd(day);
198             mon = bin2bcd(mon);
199             year = bin2bcd(year);
200             if (wday >= 0)
201                 wday = bin2bcd(wday);
202         }
203     }
204
205     /* Reading/writing the clock registers is a bit critical due to
206      * the regular update cycle of the RTC. While an update is in
207      * progress, registers 0..9 shouldn't be touched.
208      * The problem is solved like that: If an update is currently in
209      * progress (the UIP bit is set), the process sleeps for a while
210      * (50ms). This really should be enough, since the update cycle
211      * normally needs 2 ms.
212      * If the UIP bit reads as 0, we have at least 244 usecs until the
213      * update starts. This should be enough... But to be sure,
214      * additionally the RTC_SET bit is set to prevent an update cycle.
215      */
216
217     while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
218         if (in_atomic() || irqs_disabled())
219             mdelay(1);
220         else
221             schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
222     }
223
224     local_irq_save(flags);
225     RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
226     if (!op) {
227         sec  = RTC_READ( RTC_SECONDS );
228         min  = RTC_READ( RTC_MINUTES );
229         hour = RTC_READ( RTC_HOURS );
230         day  = RTC_READ( RTC_DAY_OF_MONTH );
231         mon  = RTC_READ( RTC_MONTH );
232         year = RTC_READ( RTC_YEAR );
233         wday = RTC_READ( RTC_DAY_OF_WEEK );
234     }
235     else {
236         RTC_WRITE( RTC_SECONDS, sec );
237         RTC_WRITE( RTC_MINUTES, min );
238         RTC_WRITE( RTC_HOURS, hour + pm);
239         RTC_WRITE( RTC_DAY_OF_MONTH, day );
240         RTC_WRITE( RTC_MONTH, mon );
241         RTC_WRITE( RTC_YEAR, year );
242         if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
243     }
244     RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
245     local_irq_restore(flags);
246
247     if (!op) {
248         /* read: adjust values */
249
250         if (hour & 0x80) {
251             hour &= ~0x80;
252             pm = 1;
253         }
254
255         if (!(ctrl & RTC_DM_BINARY)) {
256             sec = bcd2bin(sec);
257             min = bcd2bin(min);
258             hour = bcd2bin(hour);
259             day = bcd2bin(day);
260             mon = bcd2bin(mon);
261             year = bcd2bin(year);
262             wday = bcd2bin(wday);
263         }
264
265         if (!(ctrl & RTC_24H)) {
266             if (!pm && hour == 12)
267                 hour = 0;
268             else if (pm && hour != 12)
269                 hour += 12;
270         }
271
272         t->tm_sec  = sec;
273         t->tm_min  = min;
274         t->tm_hour = hour;
275         t->tm_mday = day;
276         t->tm_mon  = mon - 1;
277         t->tm_year = year + atari_rtc_year_offset;
278         t->tm_wday = wday - 1;
279     }
280
281     return( 0 );
282 }
283
284
285 int atari_mste_set_clock_mmss (unsigned long nowtime)
286 {
287     short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
288     struct MSTE_RTC val;
289     unsigned char rtc_minutes;
290
291     mste_read(&val);
292     rtc_minutes= val.min_ones + val.min_tens * 10;
293     if ((rtc_minutes < real_minutes
294          ? real_minutes - rtc_minutes
295          : rtc_minutes - real_minutes) < 30)
296     {
297         val.sec_ones = real_seconds % 10;
298         val.sec_tens = real_seconds / 10;
299         val.min_ones = real_minutes % 10;
300         val.min_tens = real_minutes / 10;
301         mste_write(&val);
302     }
303     else
304         return -1;
305     return 0;
306 }
307
308 int atari_tt_set_clock_mmss (unsigned long nowtime)
309 {
310     int retval = 0;
311     short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
312     unsigned char save_control, save_freq_select, rtc_minutes;
313
314     save_control = RTC_READ (RTC_CONTROL); /* tell the clock it's being set */
315     RTC_WRITE (RTC_CONTROL, save_control | RTC_SET);
316
317     save_freq_select = RTC_READ (RTC_FREQ_SELECT); /* stop and reset prescaler */
318     RTC_WRITE (RTC_FREQ_SELECT, save_freq_select | RTC_DIV_RESET2);
319
320     rtc_minutes = RTC_READ (RTC_MINUTES);
321     if (!(save_control & RTC_DM_BINARY))
322         rtc_minutes = bcd2bin(rtc_minutes);
323
324     /* Since we're only adjusting minutes and seconds, don't interfere
325        with hour overflow.  This avoids messing with unknown time zones
326        but requires your RTC not to be off by more than 30 minutes.  */
327     if ((rtc_minutes < real_minutes
328          ? real_minutes - rtc_minutes
329          : rtc_minutes - real_minutes) < 30)
330         {
331             if (!(save_control & RTC_DM_BINARY))
332                 {
333                     real_seconds = bin2bcd(real_seconds);
334                     real_minutes = bin2bcd(real_minutes);
335                 }
336             RTC_WRITE (RTC_SECONDS, real_seconds);
337             RTC_WRITE (RTC_MINUTES, real_minutes);
338         }
339     else
340         retval = -1;
341
342     RTC_WRITE (RTC_FREQ_SELECT, save_freq_select);
343     RTC_WRITE (RTC_CONTROL, save_control);
344     return retval;
345 }
346
347 /*
348  * Local variables:
349  *  c-indent-level: 4
350  *  tab-width: 8
351  * End:
352  */