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