[PATCH] m68k/Atari: Interrupt updates
[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             BIN_TO_BCD(sec);
195             BIN_TO_BCD(min);
196             BIN_TO_BCD(hour);
197             BIN_TO_BCD(day);
198             BIN_TO_BCD(mon);
199             BIN_TO_BCD(year);
200             if (wday >= 0) BIN_TO_BCD(wday);
201         }
202     }
203
204     /* Reading/writing the clock registers is a bit critical due to
205      * the regular update cycle of the RTC. While an update is in
206      * progress, registers 0..9 shouldn't be touched.
207      * The problem is solved like that: If an update is currently in
208      * progress (the UIP bit is set), the process sleeps for a while
209      * (50ms). This really should be enough, since the update cycle
210      * normally needs 2 ms.
211      * If the UIP bit reads as 0, we have at least 244 usecs until the
212      * update starts. This should be enough... But to be sure,
213      * additionally the RTC_SET bit is set to prevent an update cycle.
214      */
215
216     while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
217         if (in_atomic() || irqs_disabled())
218             mdelay(1);
219         else
220             schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
221     }
222
223     local_irq_save(flags);
224     RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
225     if (!op) {
226         sec  = RTC_READ( RTC_SECONDS );
227         min  = RTC_READ( RTC_MINUTES );
228         hour = RTC_READ( RTC_HOURS );
229         day  = RTC_READ( RTC_DAY_OF_MONTH );
230         mon  = RTC_READ( RTC_MONTH );
231         year = RTC_READ( RTC_YEAR );
232         wday = RTC_READ( RTC_DAY_OF_WEEK );
233     }
234     else {
235         RTC_WRITE( RTC_SECONDS, sec );
236         RTC_WRITE( RTC_MINUTES, min );
237         RTC_WRITE( RTC_HOURS, hour + pm);
238         RTC_WRITE( RTC_DAY_OF_MONTH, day );
239         RTC_WRITE( RTC_MONTH, mon );
240         RTC_WRITE( RTC_YEAR, year );
241         if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
242     }
243     RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
244     local_irq_restore(flags);
245
246     if (!op) {
247         /* read: adjust values */
248
249         if (hour & 0x80) {
250             hour &= ~0x80;
251             pm = 1;
252         }
253
254         if (!(ctrl & RTC_DM_BINARY)) {
255             BCD_TO_BIN(sec);
256             BCD_TO_BIN(min);
257             BCD_TO_BIN(hour);
258             BCD_TO_BIN(day);
259             BCD_TO_BIN(mon);
260             BCD_TO_BIN(year);
261             BCD_TO_BIN(wday);
262         }
263
264         if (!(ctrl & RTC_24H)) {
265             if (!pm && hour == 12)
266                 hour = 0;
267             else if (pm && hour != 12)
268                 hour += 12;
269         }
270
271         t->tm_sec  = sec;
272         t->tm_min  = min;
273         t->tm_hour = hour;
274         t->tm_mday = day;
275         t->tm_mon  = mon - 1;
276         t->tm_year = year + atari_rtc_year_offset;
277         t->tm_wday = wday - 1;
278     }
279
280     return( 0 );
281 }
282
283
284 int atari_mste_set_clock_mmss (unsigned long nowtime)
285 {
286     short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
287     struct MSTE_RTC val;
288     unsigned char rtc_minutes;
289
290     mste_read(&val);
291     rtc_minutes= val.min_ones + val.min_tens * 10;
292     if ((rtc_minutes < real_minutes
293          ? real_minutes - rtc_minutes
294          : rtc_minutes - real_minutes) < 30)
295     {
296         val.sec_ones = real_seconds % 10;
297         val.sec_tens = real_seconds / 10;
298         val.min_ones = real_minutes % 10;
299         val.min_tens = real_minutes / 10;
300         mste_write(&val);
301     }
302     else
303         return -1;
304     return 0;
305 }
306
307 int atari_tt_set_clock_mmss (unsigned long nowtime)
308 {
309     int retval = 0;
310     short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
311     unsigned char save_control, save_freq_select, rtc_minutes;
312
313     save_control = RTC_READ (RTC_CONTROL); /* tell the clock it's being set */
314     RTC_WRITE (RTC_CONTROL, save_control | RTC_SET);
315
316     save_freq_select = RTC_READ (RTC_FREQ_SELECT); /* stop and reset prescaler */
317     RTC_WRITE (RTC_FREQ_SELECT, save_freq_select | RTC_DIV_RESET2);
318
319     rtc_minutes = RTC_READ (RTC_MINUTES);
320     if (!(save_control & RTC_DM_BINARY))
321         BCD_TO_BIN (rtc_minutes);
322
323     /* Since we're only adjusting minutes and seconds, don't interfere
324        with hour overflow.  This avoids messing with unknown time zones
325        but requires your RTC not to be off by more than 30 minutes.  */
326     if ((rtc_minutes < real_minutes
327          ? real_minutes - rtc_minutes
328          : rtc_minutes - real_minutes) < 30)
329         {
330             if (!(save_control & RTC_DM_BINARY))
331                 {
332                     BIN_TO_BCD (real_seconds);
333                     BIN_TO_BCD (real_minutes);
334                 }
335             RTC_WRITE (RTC_SECONDS, real_seconds);
336             RTC_WRITE (RTC_MINUTES, real_minutes);
337         }
338     else
339         retval = -1;
340
341     RTC_WRITE (RTC_FREQ_SELECT, save_freq_select);
342     RTC_WRITE (RTC_CONTROL, save_control);
343     return retval;
344 }
345
346 /*
347  * Local variables:
348  *  c-indent-level: 4
349  *  tab-width: 8
350  * End:
351  */