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
23void __init
24atari_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.  */
40unsigned 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
59static 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
73static 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
103int 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
162int 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
284int 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
307int 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