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