GNU Linux-libre 4.14.266-gnu1
[releases.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 #include <linux/export.h>
21
22 #include <asm/atariints.h>
23
24 DEFINE_SPINLOCK(rtc_lock);
25 EXPORT_SYMBOL_GPL(rtc_lock);
26
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
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.  */
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
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
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
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
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
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
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  */