patch-2.1.87 linux/arch/m68k/atari/time.c
Next file: linux/arch/m68k/config.in
Previous file: linux/arch/m68k/atari/stram.c
Back to the patch index
Back to the overall index
- Lines: 417
- Date:
Thu Feb 12 16:30:12 1998
- Orig file:
v2.1.86/linux/arch/m68k/atari/time.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.86/linux/arch/m68k/atari/time.c linux/arch/m68k/atari/time.c
@@ -0,0 +1,416 @@
+/*
+ * linux/arch/m68k/atari/time.c
+ *
+ * Atari time and real time clock stuff
+ *
+ * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mc146818rtc.h>
+#include <linux/kd.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+
+__initfunc(void
+atari_sched_init(void (*timer_routine)(int, void *, struct pt_regs *)))
+{
+ /* set Timer C data Register */
+ mfp.tim_dt_c = INT_TICKS;
+ /* start timer C, div = 1:100 */
+ mfp.tim_ct_cd = (mfp.tim_ct_cd & 15) | 0x60;
+ /* install interrupt service routine for MFP Timer C */
+ request_irq(IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW,
+ "timer", timer_routine);
+}
+
+/* ++andreas: gettimeoffset fixed to check for pending interrupt */
+
+#define TICK_SIZE 10000
+
+/* This is always executed with interrupts disabled. */
+unsigned long atari_gettimeoffset (void)
+{
+ unsigned long ticks, offset = 0;
+
+ /* read MFP timer C current value */
+ ticks = mfp.tim_dt_c;
+ /* The probability of underflow is less than 2% */
+ if (ticks > INT_TICKS - INT_TICKS / 50)
+ /* Check for pending timer interrupt */
+ if (mfp.int_pn_b & (1 << 5))
+ offset = TICK_SIZE;
+
+ ticks = INT_TICKS - ticks;
+ ticks = ticks * 10000L / INT_TICKS;
+
+ return ticks + offset;
+}
+
+
+static void mste_read(struct MSTE_RTC *val)
+{
+#define COPY(v) val->v=(mste_rtc.v & 0xf)
+ do {
+ COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
+ COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
+ COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
+ COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
+ COPY(year_tens) ;
+ /* prevent from reading the clock while it changed */
+ } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
+#undef COPY
+}
+
+static void mste_write(struct MSTE_RTC *val)
+{
+#define COPY(v) mste_rtc.v=val->v
+ do {
+ COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
+ COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
+ COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
+ COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
+ COPY(year_tens) ;
+ /* prevent from writing the clock while it changed */
+ } while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
+#undef COPY
+}
+
+#define RTC_READ(reg) \
+ ({ unsigned char __val; \
+ outb(reg,&tt_rtc.regsel); \
+ __val = tt_rtc.data; \
+ __val; \
+ })
+
+#define RTC_WRITE(reg,val) \
+ do { \
+ outb(reg,&tt_rtc.regsel); \
+ tt_rtc.data = (val); \
+ } while(0)
+
+
+void atari_mste_gettod (int *yearp, int *monp, int *dayp,
+ int *hourp, int *minp, int *secp)
+{
+ int hr24=0, hour;
+ struct MSTE_RTC val;
+
+ mste_rtc.mode=(mste_rtc.mode | 1);
+ hr24=mste_rtc.mon_tens & 1;
+ mste_rtc.mode=(mste_rtc.mode & ~1);
+
+ mste_read(&val);
+ *secp = val.sec_ones + val.sec_tens * 10;
+ *minp = val.min_ones + val.min_tens * 10;
+ hour = val.hr_ones + val.hr_tens * 10;
+ if (!hr24) {
+ if (hour == 12 || hour == 12 + 20)
+ hour -= 12;
+ if (hour >= 20)
+ hour += 12 - 20;
+ }
+ *hourp = hour;
+ *dayp = val.day_ones + val.day_tens * 10;
+ *monp = val.mon_ones + val.mon_tens * 10;
+ *yearp = val.year_ones + val.year_tens * 10 + 80;
+}
+
+
+void atari_tt_gettod (int *yearp, int *monp, int *dayp,
+ int *hourp, int *minp, int *secp)
+{
+ unsigned char ctrl;
+ int hour, pm;
+
+ while (!(RTC_READ(RTC_FREQ_SELECT) & RTC_UIP)) ;
+ while (RTC_READ(RTC_FREQ_SELECT) & RTC_UIP) ;
+
+ *secp = RTC_READ(RTC_SECONDS);
+ *minp = RTC_READ(RTC_MINUTES);
+ hour = RTC_READ(RTC_HOURS);
+ *dayp = RTC_READ(RTC_DAY_OF_MONTH);
+ *monp = RTC_READ(RTC_MONTH);
+ *yearp = RTC_READ(RTC_YEAR);
+ pm = hour & 0x80;
+ hour &= ~0x80;
+
+ ctrl = RTC_READ(RTC_CONTROL);
+
+ if (!(ctrl & RTC_DM_BINARY)) {
+ BCD_TO_BIN(*secp);
+ BCD_TO_BIN(*minp);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(*dayp);
+ BCD_TO_BIN(*monp);
+ BCD_TO_BIN(*yearp);
+ }
+ if (!(ctrl & RTC_24H)) {
+ if (!pm && hour == 12)
+ hour = 0;
+ else if (pm && hour != 12)
+ hour += 12;
+ }
+ *hourp = hour;
+
+ /* Adjust values (let the setup valid) */
+ *yearp += atari_rtc_year_offset;
+}
+
+#define HWCLK_POLL_INTERVAL 5
+
+int atari_mste_hwclk( int op, struct hwclk_time *t )
+{
+ int hour, year;
+ int hr24=0;
+ struct MSTE_RTC val;
+
+ mste_rtc.mode=(mste_rtc.mode | 1);
+ hr24=mste_rtc.mon_tens & 1;
+ mste_rtc.mode=(mste_rtc.mode & ~1);
+
+ if (op) {
+ /* write: prepare values */
+
+ val.sec_ones = t->sec % 10;
+ val.sec_tens = t->sec / 10;
+ val.min_ones = t->min % 10;
+ val.min_tens = t->min / 10;
+ hour = t->hour;
+ if (!hr24) {
+ if (hour > 11)
+ hour += 20 - 12;
+ if (hour == 0 || hour == 20)
+ hour += 12;
+ }
+ val.hr_ones = hour % 10;
+ val.hr_tens = hour / 10;
+ val.day_ones = t->day % 10;
+ val.day_tens = t->day / 10;
+ val.mon_ones = (t->mon+1) % 10;
+ val.mon_tens = (t->mon+1) / 10;
+ year = t->year - 80;
+ val.year_ones = year % 10;
+ val.year_tens = year / 10;
+ val.weekday = t->wday;
+ mste_write(&val);
+ mste_rtc.mode=(mste_rtc.mode | 1);
+ val.year_ones = (year % 4); /* leap year register */
+ mste_rtc.mode=(mste_rtc.mode & ~1);
+ }
+ else {
+ mste_read(&val);
+ t->sec = val.sec_ones + val.sec_tens * 10;
+ t->min = val.min_ones + val.min_tens * 10;
+ hour = val.hr_ones + val.hr_tens * 10;
+ if (!hr24) {
+ if (hour == 12 || hour == 12 + 20)
+ hour -= 12;
+ if (hour >= 20)
+ hour += 12 - 20;
+ }
+ t->hour = hour;
+ t->day = val.day_ones + val.day_tens * 10;
+ t->mon = val.mon_ones + val.mon_tens * 10 - 1;
+ t->year = val.year_ones + val.year_tens * 10 + 80;
+ t->wday = val.weekday;
+ }
+ return 0;
+}
+
+int atari_tt_hwclk( int op, struct hwclk_time *t )
+{
+ int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0;
+ unsigned long flags;
+ unsigned char ctrl;
+ int pm = 0;
+
+ ctrl = RTC_READ(RTC_CONTROL); /* control registers are
+ * independent from the UIP */
+
+ if (op) {
+ /* write: prepare values */
+
+ sec = t->sec;
+ min = t->min;
+ hour = t->hour;
+ day = t->day;
+ mon = t->mon + 1;
+ year = t->year - atari_rtc_year_offset;
+ wday = t->wday + (t->wday >= 0);
+
+ if (!(ctrl & RTC_24H)) {
+ if (hour > 11) {
+ pm = 0x80;
+ if (hour != 12)
+ hour -= 12;
+ }
+ else if (hour == 0)
+ hour = 12;
+ }
+
+ if (!(ctrl & RTC_DM_BINARY)) {
+ BIN_TO_BCD(sec);
+ BIN_TO_BCD(min);
+ BIN_TO_BCD(hour);
+ BIN_TO_BCD(day);
+ BIN_TO_BCD(mon);
+ BIN_TO_BCD(year);
+ if (wday >= 0) BIN_TO_BCD(wday);
+ }
+ }
+
+ /* Reading/writing the clock registers is a bit critical due to
+ * the regular update cycle of the RTC. While an update is in
+ * progress, registers 0..9 shouldn't be touched.
+ * The problem is solved like that: If an update is currently in
+ * progress (the UIP bit is set), the process sleeps for a while
+ * (50ms). This really should be enough, since the update cycle
+ * normally needs 2 ms.
+ * If the UIP bit reads as 0, we have at least 244 usecs until the
+ * update starts. This should be enough... But to be sure,
+ * additionally the RTC_SET bit is set to prevent an update cycle.
+ */
+
+ while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + HWCLK_POLL_INTERVAL;
+ schedule();
+ }
+
+ save_flags(flags);
+ cli();
+ RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
+ if (!op) {
+ sec = RTC_READ( RTC_SECONDS );
+ min = RTC_READ( RTC_MINUTES );
+ hour = RTC_READ( RTC_HOURS );
+ day = RTC_READ( RTC_DAY_OF_MONTH );
+ mon = RTC_READ( RTC_MONTH );
+ year = RTC_READ( RTC_YEAR );
+ wday = RTC_READ( RTC_DAY_OF_WEEK );
+ }
+ else {
+ RTC_WRITE( RTC_SECONDS, sec );
+ RTC_WRITE( RTC_MINUTES, min );
+ RTC_WRITE( RTC_HOURS, hour + pm);
+ RTC_WRITE( RTC_DAY_OF_MONTH, day );
+ RTC_WRITE( RTC_MONTH, mon );
+ RTC_WRITE( RTC_YEAR, year );
+ if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
+ }
+ RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
+ restore_flags(flags);
+
+ if (!op) {
+ /* read: adjust values */
+
+ if (hour & 0x80) {
+ hour &= ~0x80;
+ pm = 1;
+ }
+
+ if (!(ctrl & RTC_DM_BINARY)) {
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+ BCD_TO_BIN(wday);
+ }
+
+ if (!(ctrl & RTC_24H)) {
+ if (!pm && hour == 12)
+ hour = 0;
+ else if (pm && hour != 12)
+ hour += 12;
+ }
+
+ t->sec = sec;
+ t->min = min;
+ t->hour = hour;
+ t->day = day;
+ t->mon = mon - 1;
+ t->year = year + atari_rtc_year_offset;
+ t->wday = wday - 1;
+ }
+
+ return( 0 );
+}
+
+
+int atari_mste_set_clock_mmss (unsigned long nowtime)
+{
+ short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
+ struct MSTE_RTC val;
+ unsigned char rtc_minutes;
+
+ mste_read(&val);
+ rtc_minutes= val.min_ones + val.min_tens * 10;
+ if ((rtc_minutes < real_minutes
+ ? real_minutes - rtc_minutes
+ : rtc_minutes - real_minutes) < 30)
+ {
+ val.sec_ones = real_seconds % 10;
+ val.sec_tens = real_seconds / 10;
+ val.min_ones = real_minutes % 10;
+ val.min_tens = real_minutes / 10;
+ mste_write(&val);
+ }
+ else
+ return -1;
+ return 0;
+}
+
+int atari_tt_set_clock_mmss (unsigned long nowtime)
+{
+ int retval = 0;
+ short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
+ unsigned char save_control, save_freq_select, rtc_minutes;
+
+ save_control = RTC_READ (RTC_CONTROL); /* tell the clock it's being set */
+ RTC_WRITE (RTC_CONTROL, save_control | RTC_SET);
+
+ save_freq_select = RTC_READ (RTC_FREQ_SELECT); /* stop and reset prescaler */
+ RTC_WRITE (RTC_FREQ_SELECT, save_freq_select | RTC_DIV_RESET2);
+
+ rtc_minutes = RTC_READ (RTC_MINUTES);
+ if (!(save_control & RTC_DM_BINARY))
+ BCD_TO_BIN (rtc_minutes);
+
+ /* Since we're only adjusting minutes and seconds, don't interfere
+ with hour overflow. This avoids messing with unknown time zones
+ but requires your RTC not to be off by more than 30 minutes. */
+ if ((rtc_minutes < real_minutes
+ ? real_minutes - rtc_minutes
+ : rtc_minutes - real_minutes) < 30)
+ {
+ if (!(save_control & RTC_DM_BINARY))
+ {
+ BIN_TO_BCD (real_seconds);
+ BIN_TO_BCD (real_minutes);
+ }
+ RTC_WRITE (RTC_SECONDS, real_seconds);
+ RTC_WRITE (RTC_MINUTES, real_minutes);
+ }
+ else
+ retval = -1;
+
+ RTC_WRITE (RTC_FREQ_SELECT, save_freq_select);
+ RTC_WRITE (RTC_CONTROL, save_control);
+ return retval;
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * tab-width: 8
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov