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