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