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