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