xref: /OK3568_Linux_fs/kernel/arch/m68k/atari/time.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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