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
mfp_timer_c_handler(int irq,void * dev_id)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
atari_sched_init(irq_handler_t timer_routine)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. */
atari_gettimeoffset(void)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
mste_read(struct MSTE_RTC * val)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
mste_write(struct MSTE_RTC * val)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
atari_mste_hwclk(int op,struct rtc_time * t)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
atari_tt_hwclk(int op,struct rtc_time * t)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
atari_mste_set_clock_mmss(unsigned long nowtime)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
atari_tt_set_clock_mmss(unsigned long nowtime)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 */
370