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