162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/arch/m68k/atari/time.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Atari time and real time clock stuff 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 962306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 1062306a36Sopenharmony_ci * for more details. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/mc146818rtc.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/rtc.h> 1862306a36Sopenharmony_ci#include <linux/bcd.h> 1962306a36Sopenharmony_ci#include <linux/clocksource.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/export.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/atariints.h> 2462306a36Sopenharmony_ci#include <asm/machdep.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciDEFINE_SPINLOCK(rtc_lock); 2762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtc_lock); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic u64 atari_read_clk(struct clocksource *cs); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct clocksource atari_clk = { 3262306a36Sopenharmony_ci .name = "mfp", 3362306a36Sopenharmony_ci .rating = 100, 3462306a36Sopenharmony_ci .read = atari_read_clk, 3562306a36Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 3662306a36Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic u32 clk_total; 4062306a36Sopenharmony_cistatic u8 last_timer_count; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic irqreturn_t mfp_timer_c_handler(int irq, void *dev_id) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci unsigned long flags; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci local_irq_save(flags); 4762306a36Sopenharmony_ci do { 4862306a36Sopenharmony_ci last_timer_count = st_mfp.tim_dt_c; 4962306a36Sopenharmony_ci } while (last_timer_count == 1); 5062306a36Sopenharmony_ci clk_total += INT_TICKS; 5162306a36Sopenharmony_ci legacy_timer_tick(1); 5262306a36Sopenharmony_ci timer_heartbeat(); 5362306a36Sopenharmony_ci local_irq_restore(flags); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return IRQ_HANDLED; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_civoid __init 5962306a36Sopenharmony_ciatari_sched_init(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci /* set Timer C data Register */ 6262306a36Sopenharmony_ci st_mfp.tim_dt_c = INT_TICKS; 6362306a36Sopenharmony_ci /* start timer C, div = 1:100 */ 6462306a36Sopenharmony_ci st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60; 6562306a36Sopenharmony_ci /* install interrupt service routine for MFP Timer C */ 6662306a36Sopenharmony_ci if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, IRQF_TIMER, "timer", 6762306a36Sopenharmony_ci NULL)) 6862306a36Sopenharmony_ci pr_err("Couldn't register timer interrupt\n"); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci clocksource_register_hz(&atari_clk, INT_CLK); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* ++andreas: gettimeoffset fixed to check for pending interrupt */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic u64 atari_read_clk(struct clocksource *cs) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci unsigned long flags; 7862306a36Sopenharmony_ci u8 count; 7962306a36Sopenharmony_ci u32 ticks; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci local_irq_save(flags); 8262306a36Sopenharmony_ci /* Ensure that the count is monotonically decreasing, even though 8362306a36Sopenharmony_ci * the result may briefly stop changing after counter wrap-around. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci count = min(st_mfp.tim_dt_c, last_timer_count); 8662306a36Sopenharmony_ci last_timer_count = count; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ticks = INT_TICKS - count; 8962306a36Sopenharmony_ci ticks += clk_total; 9062306a36Sopenharmony_ci local_irq_restore(flags); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return ticks; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void mste_read(struct MSTE_RTC *val) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci#define COPY(v) val->v=(mste_rtc.v & 0xf) 9962306a36Sopenharmony_ci do { 10062306a36Sopenharmony_ci COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ; 10162306a36Sopenharmony_ci COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ; 10262306a36Sopenharmony_ci COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ; 10362306a36Sopenharmony_ci COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ; 10462306a36Sopenharmony_ci COPY(year_tens) ; 10562306a36Sopenharmony_ci /* prevent from reading the clock while it changed */ 10662306a36Sopenharmony_ci } while (val->sec_ones != (mste_rtc.sec_ones & 0xf)); 10762306a36Sopenharmony_ci#undef COPY 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void mste_write(struct MSTE_RTC *val) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci#define COPY(v) mste_rtc.v=val->v 11362306a36Sopenharmony_ci do { 11462306a36Sopenharmony_ci COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ; 11562306a36Sopenharmony_ci COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ; 11662306a36Sopenharmony_ci COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ; 11762306a36Sopenharmony_ci COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ; 11862306a36Sopenharmony_ci COPY(year_tens) ; 11962306a36Sopenharmony_ci /* prevent from writing the clock while it changed */ 12062306a36Sopenharmony_ci } while (val->sec_ones != (mste_rtc.sec_ones & 0xf)); 12162306a36Sopenharmony_ci#undef COPY 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define RTC_READ(reg) \ 12562306a36Sopenharmony_ci ({ unsigned char __val; \ 12662306a36Sopenharmony_ci (void) atari_writeb(reg,&tt_rtc.regsel); \ 12762306a36Sopenharmony_ci __val = tt_rtc.data; \ 12862306a36Sopenharmony_ci __val; \ 12962306a36Sopenharmony_ci }) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define RTC_WRITE(reg,val) \ 13262306a36Sopenharmony_ci do { \ 13362306a36Sopenharmony_ci atari_writeb(reg,&tt_rtc.regsel); \ 13462306a36Sopenharmony_ci tt_rtc.data = (val); \ 13562306a36Sopenharmony_ci } while(0) 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define HWCLK_POLL_INTERVAL 5 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciint atari_mste_hwclk( int op, struct rtc_time *t ) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci int hour, year; 14362306a36Sopenharmony_ci int hr24=0; 14462306a36Sopenharmony_ci struct MSTE_RTC val; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci mste_rtc.mode=(mste_rtc.mode | 1); 14762306a36Sopenharmony_ci hr24=mste_rtc.mon_tens & 1; 14862306a36Sopenharmony_ci mste_rtc.mode=(mste_rtc.mode & ~1); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (op) { 15162306a36Sopenharmony_ci /* write: prepare values */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci val.sec_ones = t->tm_sec % 10; 15462306a36Sopenharmony_ci val.sec_tens = t->tm_sec / 10; 15562306a36Sopenharmony_ci val.min_ones = t->tm_min % 10; 15662306a36Sopenharmony_ci val.min_tens = t->tm_min / 10; 15762306a36Sopenharmony_ci hour = t->tm_hour; 15862306a36Sopenharmony_ci if (!hr24) { 15962306a36Sopenharmony_ci if (hour > 11) 16062306a36Sopenharmony_ci hour += 20 - 12; 16162306a36Sopenharmony_ci if (hour == 0 || hour == 20) 16262306a36Sopenharmony_ci hour += 12; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci val.hr_ones = hour % 10; 16562306a36Sopenharmony_ci val.hr_tens = hour / 10; 16662306a36Sopenharmony_ci val.day_ones = t->tm_mday % 10; 16762306a36Sopenharmony_ci val.day_tens = t->tm_mday / 10; 16862306a36Sopenharmony_ci val.mon_ones = (t->tm_mon+1) % 10; 16962306a36Sopenharmony_ci val.mon_tens = (t->tm_mon+1) / 10; 17062306a36Sopenharmony_ci year = t->tm_year - 80; 17162306a36Sopenharmony_ci val.year_ones = year % 10; 17262306a36Sopenharmony_ci val.year_tens = year / 10; 17362306a36Sopenharmony_ci val.weekday = t->tm_wday; 17462306a36Sopenharmony_ci mste_write(&val); 17562306a36Sopenharmony_ci mste_rtc.mode=(mste_rtc.mode | 1); 17662306a36Sopenharmony_ci val.year_ones = (year % 4); /* leap year register */ 17762306a36Sopenharmony_ci mste_rtc.mode=(mste_rtc.mode & ~1); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci else { 18062306a36Sopenharmony_ci mste_read(&val); 18162306a36Sopenharmony_ci t->tm_sec = val.sec_ones + val.sec_tens * 10; 18262306a36Sopenharmony_ci t->tm_min = val.min_ones + val.min_tens * 10; 18362306a36Sopenharmony_ci hour = val.hr_ones + val.hr_tens * 10; 18462306a36Sopenharmony_ci if (!hr24) { 18562306a36Sopenharmony_ci if (hour == 12 || hour == 12 + 20) 18662306a36Sopenharmony_ci hour -= 12; 18762306a36Sopenharmony_ci if (hour >= 20) 18862306a36Sopenharmony_ci hour += 12 - 20; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci t->tm_hour = hour; 19162306a36Sopenharmony_ci t->tm_mday = val.day_ones + val.day_tens * 10; 19262306a36Sopenharmony_ci t->tm_mon = val.mon_ones + val.mon_tens * 10 - 1; 19362306a36Sopenharmony_ci t->tm_year = val.year_ones + val.year_tens * 10 + 80; 19462306a36Sopenharmony_ci t->tm_wday = val.weekday; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint atari_tt_hwclk( int op, struct rtc_time *t ) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0; 20262306a36Sopenharmony_ci unsigned long flags; 20362306a36Sopenharmony_ci unsigned char ctrl; 20462306a36Sopenharmony_ci int pm = 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ctrl = RTC_READ(RTC_CONTROL); /* control registers are 20762306a36Sopenharmony_ci * independent from the UIP */ 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (op) { 21062306a36Sopenharmony_ci /* write: prepare values */ 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci sec = t->tm_sec; 21362306a36Sopenharmony_ci min = t->tm_min; 21462306a36Sopenharmony_ci hour = t->tm_hour; 21562306a36Sopenharmony_ci day = t->tm_mday; 21662306a36Sopenharmony_ci mon = t->tm_mon + 1; 21762306a36Sopenharmony_ci year = t->tm_year - atari_rtc_year_offset; 21862306a36Sopenharmony_ci wday = t->tm_wday + (t->tm_wday >= 0); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!(ctrl & RTC_24H)) { 22162306a36Sopenharmony_ci if (hour > 11) { 22262306a36Sopenharmony_ci pm = 0x80; 22362306a36Sopenharmony_ci if (hour != 12) 22462306a36Sopenharmony_ci hour -= 12; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci else if (hour == 0) 22762306a36Sopenharmony_ci hour = 12; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (!(ctrl & RTC_DM_BINARY)) { 23162306a36Sopenharmony_ci sec = bin2bcd(sec); 23262306a36Sopenharmony_ci min = bin2bcd(min); 23362306a36Sopenharmony_ci hour = bin2bcd(hour); 23462306a36Sopenharmony_ci day = bin2bcd(day); 23562306a36Sopenharmony_ci mon = bin2bcd(mon); 23662306a36Sopenharmony_ci year = bin2bcd(year); 23762306a36Sopenharmony_ci if (wday >= 0) 23862306a36Sopenharmony_ci wday = bin2bcd(wday); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Reading/writing the clock registers is a bit critical due to 24362306a36Sopenharmony_ci * the regular update cycle of the RTC. While an update is in 24462306a36Sopenharmony_ci * progress, registers 0..9 shouldn't be touched. 24562306a36Sopenharmony_ci * The problem is solved like that: If an update is currently in 24662306a36Sopenharmony_ci * progress (the UIP bit is set), the process sleeps for a while 24762306a36Sopenharmony_ci * (50ms). This really should be enough, since the update cycle 24862306a36Sopenharmony_ci * normally needs 2 ms. 24962306a36Sopenharmony_ci * If the UIP bit reads as 0, we have at least 244 usecs until the 25062306a36Sopenharmony_ci * update starts. This should be enough... But to be sure, 25162306a36Sopenharmony_ci * additionally the RTC_SET bit is set to prevent an update cycle. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) { 25562306a36Sopenharmony_ci if (in_atomic() || irqs_disabled()) 25662306a36Sopenharmony_ci mdelay(1); 25762306a36Sopenharmony_ci else 25862306a36Sopenharmony_ci schedule_timeout_interruptible(HWCLK_POLL_INTERVAL); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci local_irq_save(flags); 26262306a36Sopenharmony_ci RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET ); 26362306a36Sopenharmony_ci if (!op) { 26462306a36Sopenharmony_ci sec = RTC_READ( RTC_SECONDS ); 26562306a36Sopenharmony_ci min = RTC_READ( RTC_MINUTES ); 26662306a36Sopenharmony_ci hour = RTC_READ( RTC_HOURS ); 26762306a36Sopenharmony_ci day = RTC_READ( RTC_DAY_OF_MONTH ); 26862306a36Sopenharmony_ci mon = RTC_READ( RTC_MONTH ); 26962306a36Sopenharmony_ci year = RTC_READ( RTC_YEAR ); 27062306a36Sopenharmony_ci wday = RTC_READ( RTC_DAY_OF_WEEK ); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci else { 27362306a36Sopenharmony_ci RTC_WRITE( RTC_SECONDS, sec ); 27462306a36Sopenharmony_ci RTC_WRITE( RTC_MINUTES, min ); 27562306a36Sopenharmony_ci RTC_WRITE( RTC_HOURS, hour + pm); 27662306a36Sopenharmony_ci RTC_WRITE( RTC_DAY_OF_MONTH, day ); 27762306a36Sopenharmony_ci RTC_WRITE( RTC_MONTH, mon ); 27862306a36Sopenharmony_ci RTC_WRITE( RTC_YEAR, year ); 27962306a36Sopenharmony_ci if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday ); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET ); 28262306a36Sopenharmony_ci local_irq_restore(flags); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (!op) { 28562306a36Sopenharmony_ci /* read: adjust values */ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (hour & 0x80) { 28862306a36Sopenharmony_ci hour &= ~0x80; 28962306a36Sopenharmony_ci pm = 1; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!(ctrl & RTC_DM_BINARY)) { 29362306a36Sopenharmony_ci sec = bcd2bin(sec); 29462306a36Sopenharmony_ci min = bcd2bin(min); 29562306a36Sopenharmony_ci hour = bcd2bin(hour); 29662306a36Sopenharmony_ci day = bcd2bin(day); 29762306a36Sopenharmony_ci mon = bcd2bin(mon); 29862306a36Sopenharmony_ci year = bcd2bin(year); 29962306a36Sopenharmony_ci wday = bcd2bin(wday); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!(ctrl & RTC_24H)) { 30362306a36Sopenharmony_ci if (!pm && hour == 12) 30462306a36Sopenharmony_ci hour = 0; 30562306a36Sopenharmony_ci else if (pm && hour != 12) 30662306a36Sopenharmony_ci hour += 12; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci t->tm_sec = sec; 31062306a36Sopenharmony_ci t->tm_min = min; 31162306a36Sopenharmony_ci t->tm_hour = hour; 31262306a36Sopenharmony_ci t->tm_mday = day; 31362306a36Sopenharmony_ci t->tm_mon = mon - 1; 31462306a36Sopenharmony_ci t->tm_year = year + atari_rtc_year_offset; 31562306a36Sopenharmony_ci t->tm_wday = wday - 1; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return( 0 ); 31962306a36Sopenharmony_ci} 320