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