1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * linux/arch/m68k/atari/time.c
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Atari time and real time clock stuff
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General Public
9*4882a593Smuzhiyun * License. See the file COPYING in the main directory of this archive
10*4882a593Smuzhiyun * for more details.
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include <linux/types.h>
14*4882a593Smuzhiyun #include <linux/mc146818rtc.h>
15*4882a593Smuzhiyun #include <linux/interrupt.h>
16*4882a593Smuzhiyun #include <linux/init.h>
17*4882a593Smuzhiyun #include <linux/rtc.h>
18*4882a593Smuzhiyun #include <linux/bcd.h>
19*4882a593Smuzhiyun #include <linux/clocksource.h>
20*4882a593Smuzhiyun #include <linux/delay.h>
21*4882a593Smuzhiyun #include <linux/export.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #include <asm/atariints.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun DEFINE_SPINLOCK(rtc_lock);
26*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(rtc_lock);
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun static u64 atari_read_clk(struct clocksource *cs);
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun static struct clocksource atari_clk = {
31*4882a593Smuzhiyun .name = "mfp",
32*4882a593Smuzhiyun .rating = 100,
33*4882a593Smuzhiyun .read = atari_read_clk,
34*4882a593Smuzhiyun .mask = CLOCKSOURCE_MASK(32),
35*4882a593Smuzhiyun .flags = CLOCK_SOURCE_IS_CONTINUOUS,
36*4882a593Smuzhiyun };
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun static u32 clk_total;
39*4882a593Smuzhiyun static u8 last_timer_count;
40*4882a593Smuzhiyun
mfp_timer_c_handler(int irq,void * dev_id)41*4882a593Smuzhiyun static irqreturn_t mfp_timer_c_handler(int irq, void *dev_id)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun irq_handler_t timer_routine = dev_id;
44*4882a593Smuzhiyun unsigned long flags;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun local_irq_save(flags);
47*4882a593Smuzhiyun do {
48*4882a593Smuzhiyun last_timer_count = st_mfp.tim_dt_c;
49*4882a593Smuzhiyun } while (last_timer_count == 1);
50*4882a593Smuzhiyun clk_total += INT_TICKS;
51*4882a593Smuzhiyun timer_routine(0, NULL);
52*4882a593Smuzhiyun local_irq_restore(flags);
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun return IRQ_HANDLED;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun void __init
atari_sched_init(irq_handler_t timer_routine)58*4882a593Smuzhiyun atari_sched_init(irq_handler_t timer_routine)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun /* set Timer C data Register */
61*4882a593Smuzhiyun st_mfp.tim_dt_c = INT_TICKS;
62*4882a593Smuzhiyun /* start timer C, div = 1:100 */
63*4882a593Smuzhiyun st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60;
64*4882a593Smuzhiyun /* install interrupt service routine for MFP Timer C */
65*4882a593Smuzhiyun if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, IRQF_TIMER, "timer",
66*4882a593Smuzhiyun timer_routine))
67*4882a593Smuzhiyun pr_err("Couldn't register timer interrupt\n");
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun clocksource_register_hz(&atari_clk, INT_CLK);
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun /* ++andreas: gettimeoffset fixed to check for pending interrupt */
73*4882a593Smuzhiyun
atari_read_clk(struct clocksource * cs)74*4882a593Smuzhiyun static u64 atari_read_clk(struct clocksource *cs)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun unsigned long flags;
77*4882a593Smuzhiyun u8 count;
78*4882a593Smuzhiyun u32 ticks;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun local_irq_save(flags);
81*4882a593Smuzhiyun /* Ensure that the count is monotonically decreasing, even though
82*4882a593Smuzhiyun * the result may briefly stop changing after counter wrap-around.
83*4882a593Smuzhiyun */
84*4882a593Smuzhiyun count = min(st_mfp.tim_dt_c, last_timer_count);
85*4882a593Smuzhiyun last_timer_count = count;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun ticks = INT_TICKS - count;
88*4882a593Smuzhiyun ticks += clk_total;
89*4882a593Smuzhiyun local_irq_restore(flags);
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun return ticks;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun
mste_read(struct MSTE_RTC * val)95*4882a593Smuzhiyun static void mste_read(struct MSTE_RTC *val)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun #define COPY(v) val->v=(mste_rtc.v & 0xf)
98*4882a593Smuzhiyun do {
99*4882a593Smuzhiyun COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
100*4882a593Smuzhiyun COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
101*4882a593Smuzhiyun COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
102*4882a593Smuzhiyun COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
103*4882a593Smuzhiyun COPY(year_tens) ;
104*4882a593Smuzhiyun /* prevent from reading the clock while it changed */
105*4882a593Smuzhiyun } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
106*4882a593Smuzhiyun #undef COPY
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
mste_write(struct MSTE_RTC * val)109*4882a593Smuzhiyun static void mste_write(struct MSTE_RTC *val)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun #define COPY(v) mste_rtc.v=val->v
112*4882a593Smuzhiyun do {
113*4882a593Smuzhiyun COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
114*4882a593Smuzhiyun COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
115*4882a593Smuzhiyun COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
116*4882a593Smuzhiyun COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
117*4882a593Smuzhiyun COPY(year_tens) ;
118*4882a593Smuzhiyun /* prevent from writing the clock while it changed */
119*4882a593Smuzhiyun } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
120*4882a593Smuzhiyun #undef COPY
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun #define RTC_READ(reg) \
124*4882a593Smuzhiyun ({ unsigned char __val; \
125*4882a593Smuzhiyun (void) atari_writeb(reg,&tt_rtc.regsel); \
126*4882a593Smuzhiyun __val = tt_rtc.data; \
127*4882a593Smuzhiyun __val; \
128*4882a593Smuzhiyun })
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun #define RTC_WRITE(reg,val) \
131*4882a593Smuzhiyun do { \
132*4882a593Smuzhiyun atari_writeb(reg,&tt_rtc.regsel); \
133*4882a593Smuzhiyun tt_rtc.data = (val); \
134*4882a593Smuzhiyun } while(0)
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun #define HWCLK_POLL_INTERVAL 5
138*4882a593Smuzhiyun
atari_mste_hwclk(int op,struct rtc_time * t)139*4882a593Smuzhiyun int atari_mste_hwclk( int op, struct rtc_time *t )
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun int hour, year;
142*4882a593Smuzhiyun int hr24=0;
143*4882a593Smuzhiyun struct MSTE_RTC val;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun mste_rtc.mode=(mste_rtc.mode | 1);
146*4882a593Smuzhiyun hr24=mste_rtc.mon_tens & 1;
147*4882a593Smuzhiyun mste_rtc.mode=(mste_rtc.mode & ~1);
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun if (op) {
150*4882a593Smuzhiyun /* write: prepare values */
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun val.sec_ones = t->tm_sec % 10;
153*4882a593Smuzhiyun val.sec_tens = t->tm_sec / 10;
154*4882a593Smuzhiyun val.min_ones = t->tm_min % 10;
155*4882a593Smuzhiyun val.min_tens = t->tm_min / 10;
156*4882a593Smuzhiyun hour = t->tm_hour;
157*4882a593Smuzhiyun if (!hr24) {
158*4882a593Smuzhiyun if (hour > 11)
159*4882a593Smuzhiyun hour += 20 - 12;
160*4882a593Smuzhiyun if (hour == 0 || hour == 20)
161*4882a593Smuzhiyun hour += 12;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun val.hr_ones = hour % 10;
164*4882a593Smuzhiyun val.hr_tens = hour / 10;
165*4882a593Smuzhiyun val.day_ones = t->tm_mday % 10;
166*4882a593Smuzhiyun val.day_tens = t->tm_mday / 10;
167*4882a593Smuzhiyun val.mon_ones = (t->tm_mon+1) % 10;
168*4882a593Smuzhiyun val.mon_tens = (t->tm_mon+1) / 10;
169*4882a593Smuzhiyun year = t->tm_year - 80;
170*4882a593Smuzhiyun val.year_ones = year % 10;
171*4882a593Smuzhiyun val.year_tens = year / 10;
172*4882a593Smuzhiyun val.weekday = t->tm_wday;
173*4882a593Smuzhiyun mste_write(&val);
174*4882a593Smuzhiyun mste_rtc.mode=(mste_rtc.mode | 1);
175*4882a593Smuzhiyun val.year_ones = (year % 4); /* leap year register */
176*4882a593Smuzhiyun mste_rtc.mode=(mste_rtc.mode & ~1);
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun else {
179*4882a593Smuzhiyun mste_read(&val);
180*4882a593Smuzhiyun t->tm_sec = val.sec_ones + val.sec_tens * 10;
181*4882a593Smuzhiyun t->tm_min = val.min_ones + val.min_tens * 10;
182*4882a593Smuzhiyun hour = val.hr_ones + val.hr_tens * 10;
183*4882a593Smuzhiyun if (!hr24) {
184*4882a593Smuzhiyun if (hour == 12 || hour == 12 + 20)
185*4882a593Smuzhiyun hour -= 12;
186*4882a593Smuzhiyun if (hour >= 20)
187*4882a593Smuzhiyun hour += 12 - 20;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun t->tm_hour = hour;
190*4882a593Smuzhiyun t->tm_mday = val.day_ones + val.day_tens * 10;
191*4882a593Smuzhiyun t->tm_mon = val.mon_ones + val.mon_tens * 10 - 1;
192*4882a593Smuzhiyun t->tm_year = val.year_ones + val.year_tens * 10 + 80;
193*4882a593Smuzhiyun t->tm_wday = val.weekday;
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun return 0;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
atari_tt_hwclk(int op,struct rtc_time * t)198*4882a593Smuzhiyun int atari_tt_hwclk( int op, struct rtc_time *t )
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0;
201*4882a593Smuzhiyun unsigned long flags;
202*4882a593Smuzhiyun unsigned char ctrl;
203*4882a593Smuzhiyun int pm = 0;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun ctrl = RTC_READ(RTC_CONTROL); /* control registers are
206*4882a593Smuzhiyun * independent from the UIP */
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun if (op) {
209*4882a593Smuzhiyun /* write: prepare values */
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun sec = t->tm_sec;
212*4882a593Smuzhiyun min = t->tm_min;
213*4882a593Smuzhiyun hour = t->tm_hour;
214*4882a593Smuzhiyun day = t->tm_mday;
215*4882a593Smuzhiyun mon = t->tm_mon + 1;
216*4882a593Smuzhiyun year = t->tm_year - atari_rtc_year_offset;
217*4882a593Smuzhiyun wday = t->tm_wday + (t->tm_wday >= 0);
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun if (!(ctrl & RTC_24H)) {
220*4882a593Smuzhiyun if (hour > 11) {
221*4882a593Smuzhiyun pm = 0x80;
222*4882a593Smuzhiyun if (hour != 12)
223*4882a593Smuzhiyun hour -= 12;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun else if (hour == 0)
226*4882a593Smuzhiyun hour = 12;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun if (!(ctrl & RTC_DM_BINARY)) {
230*4882a593Smuzhiyun sec = bin2bcd(sec);
231*4882a593Smuzhiyun min = bin2bcd(min);
232*4882a593Smuzhiyun hour = bin2bcd(hour);
233*4882a593Smuzhiyun day = bin2bcd(day);
234*4882a593Smuzhiyun mon = bin2bcd(mon);
235*4882a593Smuzhiyun year = bin2bcd(year);
236*4882a593Smuzhiyun if (wday >= 0)
237*4882a593Smuzhiyun wday = bin2bcd(wday);
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun /* Reading/writing the clock registers is a bit critical due to
242*4882a593Smuzhiyun * the regular update cycle of the RTC. While an update is in
243*4882a593Smuzhiyun * progress, registers 0..9 shouldn't be touched.
244*4882a593Smuzhiyun * The problem is solved like that: If an update is currently in
245*4882a593Smuzhiyun * progress (the UIP bit is set), the process sleeps for a while
246*4882a593Smuzhiyun * (50ms). This really should be enough, since the update cycle
247*4882a593Smuzhiyun * normally needs 2 ms.
248*4882a593Smuzhiyun * If the UIP bit reads as 0, we have at least 244 usecs until the
249*4882a593Smuzhiyun * update starts. This should be enough... But to be sure,
250*4882a593Smuzhiyun * additionally the RTC_SET bit is set to prevent an update cycle.
251*4882a593Smuzhiyun */
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
254*4882a593Smuzhiyun if (in_atomic() || irqs_disabled())
255*4882a593Smuzhiyun mdelay(1);
256*4882a593Smuzhiyun else
257*4882a593Smuzhiyun schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun local_irq_save(flags);
261*4882a593Smuzhiyun RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
262*4882a593Smuzhiyun if (!op) {
263*4882a593Smuzhiyun sec = RTC_READ( RTC_SECONDS );
264*4882a593Smuzhiyun min = RTC_READ( RTC_MINUTES );
265*4882a593Smuzhiyun hour = RTC_READ( RTC_HOURS );
266*4882a593Smuzhiyun day = RTC_READ( RTC_DAY_OF_MONTH );
267*4882a593Smuzhiyun mon = RTC_READ( RTC_MONTH );
268*4882a593Smuzhiyun year = RTC_READ( RTC_YEAR );
269*4882a593Smuzhiyun wday = RTC_READ( RTC_DAY_OF_WEEK );
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun else {
272*4882a593Smuzhiyun RTC_WRITE( RTC_SECONDS, sec );
273*4882a593Smuzhiyun RTC_WRITE( RTC_MINUTES, min );
274*4882a593Smuzhiyun RTC_WRITE( RTC_HOURS, hour + pm);
275*4882a593Smuzhiyun RTC_WRITE( RTC_DAY_OF_MONTH, day );
276*4882a593Smuzhiyun RTC_WRITE( RTC_MONTH, mon );
277*4882a593Smuzhiyun RTC_WRITE( RTC_YEAR, year );
278*4882a593Smuzhiyun if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
281*4882a593Smuzhiyun local_irq_restore(flags);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun if (!op) {
284*4882a593Smuzhiyun /* read: adjust values */
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun if (hour & 0x80) {
287*4882a593Smuzhiyun hour &= ~0x80;
288*4882a593Smuzhiyun pm = 1;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun if (!(ctrl & RTC_DM_BINARY)) {
292*4882a593Smuzhiyun sec = bcd2bin(sec);
293*4882a593Smuzhiyun min = bcd2bin(min);
294*4882a593Smuzhiyun hour = bcd2bin(hour);
295*4882a593Smuzhiyun day = bcd2bin(day);
296*4882a593Smuzhiyun mon = bcd2bin(mon);
297*4882a593Smuzhiyun year = bcd2bin(year);
298*4882a593Smuzhiyun wday = bcd2bin(wday);
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun if (!(ctrl & RTC_24H)) {
302*4882a593Smuzhiyun if (!pm && hour == 12)
303*4882a593Smuzhiyun hour = 0;
304*4882a593Smuzhiyun else if (pm && hour != 12)
305*4882a593Smuzhiyun hour += 12;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun t->tm_sec = sec;
309*4882a593Smuzhiyun t->tm_min = min;
310*4882a593Smuzhiyun t->tm_hour = hour;
311*4882a593Smuzhiyun t->tm_mday = day;
312*4882a593Smuzhiyun t->tm_mon = mon - 1;
313*4882a593Smuzhiyun t->tm_year = year + atari_rtc_year_offset;
314*4882a593Smuzhiyun t->tm_wday = wday - 1;
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun return( 0 );
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun /*
321*4882a593Smuzhiyun * Local variables:
322*4882a593Smuzhiyun * c-indent-level: 4
323*4882a593Smuzhiyun * tab-width: 8
324*4882a593Smuzhiyun * End:
325*4882a593Smuzhiyun */
326