blob: 1edde27fa32dbb600dd5d07822cfd0bb5281b992 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Geert Uytterhoeven69961c32006-10-09 22:23:31 +020019#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
21#include <asm/atariints.h>
22
Geert Uytterhoeven7ae48332008-10-13 21:58:55 +020023DEFINE_SPINLOCK(rtc_lock);
24EXPORT_SYMBOL_GPL(rtc_lock);
25
Linus Torvalds1da177e2005-04-16 15:20:36 -070026void __init
David Howells40220c12006-10-09 12:19:47 +010027atari_sched_init(irq_handler_t timer_routine)
Linus Torvalds1da177e2005-04-16 15:20:36 -070028{
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. */
43unsigned 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
62static 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
76static 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
106int 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
165int 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)) {
Adrian Bunk5b1d5f92008-10-13 21:58:47 +0200197 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 }
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
Geert Uytterhoeven69961c32006-10-09 22:23:31 +0200220 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
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)) {
Adrian Bunk5b1d5f92008-10-13 21:58:47 +0200259 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 }
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
288int 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
311int 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))
Adrian Bunk5b1d5f92008-10-13 21:58:47 +0200325 rtc_minutes = bcd2bin(rtc_minutes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
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 {
Adrian Bunk5b1d5f92008-10-13 21:58:47 +0200336 real_seconds = bin2bcd(real_seconds);
337 real_minutes = bin2bcd(real_minutes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 }
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 */