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