xref: /openbmc/linux/arch/m68k/atari/time.c (revision 40220c1a)
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>
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include <asm/atariints.h>
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds void __init
2340220c1aSDavid Howells atari_sched_init(irq_handler_t timer_routine)
241da177e4SLinus Torvalds {
251da177e4SLinus Torvalds     /* set Timer C data Register */
261da177e4SLinus Torvalds     mfp.tim_dt_c = INT_TICKS;
271da177e4SLinus Torvalds     /* start timer C, div = 1:100 */
281da177e4SLinus Torvalds     mfp.tim_ct_cd = (mfp.tim_ct_cd & 15) | 0x60;
291da177e4SLinus Torvalds     /* install interrupt service routine for MFP Timer C */
301da177e4SLinus Torvalds     request_irq(IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW,
311da177e4SLinus Torvalds                 "timer", timer_routine);
321da177e4SLinus Torvalds }
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds /* ++andreas: gettimeoffset fixed to check for pending interrupt */
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds #define TICK_SIZE 10000
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds /* This is always executed with interrupts disabled.  */
391da177e4SLinus Torvalds unsigned long atari_gettimeoffset (void)
401da177e4SLinus Torvalds {
411da177e4SLinus Torvalds   unsigned long ticks, offset = 0;
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds   /* read MFP timer C current value */
441da177e4SLinus Torvalds   ticks = mfp.tim_dt_c;
451da177e4SLinus Torvalds   /* The probability of underflow is less than 2% */
461da177e4SLinus Torvalds   if (ticks > INT_TICKS - INT_TICKS / 50)
471da177e4SLinus Torvalds     /* Check for pending timer interrupt */
481da177e4SLinus Torvalds     if (mfp.int_pn_b & (1 << 5))
491da177e4SLinus Torvalds       offset = TICK_SIZE;
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds   ticks = INT_TICKS - ticks;
521da177e4SLinus Torvalds   ticks = ticks * 10000L / INT_TICKS;
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds   return ticks + offset;
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds static void mste_read(struct MSTE_RTC *val)
591da177e4SLinus Torvalds {
601da177e4SLinus Torvalds #define COPY(v) val->v=(mste_rtc.v & 0xf)
611da177e4SLinus Torvalds 	do {
621da177e4SLinus Torvalds 		COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
631da177e4SLinus Torvalds 		COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
641da177e4SLinus Torvalds 		COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
651da177e4SLinus Torvalds 		COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
661da177e4SLinus Torvalds 		COPY(year_tens) ;
671da177e4SLinus Torvalds 	/* prevent from reading the clock while it changed */
681da177e4SLinus Torvalds 	} while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
691da177e4SLinus Torvalds #undef COPY
701da177e4SLinus Torvalds }
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds static void mste_write(struct MSTE_RTC *val)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds #define COPY(v) mste_rtc.v=val->v
751da177e4SLinus Torvalds 	do {
761da177e4SLinus Torvalds 		COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
771da177e4SLinus Torvalds 		COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
781da177e4SLinus Torvalds 		COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
791da177e4SLinus Torvalds 		COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
801da177e4SLinus Torvalds 		COPY(year_tens) ;
811da177e4SLinus Torvalds 	/* prevent from writing the clock while it changed */
821da177e4SLinus Torvalds 	} while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
831da177e4SLinus Torvalds #undef COPY
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds #define	RTC_READ(reg)				\
871da177e4SLinus Torvalds     ({	unsigned char	__val;			\
881da177e4SLinus Torvalds 		(void) atari_writeb(reg,&tt_rtc.regsel);	\
891da177e4SLinus Torvalds 		__val = tt_rtc.data;		\
901da177e4SLinus Torvalds 		__val;				\
911da177e4SLinus Torvalds 	})
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds #define	RTC_WRITE(reg,val)			\
941da177e4SLinus Torvalds     do {					\
951da177e4SLinus Torvalds 		atari_writeb(reg,&tt_rtc.regsel);	\
961da177e4SLinus Torvalds 		tt_rtc.data = (val);		\
971da177e4SLinus Torvalds 	} while(0)
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds #define HWCLK_POLL_INTERVAL	5
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds int atari_mste_hwclk( int op, struct rtc_time *t )
1031da177e4SLinus Torvalds {
1041da177e4SLinus Torvalds     int hour, year;
1051da177e4SLinus Torvalds     int hr24=0;
1061da177e4SLinus Torvalds     struct MSTE_RTC val;
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds     mste_rtc.mode=(mste_rtc.mode | 1);
1091da177e4SLinus Torvalds     hr24=mste_rtc.mon_tens & 1;
1101da177e4SLinus Torvalds     mste_rtc.mode=(mste_rtc.mode & ~1);
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds     if (op) {
1131da177e4SLinus Torvalds         /* write: prepare values */
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds         val.sec_ones = t->tm_sec % 10;
1161da177e4SLinus Torvalds         val.sec_tens = t->tm_sec / 10;
1171da177e4SLinus Torvalds         val.min_ones = t->tm_min % 10;
1181da177e4SLinus Torvalds         val.min_tens = t->tm_min / 10;
1191da177e4SLinus Torvalds         hour = t->tm_hour;
1201da177e4SLinus Torvalds         if (!hr24) {
1211da177e4SLinus Torvalds 	    if (hour > 11)
1221da177e4SLinus Torvalds 		hour += 20 - 12;
1231da177e4SLinus Torvalds 	    if (hour == 0 || hour == 20)
1241da177e4SLinus Torvalds 		hour += 12;
1251da177e4SLinus Torvalds         }
1261da177e4SLinus Torvalds         val.hr_ones = hour % 10;
1271da177e4SLinus Torvalds         val.hr_tens = hour / 10;
1281da177e4SLinus Torvalds         val.day_ones = t->tm_mday % 10;
1291da177e4SLinus Torvalds         val.day_tens = t->tm_mday / 10;
1301da177e4SLinus Torvalds         val.mon_ones = (t->tm_mon+1) % 10;
1311da177e4SLinus Torvalds         val.mon_tens = (t->tm_mon+1) / 10;
1321da177e4SLinus Torvalds         year = t->tm_year - 80;
1331da177e4SLinus Torvalds         val.year_ones = year % 10;
1341da177e4SLinus Torvalds         val.year_tens = year / 10;
1351da177e4SLinus Torvalds         val.weekday = t->tm_wday;
1361da177e4SLinus Torvalds         mste_write(&val);
1371da177e4SLinus Torvalds         mste_rtc.mode=(mste_rtc.mode | 1);
1381da177e4SLinus Torvalds         val.year_ones = (year % 4);	/* leap year register */
1391da177e4SLinus Torvalds         mste_rtc.mode=(mste_rtc.mode & ~1);
1401da177e4SLinus Torvalds     }
1411da177e4SLinus Torvalds     else {
1421da177e4SLinus Torvalds         mste_read(&val);
1431da177e4SLinus Torvalds         t->tm_sec = val.sec_ones + val.sec_tens * 10;
1441da177e4SLinus Torvalds         t->tm_min = val.min_ones + val.min_tens * 10;
1451da177e4SLinus Torvalds         hour = val.hr_ones + val.hr_tens * 10;
1461da177e4SLinus Torvalds 	if (!hr24) {
1471da177e4SLinus Torvalds 	    if (hour == 12 || hour == 12 + 20)
1481da177e4SLinus Torvalds 		hour -= 12;
1491da177e4SLinus Torvalds 	    if (hour >= 20)
1501da177e4SLinus Torvalds                 hour += 12 - 20;
1511da177e4SLinus Torvalds         }
1521da177e4SLinus Torvalds 	t->tm_hour = hour;
1531da177e4SLinus Torvalds 	t->tm_mday = val.day_ones + val.day_tens * 10;
1541da177e4SLinus Torvalds         t->tm_mon  = val.mon_ones + val.mon_tens * 10 - 1;
1551da177e4SLinus Torvalds         t->tm_year = val.year_ones + val.year_tens * 10 + 80;
1561da177e4SLinus Torvalds         t->tm_wday = val.weekday;
1571da177e4SLinus Torvalds     }
1581da177e4SLinus Torvalds     return 0;
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds int atari_tt_hwclk( int op, struct rtc_time *t )
1621da177e4SLinus Torvalds {
1631da177e4SLinus Torvalds     int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0;
1641da177e4SLinus Torvalds     unsigned long	flags;
1651da177e4SLinus Torvalds     unsigned char	ctrl;
1661da177e4SLinus Torvalds     int pm = 0;
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds     ctrl = RTC_READ(RTC_CONTROL); /* control registers are
1691da177e4SLinus Torvalds                                    * independent from the UIP */
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds     if (op) {
1721da177e4SLinus Torvalds         /* write: prepare values */
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds         sec  = t->tm_sec;
1751da177e4SLinus Torvalds         min  = t->tm_min;
1761da177e4SLinus Torvalds         hour = t->tm_hour;
1771da177e4SLinus Torvalds         day  = t->tm_mday;
1781da177e4SLinus Torvalds         mon  = t->tm_mon + 1;
1791da177e4SLinus Torvalds         year = t->tm_year - atari_rtc_year_offset;
1801da177e4SLinus Torvalds         wday = t->tm_wday + (t->tm_wday >= 0);
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds         if (!(ctrl & RTC_24H)) {
1831da177e4SLinus Torvalds 	    if (hour > 11) {
1841da177e4SLinus Torvalds 		pm = 0x80;
1851da177e4SLinus Torvalds 		if (hour != 12)
1861da177e4SLinus Torvalds 		    hour -= 12;
1871da177e4SLinus Torvalds 	    }
1881da177e4SLinus Torvalds 	    else if (hour == 0)
1891da177e4SLinus Torvalds 		hour = 12;
1901da177e4SLinus Torvalds         }
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds         if (!(ctrl & RTC_DM_BINARY)) {
1931da177e4SLinus Torvalds             BIN_TO_BCD(sec);
1941da177e4SLinus Torvalds             BIN_TO_BCD(min);
1951da177e4SLinus Torvalds             BIN_TO_BCD(hour);
1961da177e4SLinus Torvalds             BIN_TO_BCD(day);
1971da177e4SLinus Torvalds             BIN_TO_BCD(mon);
1981da177e4SLinus Torvalds             BIN_TO_BCD(year);
1991da177e4SLinus Torvalds             if (wday >= 0) BIN_TO_BCD(wday);
2001da177e4SLinus Torvalds         }
2011da177e4SLinus Torvalds     }
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds     /* Reading/writing the clock registers is a bit critical due to
2041da177e4SLinus Torvalds      * the regular update cycle of the RTC. While an update is in
2051da177e4SLinus Torvalds      * progress, registers 0..9 shouldn't be touched.
2061da177e4SLinus Torvalds      * The problem is solved like that: If an update is currently in
2071da177e4SLinus Torvalds      * progress (the UIP bit is set), the process sleeps for a while
2081da177e4SLinus Torvalds      * (50ms). This really should be enough, since the update cycle
2091da177e4SLinus Torvalds      * normally needs 2 ms.
2101da177e4SLinus Torvalds      * If the UIP bit reads as 0, we have at least 244 usecs until the
2111da177e4SLinus Torvalds      * update starts. This should be enough... But to be sure,
2121da177e4SLinus Torvalds      * additionally the RTC_SET bit is set to prevent an update cycle.
2131da177e4SLinus Torvalds      */
2141da177e4SLinus Torvalds 
21528faa429SNishanth Aravamudan     while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP )
21628faa429SNishanth Aravamudan         schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds     local_irq_save(flags);
2191da177e4SLinus Torvalds     RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
2201da177e4SLinus Torvalds     if (!op) {
2211da177e4SLinus Torvalds         sec  = RTC_READ( RTC_SECONDS );
2221da177e4SLinus Torvalds         min  = RTC_READ( RTC_MINUTES );
2231da177e4SLinus Torvalds         hour = RTC_READ( RTC_HOURS );
2241da177e4SLinus Torvalds         day  = RTC_READ( RTC_DAY_OF_MONTH );
2251da177e4SLinus Torvalds         mon  = RTC_READ( RTC_MONTH );
2261da177e4SLinus Torvalds         year = RTC_READ( RTC_YEAR );
2271da177e4SLinus Torvalds         wday = RTC_READ( RTC_DAY_OF_WEEK );
2281da177e4SLinus Torvalds     }
2291da177e4SLinus Torvalds     else {
2301da177e4SLinus Torvalds         RTC_WRITE( RTC_SECONDS, sec );
2311da177e4SLinus Torvalds         RTC_WRITE( RTC_MINUTES, min );
2321da177e4SLinus Torvalds         RTC_WRITE( RTC_HOURS, hour + pm);
2331da177e4SLinus Torvalds         RTC_WRITE( RTC_DAY_OF_MONTH, day );
2341da177e4SLinus Torvalds         RTC_WRITE( RTC_MONTH, mon );
2351da177e4SLinus Torvalds         RTC_WRITE( RTC_YEAR, year );
2361da177e4SLinus Torvalds         if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
2371da177e4SLinus Torvalds     }
2381da177e4SLinus Torvalds     RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
2391da177e4SLinus Torvalds     local_irq_restore(flags);
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds     if (!op) {
2421da177e4SLinus Torvalds         /* read: adjust values */
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds         if (hour & 0x80) {
2451da177e4SLinus Torvalds 	    hour &= ~0x80;
2461da177e4SLinus Torvalds 	    pm = 1;
2471da177e4SLinus Torvalds 	}
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	if (!(ctrl & RTC_DM_BINARY)) {
2501da177e4SLinus Torvalds             BCD_TO_BIN(sec);
2511da177e4SLinus Torvalds             BCD_TO_BIN(min);
2521da177e4SLinus Torvalds             BCD_TO_BIN(hour);
2531da177e4SLinus Torvalds             BCD_TO_BIN(day);
2541da177e4SLinus Torvalds             BCD_TO_BIN(mon);
2551da177e4SLinus Torvalds             BCD_TO_BIN(year);
2561da177e4SLinus Torvalds             BCD_TO_BIN(wday);
2571da177e4SLinus Torvalds         }
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds         if (!(ctrl & RTC_24H)) {
2601da177e4SLinus Torvalds 	    if (!pm && hour == 12)
2611da177e4SLinus Torvalds 		hour = 0;
2621da177e4SLinus Torvalds 	    else if (pm && hour != 12)
2631da177e4SLinus Torvalds 		hour += 12;
2641da177e4SLinus Torvalds         }
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds         t->tm_sec  = sec;
2671da177e4SLinus Torvalds         t->tm_min  = min;
2681da177e4SLinus Torvalds         t->tm_hour = hour;
2691da177e4SLinus Torvalds         t->tm_mday = day;
2701da177e4SLinus Torvalds         t->tm_mon  = mon - 1;
2711da177e4SLinus Torvalds         t->tm_year = year + atari_rtc_year_offset;
2721da177e4SLinus Torvalds         t->tm_wday = wday - 1;
2731da177e4SLinus Torvalds     }
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds     return( 0 );
2761da177e4SLinus Torvalds }
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds int atari_mste_set_clock_mmss (unsigned long nowtime)
2801da177e4SLinus Torvalds {
2811da177e4SLinus Torvalds     short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
2821da177e4SLinus Torvalds     struct MSTE_RTC val;
2831da177e4SLinus Torvalds     unsigned char rtc_minutes;
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds     mste_read(&val);
2861da177e4SLinus Torvalds     rtc_minutes= val.min_ones + val.min_tens * 10;
2871da177e4SLinus Torvalds     if ((rtc_minutes < real_minutes
2881da177e4SLinus Torvalds          ? real_minutes - rtc_minutes
2891da177e4SLinus Torvalds          : rtc_minutes - real_minutes) < 30)
2901da177e4SLinus Torvalds     {
2911da177e4SLinus Torvalds         val.sec_ones = real_seconds % 10;
2921da177e4SLinus Torvalds         val.sec_tens = real_seconds / 10;
2931da177e4SLinus Torvalds         val.min_ones = real_minutes % 10;
2941da177e4SLinus Torvalds         val.min_tens = real_minutes / 10;
2951da177e4SLinus Torvalds         mste_write(&val);
2961da177e4SLinus Torvalds     }
2971da177e4SLinus Torvalds     else
2981da177e4SLinus Torvalds         return -1;
2991da177e4SLinus Torvalds     return 0;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds int atari_tt_set_clock_mmss (unsigned long nowtime)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds     int retval = 0;
3051da177e4SLinus Torvalds     short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
3061da177e4SLinus Torvalds     unsigned char save_control, save_freq_select, rtc_minutes;
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds     save_control = RTC_READ (RTC_CONTROL); /* tell the clock it's being set */
3091da177e4SLinus Torvalds     RTC_WRITE (RTC_CONTROL, save_control | RTC_SET);
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds     save_freq_select = RTC_READ (RTC_FREQ_SELECT); /* stop and reset prescaler */
3121da177e4SLinus Torvalds     RTC_WRITE (RTC_FREQ_SELECT, save_freq_select | RTC_DIV_RESET2);
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds     rtc_minutes = RTC_READ (RTC_MINUTES);
3151da177e4SLinus Torvalds     if (!(save_control & RTC_DM_BINARY))
3161da177e4SLinus Torvalds         BCD_TO_BIN (rtc_minutes);
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds     /* Since we're only adjusting minutes and seconds, don't interfere
3191da177e4SLinus Torvalds        with hour overflow.  This avoids messing with unknown time zones
3201da177e4SLinus Torvalds        but requires your RTC not to be off by more than 30 minutes.  */
3211da177e4SLinus Torvalds     if ((rtc_minutes < real_minutes
3221da177e4SLinus Torvalds          ? real_minutes - rtc_minutes
3231da177e4SLinus Torvalds          : rtc_minutes - real_minutes) < 30)
3241da177e4SLinus Torvalds         {
3251da177e4SLinus Torvalds             if (!(save_control & RTC_DM_BINARY))
3261da177e4SLinus Torvalds                 {
3271da177e4SLinus Torvalds                     BIN_TO_BCD (real_seconds);
3281da177e4SLinus Torvalds                     BIN_TO_BCD (real_minutes);
3291da177e4SLinus Torvalds                 }
3301da177e4SLinus Torvalds             RTC_WRITE (RTC_SECONDS, real_seconds);
3311da177e4SLinus Torvalds             RTC_WRITE (RTC_MINUTES, real_minutes);
3321da177e4SLinus Torvalds         }
3331da177e4SLinus Torvalds     else
3341da177e4SLinus Torvalds         retval = -1;
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds     RTC_WRITE (RTC_FREQ_SELECT, save_freq_select);
3371da177e4SLinus Torvalds     RTC_WRITE (RTC_CONTROL, save_control);
3381da177e4SLinus Torvalds     return retval;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds /*
3421da177e4SLinus Torvalds  * Local variables:
3431da177e4SLinus Torvalds  *  c-indent-level: 4
3441da177e4SLinus Torvalds  *  tab-width: 8
3451da177e4SLinus Torvalds  * End:
3461da177e4SLinus Torvalds  */
347