xref: /openbmc/linux/arch/m68k/atari/time.c (revision fa60ce2c)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/arch/m68k/atari/time.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Atari time and real time clock stuff
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * This file is subject to the terms and conditions of the GNU General Public
91da177e4SLinus Torvalds  * License.  See the file COPYING in the main directory of this archive
101da177e4SLinus Torvalds  * for more details.
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include <linux/types.h>
141da177e4SLinus Torvalds #include <linux/mc146818rtc.h>
151da177e4SLinus Torvalds #include <linux/interrupt.h>
161da177e4SLinus Torvalds #include <linux/init.h>
171da177e4SLinus Torvalds #include <linux/rtc.h>
181da177e4SLinus Torvalds #include <linux/bcd.h>
1926ccd2d3SFinn Thain #include <linux/clocksource.h>
2069961c37SGeert Uytterhoeven #include <linux/delay.h>
2112799fe4SPaul Gortmaker #include <linux/export.h>
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds #include <asm/atariints.h>
24d6444094SArnd Bergmann #include <asm/machdep.h>
251da177e4SLinus Torvalds 
267ae4833aSGeert Uytterhoeven DEFINE_SPINLOCK(rtc_lock);
277ae4833aSGeert Uytterhoeven EXPORT_SYMBOL_GPL(rtc_lock);
287ae4833aSGeert Uytterhoeven 
2926ccd2d3SFinn Thain static u64 atari_read_clk(struct clocksource *cs);
3026ccd2d3SFinn Thain 
3126ccd2d3SFinn Thain static struct clocksource atari_clk = {
3226ccd2d3SFinn Thain 	.name   = "mfp",
3326ccd2d3SFinn Thain 	.rating = 100,
3426ccd2d3SFinn Thain 	.read   = atari_read_clk,
3526ccd2d3SFinn Thain 	.mask   = CLOCKSOURCE_MASK(32),
3626ccd2d3SFinn Thain 	.flags  = CLOCK_SOURCE_IS_CONTINUOUS,
3726ccd2d3SFinn Thain };
3826ccd2d3SFinn Thain 
3926ccd2d3SFinn Thain static u32 clk_total;
4026ccd2d3SFinn Thain static u8 last_timer_count;
4126ccd2d3SFinn Thain 
mfp_timer_c_handler(int irq,void * dev_id)421efdd4bdSFinn Thain static irqreturn_t mfp_timer_c_handler(int irq, void *dev_id)
431efdd4bdSFinn Thain {
441efdd4bdSFinn Thain 	unsigned long flags;
451efdd4bdSFinn Thain 
461efdd4bdSFinn Thain 	local_irq_save(flags);
4726ccd2d3SFinn Thain 	do {
4826ccd2d3SFinn Thain 		last_timer_count = st_mfp.tim_dt_c;
4926ccd2d3SFinn Thain 	} while (last_timer_count == 1);
5026ccd2d3SFinn Thain 	clk_total += INT_TICKS;
5142f1d57fSArnd Bergmann 	legacy_timer_tick(1);
52d6444094SArnd Bergmann 	timer_heartbeat();
531efdd4bdSFinn Thain 	local_irq_restore(flags);
541efdd4bdSFinn Thain 
551efdd4bdSFinn Thain 	return IRQ_HANDLED;
561efdd4bdSFinn Thain }
571efdd4bdSFinn Thain 
581da177e4SLinus Torvalds void __init
atari_sched_init(void)59*f9a01539SArnd Bergmann atari_sched_init(void)
601da177e4SLinus Torvalds {
611da177e4SLinus Torvalds     /* set Timer C data Register */
623d92e8f3SGeert Uytterhoeven     st_mfp.tim_dt_c = INT_TICKS;
631da177e4SLinus Torvalds     /* start timer C, div = 1:100 */
643d92e8f3SGeert Uytterhoeven     st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60;
651da177e4SLinus Torvalds     /* install interrupt service routine for MFP Timer C */
6626ccd2d3SFinn Thain     if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, IRQF_TIMER, "timer",
6742f1d57fSArnd Bergmann                     NULL))
685b8b4c3dSGeert Uytterhoeven 	pr_err("Couldn't register timer interrupt\n");
6926ccd2d3SFinn Thain 
7026ccd2d3SFinn Thain     clocksource_register_hz(&atari_clk, INT_CLK);
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds /* ++andreas: gettimeoffset fixed to check for pending interrupt */
741da177e4SLinus Torvalds 
atari_read_clk(struct clocksource * cs)7526ccd2d3SFinn Thain static u64 atari_read_clk(struct clocksource *cs)
761da177e4SLinus Torvalds {
7726ccd2d3SFinn Thain 	unsigned long flags;
7826ccd2d3SFinn Thain 	u8 count;
7926ccd2d3SFinn Thain 	u32 ticks;
801da177e4SLinus Torvalds 
8126ccd2d3SFinn Thain 	local_irq_save(flags);
8226ccd2d3SFinn Thain 	/* Ensure that the count is monotonically decreasing, even though
8326ccd2d3SFinn Thain 	 * the result may briefly stop changing after counter wrap-around.
8426ccd2d3SFinn Thain 	 */
8526ccd2d3SFinn Thain 	count = min(st_mfp.tim_dt_c, last_timer_count);
8626ccd2d3SFinn Thain 	last_timer_count = count;
871da177e4SLinus Torvalds 
8826ccd2d3SFinn Thain 	ticks = INT_TICKS - count;
8926ccd2d3SFinn Thain 	ticks += clk_total;
9026ccd2d3SFinn Thain 	local_irq_restore(flags);
911da177e4SLinus Torvalds 
9226ccd2d3SFinn Thain 	return ticks;
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 
mste_read(struct MSTE_RTC * val)961da177e4SLinus Torvalds static void mste_read(struct MSTE_RTC *val)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds #define COPY(v) val->v=(mste_rtc.v & 0xf)
991da177e4SLinus Torvalds 	do {
1001da177e4SLinus Torvalds 		COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
1011da177e4SLinus Torvalds 		COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
1021da177e4SLinus Torvalds 		COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
1031da177e4SLinus Torvalds 		COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
1041da177e4SLinus Torvalds 		COPY(year_tens) ;
1051da177e4SLinus Torvalds 	/* prevent from reading the clock while it changed */
1061da177e4SLinus Torvalds 	} while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
1071da177e4SLinus Torvalds #undef COPY
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds 
mste_write(struct MSTE_RTC * val)1101da177e4SLinus Torvalds static void mste_write(struct MSTE_RTC *val)
1111da177e4SLinus Torvalds {
1121da177e4SLinus Torvalds #define COPY(v) mste_rtc.v=val->v
1131da177e4SLinus Torvalds 	do {
1141da177e4SLinus Torvalds 		COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
1151da177e4SLinus Torvalds 		COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
1161da177e4SLinus Torvalds 		COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
1171da177e4SLinus Torvalds 		COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
1181da177e4SLinus Torvalds 		COPY(year_tens) ;
1191da177e4SLinus Torvalds 	/* prevent from writing the clock while it changed */
1201da177e4SLinus Torvalds 	} while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
1211da177e4SLinus Torvalds #undef COPY
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds #define	RTC_READ(reg)				\
1251da177e4SLinus Torvalds     ({	unsigned char	__val;			\
1261da177e4SLinus Torvalds 		(void) atari_writeb(reg,&tt_rtc.regsel);	\
1271da177e4SLinus Torvalds 		__val = tt_rtc.data;		\
1281da177e4SLinus Torvalds 		__val;				\
1291da177e4SLinus Torvalds 	})
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds #define	RTC_WRITE(reg,val)			\
1321da177e4SLinus Torvalds     do {					\
1331da177e4SLinus Torvalds 		atari_writeb(reg,&tt_rtc.regsel);	\
1341da177e4SLinus Torvalds 		tt_rtc.data = (val);		\
1351da177e4SLinus Torvalds 	} while(0)
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds #define HWCLK_POLL_INTERVAL	5
1391da177e4SLinus Torvalds 
atari_mste_hwclk(int op,struct rtc_time * t)1401da177e4SLinus Torvalds int atari_mste_hwclk( int op, struct rtc_time *t )
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds     int hour, year;
1431da177e4SLinus Torvalds     int hr24=0;
1441da177e4SLinus Torvalds     struct MSTE_RTC val;
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds     mste_rtc.mode=(mste_rtc.mode | 1);
1471da177e4SLinus Torvalds     hr24=mste_rtc.mon_tens & 1;
1481da177e4SLinus Torvalds     mste_rtc.mode=(mste_rtc.mode & ~1);
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds     if (op) {
1511da177e4SLinus Torvalds         /* write: prepare values */
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds         val.sec_ones = t->tm_sec % 10;
1541da177e4SLinus Torvalds         val.sec_tens = t->tm_sec / 10;
1551da177e4SLinus Torvalds         val.min_ones = t->tm_min % 10;
1561da177e4SLinus Torvalds         val.min_tens = t->tm_min / 10;
1571da177e4SLinus Torvalds         hour = t->tm_hour;
1581da177e4SLinus Torvalds         if (!hr24) {
1591da177e4SLinus Torvalds 	    if (hour > 11)
1601da177e4SLinus Torvalds 		hour += 20 - 12;
1611da177e4SLinus Torvalds 	    if (hour == 0 || hour == 20)
1621da177e4SLinus Torvalds 		hour += 12;
1631da177e4SLinus Torvalds         }
1641da177e4SLinus Torvalds         val.hr_ones = hour % 10;
1651da177e4SLinus Torvalds         val.hr_tens = hour / 10;
1661da177e4SLinus Torvalds         val.day_ones = t->tm_mday % 10;
1671da177e4SLinus Torvalds         val.day_tens = t->tm_mday / 10;
1681da177e4SLinus Torvalds         val.mon_ones = (t->tm_mon+1) % 10;
1691da177e4SLinus Torvalds         val.mon_tens = (t->tm_mon+1) / 10;
1701da177e4SLinus Torvalds         year = t->tm_year - 80;
1711da177e4SLinus Torvalds         val.year_ones = year % 10;
1721da177e4SLinus Torvalds         val.year_tens = year / 10;
1731da177e4SLinus Torvalds         val.weekday = t->tm_wday;
1741da177e4SLinus Torvalds         mste_write(&val);
1751da177e4SLinus Torvalds         mste_rtc.mode=(mste_rtc.mode | 1);
1761da177e4SLinus Torvalds         val.year_ones = (year % 4);	/* leap year register */
1771da177e4SLinus Torvalds         mste_rtc.mode=(mste_rtc.mode & ~1);
1781da177e4SLinus Torvalds     }
1791da177e4SLinus Torvalds     else {
1801da177e4SLinus Torvalds         mste_read(&val);
1811da177e4SLinus Torvalds         t->tm_sec = val.sec_ones + val.sec_tens * 10;
1821da177e4SLinus Torvalds         t->tm_min = val.min_ones + val.min_tens * 10;
1831da177e4SLinus Torvalds         hour = val.hr_ones + val.hr_tens * 10;
1841da177e4SLinus Torvalds 	if (!hr24) {
1851da177e4SLinus Torvalds 	    if (hour == 12 || hour == 12 + 20)
1861da177e4SLinus Torvalds 		hour -= 12;
1871da177e4SLinus Torvalds 	    if (hour >= 20)
1881da177e4SLinus Torvalds                 hour += 12 - 20;
1891da177e4SLinus Torvalds         }
1901da177e4SLinus Torvalds 	t->tm_hour = hour;
1911da177e4SLinus Torvalds 	t->tm_mday = val.day_ones + val.day_tens * 10;
1921da177e4SLinus Torvalds         t->tm_mon  = val.mon_ones + val.mon_tens * 10 - 1;
1931da177e4SLinus Torvalds         t->tm_year = val.year_ones + val.year_tens * 10 + 80;
1941da177e4SLinus Torvalds         t->tm_wday = val.weekday;
1951da177e4SLinus Torvalds     }
1961da177e4SLinus Torvalds     return 0;
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds 
atari_tt_hwclk(int op,struct rtc_time * t)1991da177e4SLinus Torvalds int atari_tt_hwclk( int op, struct rtc_time *t )
2001da177e4SLinus Torvalds {
2011da177e4SLinus Torvalds     int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0;
2021da177e4SLinus Torvalds     unsigned long	flags;
2031da177e4SLinus Torvalds     unsigned char	ctrl;
2041da177e4SLinus Torvalds     int pm = 0;
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds     ctrl = RTC_READ(RTC_CONTROL); /* control registers are
2071da177e4SLinus Torvalds                                    * independent from the UIP */
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds     if (op) {
2101da177e4SLinus Torvalds         /* write: prepare values */
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds         sec  = t->tm_sec;
2131da177e4SLinus Torvalds         min  = t->tm_min;
2141da177e4SLinus Torvalds         hour = t->tm_hour;
2151da177e4SLinus Torvalds         day  = t->tm_mday;
2161da177e4SLinus Torvalds         mon  = t->tm_mon + 1;
2171da177e4SLinus Torvalds         year = t->tm_year - atari_rtc_year_offset;
2181da177e4SLinus Torvalds         wday = t->tm_wday + (t->tm_wday >= 0);
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds         if (!(ctrl & RTC_24H)) {
2211da177e4SLinus Torvalds 	    if (hour > 11) {
2221da177e4SLinus Torvalds 		pm = 0x80;
2231da177e4SLinus Torvalds 		if (hour != 12)
2241da177e4SLinus Torvalds 		    hour -= 12;
2251da177e4SLinus Torvalds 	    }
2261da177e4SLinus Torvalds 	    else if (hour == 0)
2271da177e4SLinus Torvalds 		hour = 12;
2281da177e4SLinus Torvalds         }
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds         if (!(ctrl & RTC_DM_BINARY)) {
2315b1d5f95SAdrian Bunk 	    sec = bin2bcd(sec);
2325b1d5f95SAdrian Bunk 	    min = bin2bcd(min);
2335b1d5f95SAdrian Bunk 	    hour = bin2bcd(hour);
2345b1d5f95SAdrian Bunk 	    day = bin2bcd(day);
2355b1d5f95SAdrian Bunk 	    mon = bin2bcd(mon);
2365b1d5f95SAdrian Bunk 	    year = bin2bcd(year);
2375b1d5f95SAdrian Bunk 	    if (wday >= 0)
2385b1d5f95SAdrian Bunk 		wday = bin2bcd(wday);
2391da177e4SLinus Torvalds         }
2401da177e4SLinus Torvalds     }
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds     /* Reading/writing the clock registers is a bit critical due to
2431da177e4SLinus Torvalds      * the regular update cycle of the RTC. While an update is in
2441da177e4SLinus Torvalds      * progress, registers 0..9 shouldn't be touched.
2451da177e4SLinus Torvalds      * The problem is solved like that: If an update is currently in
2461da177e4SLinus Torvalds      * progress (the UIP bit is set), the process sleeps for a while
2471da177e4SLinus Torvalds      * (50ms). This really should be enough, since the update cycle
2481da177e4SLinus Torvalds      * normally needs 2 ms.
2491da177e4SLinus Torvalds      * If the UIP bit reads as 0, we have at least 244 usecs until the
2501da177e4SLinus Torvalds      * update starts. This should be enough... But to be sure,
2511da177e4SLinus Torvalds      * additionally the RTC_SET bit is set to prevent an update cycle.
2521da177e4SLinus Torvalds      */
2531da177e4SLinus Torvalds 
25469961c37SGeert Uytterhoeven     while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
25569961c37SGeert Uytterhoeven 	if (in_atomic() || irqs_disabled())
25669961c37SGeert Uytterhoeven 	    mdelay(1);
25769961c37SGeert Uytterhoeven 	else
25828faa429SNishanth Aravamudan 	    schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
25969961c37SGeert Uytterhoeven     }
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds     local_irq_save(flags);
2621da177e4SLinus Torvalds     RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
2631da177e4SLinus Torvalds     if (!op) {
2641da177e4SLinus Torvalds         sec  = RTC_READ( RTC_SECONDS );
2651da177e4SLinus Torvalds         min  = RTC_READ( RTC_MINUTES );
2661da177e4SLinus Torvalds         hour = RTC_READ( RTC_HOURS );
2671da177e4SLinus Torvalds         day  = RTC_READ( RTC_DAY_OF_MONTH );
2681da177e4SLinus Torvalds         mon  = RTC_READ( RTC_MONTH );
2691da177e4SLinus Torvalds         year = RTC_READ( RTC_YEAR );
2701da177e4SLinus Torvalds         wday = RTC_READ( RTC_DAY_OF_WEEK );
2711da177e4SLinus Torvalds     }
2721da177e4SLinus Torvalds     else {
2731da177e4SLinus Torvalds         RTC_WRITE( RTC_SECONDS, sec );
2741da177e4SLinus Torvalds         RTC_WRITE( RTC_MINUTES, min );
2751da177e4SLinus Torvalds         RTC_WRITE( RTC_HOURS, hour + pm);
2761da177e4SLinus Torvalds         RTC_WRITE( RTC_DAY_OF_MONTH, day );
2771da177e4SLinus Torvalds         RTC_WRITE( RTC_MONTH, mon );
2781da177e4SLinus Torvalds         RTC_WRITE( RTC_YEAR, year );
2791da177e4SLinus Torvalds         if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
2801da177e4SLinus Torvalds     }
2811da177e4SLinus Torvalds     RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
2821da177e4SLinus Torvalds     local_irq_restore(flags);
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds     if (!op) {
2851da177e4SLinus Torvalds         /* read: adjust values */
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds         if (hour & 0x80) {
2881da177e4SLinus Torvalds 	    hour &= ~0x80;
2891da177e4SLinus Torvalds 	    pm = 1;
2901da177e4SLinus Torvalds 	}
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	if (!(ctrl & RTC_DM_BINARY)) {
2935b1d5f95SAdrian Bunk 	    sec = bcd2bin(sec);
2945b1d5f95SAdrian Bunk 	    min = bcd2bin(min);
2955b1d5f95SAdrian Bunk 	    hour = bcd2bin(hour);
2965b1d5f95SAdrian Bunk 	    day = bcd2bin(day);
2975b1d5f95SAdrian Bunk 	    mon = bcd2bin(mon);
2985b1d5f95SAdrian Bunk 	    year = bcd2bin(year);
2995b1d5f95SAdrian Bunk 	    wday = bcd2bin(wday);
3001da177e4SLinus Torvalds         }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds         if (!(ctrl & RTC_24H)) {
3031da177e4SLinus Torvalds 	    if (!pm && hour == 12)
3041da177e4SLinus Torvalds 		hour = 0;
3051da177e4SLinus Torvalds 	    else if (pm && hour != 12)
3061da177e4SLinus Torvalds 		hour += 12;
3071da177e4SLinus Torvalds         }
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds         t->tm_sec  = sec;
3101da177e4SLinus Torvalds         t->tm_min  = min;
3111da177e4SLinus Torvalds         t->tm_hour = hour;
3121da177e4SLinus Torvalds         t->tm_mday = day;
3131da177e4SLinus Torvalds         t->tm_mon  = mon - 1;
3141da177e4SLinus Torvalds         t->tm_year = year + atari_rtc_year_offset;
3151da177e4SLinus Torvalds         t->tm_wday = wday - 1;
3161da177e4SLinus Torvalds     }
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds     return( 0 );
3191da177e4SLinus Torvalds }
320