18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/arch/m68k/atari/time.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Atari time and real time clock stuff 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 98c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 108c2ecf20Sopenharmony_ci * for more details. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/mc146818rtc.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/rtc.h> 188c2ecf20Sopenharmony_ci#include <linux/bcd.h> 198c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/export.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <asm/atariints.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(rtc_lock); 268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtc_lock); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic u64 atari_read_clk(struct clocksource *cs); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic struct clocksource atari_clk = { 318c2ecf20Sopenharmony_ci .name = "mfp", 328c2ecf20Sopenharmony_ci .rating = 100, 338c2ecf20Sopenharmony_ci .read = atari_read_clk, 348c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 358c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic u32 clk_total; 398c2ecf20Sopenharmony_cistatic u8 last_timer_count; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic irqreturn_t mfp_timer_c_handler(int irq, void *dev_id) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci irq_handler_t timer_routine = dev_id; 448c2ecf20Sopenharmony_ci unsigned long flags; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci local_irq_save(flags); 478c2ecf20Sopenharmony_ci do { 488c2ecf20Sopenharmony_ci last_timer_count = st_mfp.tim_dt_c; 498c2ecf20Sopenharmony_ci } while (last_timer_count == 1); 508c2ecf20Sopenharmony_ci clk_total += INT_TICKS; 518c2ecf20Sopenharmony_ci timer_routine(0, NULL); 528c2ecf20Sopenharmony_ci local_irq_restore(flags); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return IRQ_HANDLED; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_civoid __init 588c2ecf20Sopenharmony_ciatari_sched_init(irq_handler_t timer_routine) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci /* set Timer C data Register */ 618c2ecf20Sopenharmony_ci st_mfp.tim_dt_c = INT_TICKS; 628c2ecf20Sopenharmony_ci /* start timer C, div = 1:100 */ 638c2ecf20Sopenharmony_ci st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60; 648c2ecf20Sopenharmony_ci /* install interrupt service routine for MFP Timer C */ 658c2ecf20Sopenharmony_ci if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, IRQF_TIMER, "timer", 668c2ecf20Sopenharmony_ci timer_routine)) 678c2ecf20Sopenharmony_ci pr_err("Couldn't register timer interrupt\n"); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci clocksource_register_hz(&atari_clk, INT_CLK); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* ++andreas: gettimeoffset fixed to check for pending interrupt */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic u64 atari_read_clk(struct clocksource *cs) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned long flags; 778c2ecf20Sopenharmony_ci u8 count; 788c2ecf20Sopenharmony_ci u32 ticks; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci local_irq_save(flags); 818c2ecf20Sopenharmony_ci /* Ensure that the count is monotonically decreasing, even though 828c2ecf20Sopenharmony_ci * the result may briefly stop changing after counter wrap-around. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci count = min(st_mfp.tim_dt_c, last_timer_count); 858c2ecf20Sopenharmony_ci last_timer_count = count; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci ticks = INT_TICKS - count; 888c2ecf20Sopenharmony_ci ticks += clk_total; 898c2ecf20Sopenharmony_ci local_irq_restore(flags); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return ticks; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void mste_read(struct MSTE_RTC *val) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci#define COPY(v) val->v=(mste_rtc.v & 0xf) 988c2ecf20Sopenharmony_ci do { 998c2ecf20Sopenharmony_ci COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ; 1008c2ecf20Sopenharmony_ci COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ; 1018c2ecf20Sopenharmony_ci COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ; 1028c2ecf20Sopenharmony_ci COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ; 1038c2ecf20Sopenharmony_ci COPY(year_tens) ; 1048c2ecf20Sopenharmony_ci /* prevent from reading the clock while it changed */ 1058c2ecf20Sopenharmony_ci } while (val->sec_ones != (mste_rtc.sec_ones & 0xf)); 1068c2ecf20Sopenharmony_ci#undef COPY 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void mste_write(struct MSTE_RTC *val) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci#define COPY(v) mste_rtc.v=val->v 1128c2ecf20Sopenharmony_ci do { 1138c2ecf20Sopenharmony_ci COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ; 1148c2ecf20Sopenharmony_ci COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ; 1158c2ecf20Sopenharmony_ci COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ; 1168c2ecf20Sopenharmony_ci COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ; 1178c2ecf20Sopenharmony_ci COPY(year_tens) ; 1188c2ecf20Sopenharmony_ci /* prevent from writing the clock while it changed */ 1198c2ecf20Sopenharmony_ci } while (val->sec_ones != (mste_rtc.sec_ones & 0xf)); 1208c2ecf20Sopenharmony_ci#undef COPY 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define RTC_READ(reg) \ 1248c2ecf20Sopenharmony_ci ({ unsigned char __val; \ 1258c2ecf20Sopenharmony_ci (void) atari_writeb(reg,&tt_rtc.regsel); \ 1268c2ecf20Sopenharmony_ci __val = tt_rtc.data; \ 1278c2ecf20Sopenharmony_ci __val; \ 1288c2ecf20Sopenharmony_ci }) 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#define RTC_WRITE(reg,val) \ 1318c2ecf20Sopenharmony_ci do { \ 1328c2ecf20Sopenharmony_ci atari_writeb(reg,&tt_rtc.regsel); \ 1338c2ecf20Sopenharmony_ci tt_rtc.data = (val); \ 1348c2ecf20Sopenharmony_ci } while(0) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define HWCLK_POLL_INTERVAL 5 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciint atari_mste_hwclk( int op, struct rtc_time *t ) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci int hour, year; 1428c2ecf20Sopenharmony_ci int hr24=0; 1438c2ecf20Sopenharmony_ci struct MSTE_RTC val; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci mste_rtc.mode=(mste_rtc.mode | 1); 1468c2ecf20Sopenharmony_ci hr24=mste_rtc.mon_tens & 1; 1478c2ecf20Sopenharmony_ci mste_rtc.mode=(mste_rtc.mode & ~1); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (op) { 1508c2ecf20Sopenharmony_ci /* write: prepare values */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci val.sec_ones = t->tm_sec % 10; 1538c2ecf20Sopenharmony_ci val.sec_tens = t->tm_sec / 10; 1548c2ecf20Sopenharmony_ci val.min_ones = t->tm_min % 10; 1558c2ecf20Sopenharmony_ci val.min_tens = t->tm_min / 10; 1568c2ecf20Sopenharmony_ci hour = t->tm_hour; 1578c2ecf20Sopenharmony_ci if (!hr24) { 1588c2ecf20Sopenharmony_ci if (hour > 11) 1598c2ecf20Sopenharmony_ci hour += 20 - 12; 1608c2ecf20Sopenharmony_ci if (hour == 0 || hour == 20) 1618c2ecf20Sopenharmony_ci hour += 12; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci val.hr_ones = hour % 10; 1648c2ecf20Sopenharmony_ci val.hr_tens = hour / 10; 1658c2ecf20Sopenharmony_ci val.day_ones = t->tm_mday % 10; 1668c2ecf20Sopenharmony_ci val.day_tens = t->tm_mday / 10; 1678c2ecf20Sopenharmony_ci val.mon_ones = (t->tm_mon+1) % 10; 1688c2ecf20Sopenharmony_ci val.mon_tens = (t->tm_mon+1) / 10; 1698c2ecf20Sopenharmony_ci year = t->tm_year - 80; 1708c2ecf20Sopenharmony_ci val.year_ones = year % 10; 1718c2ecf20Sopenharmony_ci val.year_tens = year / 10; 1728c2ecf20Sopenharmony_ci val.weekday = t->tm_wday; 1738c2ecf20Sopenharmony_ci mste_write(&val); 1748c2ecf20Sopenharmony_ci mste_rtc.mode=(mste_rtc.mode | 1); 1758c2ecf20Sopenharmony_ci val.year_ones = (year % 4); /* leap year register */ 1768c2ecf20Sopenharmony_ci mste_rtc.mode=(mste_rtc.mode & ~1); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci else { 1798c2ecf20Sopenharmony_ci mste_read(&val); 1808c2ecf20Sopenharmony_ci t->tm_sec = val.sec_ones + val.sec_tens * 10; 1818c2ecf20Sopenharmony_ci t->tm_min = val.min_ones + val.min_tens * 10; 1828c2ecf20Sopenharmony_ci hour = val.hr_ones + val.hr_tens * 10; 1838c2ecf20Sopenharmony_ci if (!hr24) { 1848c2ecf20Sopenharmony_ci if (hour == 12 || hour == 12 + 20) 1858c2ecf20Sopenharmony_ci hour -= 12; 1868c2ecf20Sopenharmony_ci if (hour >= 20) 1878c2ecf20Sopenharmony_ci hour += 12 - 20; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci t->tm_hour = hour; 1908c2ecf20Sopenharmony_ci t->tm_mday = val.day_ones + val.day_tens * 10; 1918c2ecf20Sopenharmony_ci t->tm_mon = val.mon_ones + val.mon_tens * 10 - 1; 1928c2ecf20Sopenharmony_ci t->tm_year = val.year_ones + val.year_tens * 10 + 80; 1938c2ecf20Sopenharmony_ci t->tm_wday = val.weekday; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ciint atari_tt_hwclk( int op, struct rtc_time *t ) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0; 2018c2ecf20Sopenharmony_ci unsigned long flags; 2028c2ecf20Sopenharmony_ci unsigned char ctrl; 2038c2ecf20Sopenharmony_ci int pm = 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ctrl = RTC_READ(RTC_CONTROL); /* control registers are 2068c2ecf20Sopenharmony_ci * independent from the UIP */ 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (op) { 2098c2ecf20Sopenharmony_ci /* write: prepare values */ 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci sec = t->tm_sec; 2128c2ecf20Sopenharmony_ci min = t->tm_min; 2138c2ecf20Sopenharmony_ci hour = t->tm_hour; 2148c2ecf20Sopenharmony_ci day = t->tm_mday; 2158c2ecf20Sopenharmony_ci mon = t->tm_mon + 1; 2168c2ecf20Sopenharmony_ci year = t->tm_year - atari_rtc_year_offset; 2178c2ecf20Sopenharmony_ci wday = t->tm_wday + (t->tm_wday >= 0); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (!(ctrl & RTC_24H)) { 2208c2ecf20Sopenharmony_ci if (hour > 11) { 2218c2ecf20Sopenharmony_ci pm = 0x80; 2228c2ecf20Sopenharmony_ci if (hour != 12) 2238c2ecf20Sopenharmony_ci hour -= 12; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci else if (hour == 0) 2268c2ecf20Sopenharmony_ci hour = 12; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (!(ctrl & RTC_DM_BINARY)) { 2308c2ecf20Sopenharmony_ci sec = bin2bcd(sec); 2318c2ecf20Sopenharmony_ci min = bin2bcd(min); 2328c2ecf20Sopenharmony_ci hour = bin2bcd(hour); 2338c2ecf20Sopenharmony_ci day = bin2bcd(day); 2348c2ecf20Sopenharmony_ci mon = bin2bcd(mon); 2358c2ecf20Sopenharmony_ci year = bin2bcd(year); 2368c2ecf20Sopenharmony_ci if (wday >= 0) 2378c2ecf20Sopenharmony_ci wday = bin2bcd(wday); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Reading/writing the clock registers is a bit critical due to 2428c2ecf20Sopenharmony_ci * the regular update cycle of the RTC. While an update is in 2438c2ecf20Sopenharmony_ci * progress, registers 0..9 shouldn't be touched. 2448c2ecf20Sopenharmony_ci * The problem is solved like that: If an update is currently in 2458c2ecf20Sopenharmony_ci * progress (the UIP bit is set), the process sleeps for a while 2468c2ecf20Sopenharmony_ci * (50ms). This really should be enough, since the update cycle 2478c2ecf20Sopenharmony_ci * normally needs 2 ms. 2488c2ecf20Sopenharmony_ci * If the UIP bit reads as 0, we have at least 244 usecs until the 2498c2ecf20Sopenharmony_ci * update starts. This should be enough... But to be sure, 2508c2ecf20Sopenharmony_ci * additionally the RTC_SET bit is set to prevent an update cycle. 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) { 2548c2ecf20Sopenharmony_ci if (in_atomic() || irqs_disabled()) 2558c2ecf20Sopenharmony_ci mdelay(1); 2568c2ecf20Sopenharmony_ci else 2578c2ecf20Sopenharmony_ci schedule_timeout_interruptible(HWCLK_POLL_INTERVAL); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci local_irq_save(flags); 2618c2ecf20Sopenharmony_ci RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET ); 2628c2ecf20Sopenharmony_ci if (!op) { 2638c2ecf20Sopenharmony_ci sec = RTC_READ( RTC_SECONDS ); 2648c2ecf20Sopenharmony_ci min = RTC_READ( RTC_MINUTES ); 2658c2ecf20Sopenharmony_ci hour = RTC_READ( RTC_HOURS ); 2668c2ecf20Sopenharmony_ci day = RTC_READ( RTC_DAY_OF_MONTH ); 2678c2ecf20Sopenharmony_ci mon = RTC_READ( RTC_MONTH ); 2688c2ecf20Sopenharmony_ci year = RTC_READ( RTC_YEAR ); 2698c2ecf20Sopenharmony_ci wday = RTC_READ( RTC_DAY_OF_WEEK ); 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci else { 2728c2ecf20Sopenharmony_ci RTC_WRITE( RTC_SECONDS, sec ); 2738c2ecf20Sopenharmony_ci RTC_WRITE( RTC_MINUTES, min ); 2748c2ecf20Sopenharmony_ci RTC_WRITE( RTC_HOURS, hour + pm); 2758c2ecf20Sopenharmony_ci RTC_WRITE( RTC_DAY_OF_MONTH, day ); 2768c2ecf20Sopenharmony_ci RTC_WRITE( RTC_MONTH, mon ); 2778c2ecf20Sopenharmony_ci RTC_WRITE( RTC_YEAR, year ); 2788c2ecf20Sopenharmony_ci if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday ); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET ); 2818c2ecf20Sopenharmony_ci local_irq_restore(flags); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (!op) { 2848c2ecf20Sopenharmony_ci /* read: adjust values */ 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (hour & 0x80) { 2878c2ecf20Sopenharmony_ci hour &= ~0x80; 2888c2ecf20Sopenharmony_ci pm = 1; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (!(ctrl & RTC_DM_BINARY)) { 2928c2ecf20Sopenharmony_ci sec = bcd2bin(sec); 2938c2ecf20Sopenharmony_ci min = bcd2bin(min); 2948c2ecf20Sopenharmony_ci hour = bcd2bin(hour); 2958c2ecf20Sopenharmony_ci day = bcd2bin(day); 2968c2ecf20Sopenharmony_ci mon = bcd2bin(mon); 2978c2ecf20Sopenharmony_ci year = bcd2bin(year); 2988c2ecf20Sopenharmony_ci wday = bcd2bin(wday); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (!(ctrl & RTC_24H)) { 3028c2ecf20Sopenharmony_ci if (!pm && hour == 12) 3038c2ecf20Sopenharmony_ci hour = 0; 3048c2ecf20Sopenharmony_ci else if (pm && hour != 12) 3058c2ecf20Sopenharmony_ci hour += 12; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci t->tm_sec = sec; 3098c2ecf20Sopenharmony_ci t->tm_min = min; 3108c2ecf20Sopenharmony_ci t->tm_hour = hour; 3118c2ecf20Sopenharmony_ci t->tm_mday = day; 3128c2ecf20Sopenharmony_ci t->tm_mon = mon - 1; 3138c2ecf20Sopenharmony_ci t->tm_year = year + atari_rtc_year_offset; 3148c2ecf20Sopenharmony_ci t->tm_wday = wday - 1; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return( 0 ); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/* 3218c2ecf20Sopenharmony_ci * Local variables: 3228c2ecf20Sopenharmony_ci * c-indent-level: 4 3238c2ecf20Sopenharmony_ci * tab-width: 8 3248c2ecf20Sopenharmony_ci * End: 3258c2ecf20Sopenharmony_ci */ 326