xref: /openbmc/linux/arch/m68k/atari/time.c (revision f15cbe6f1a4b4d9df59142fc8e4abb973302cf44)
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  */
352