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