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