patch-2.1.68 linux/arch/alpha/kernel/time.c

Next file: linux/arch/alpha/kernel/traps.c
Previous file: linux/arch/alpha/kernel/signal.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.67/linux/arch/alpha/kernel/time.c linux/arch/alpha/kernel/time.c
@@ -17,17 +17,18 @@
  *      (round system clock to nearest tick instead of truncating)
  *      fixed algorithm in time_init for getting time from CMOS clock
  */
+#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/param.h>
 #include <linux/string.h>
 #include <linux/mm.h>
+#include <linux/delay.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/hwrpb.h>
-#include <asm/delay.h>
 
 #include <linux/mc146818rtc.h>
 #include <linux/timex.h>
@@ -52,16 +53,18 @@
 
 /* lump static variables together for more efficient access: */
 static struct {
-	__u32		last_time;		/* cycle counter last time it got invoked */
-	unsigned long	scaled_ticks_per_cycle;	/* ticks/cycle * 2^48 */
-	long		last_rtc_update;	/* last time the cmos clock got updated */
+	/* cycle counter last time it got invoked */
+	__u32 last_time;
+	/* ticks/cycle * 2^48 */
+	unsigned long scaled_ticks_per_cycle;
+	/* last time the cmos clock got updated */
+	time_t last_rtc_update;
 } state;
 
 
 static inline __u32 rpcc(void)
 {
     __u32 result;
-
     asm volatile ("rpcc %0" : "r="(result));
     return result;
 }
@@ -73,37 +76,46 @@
  */
 void timer_interrupt(int irq, void *dev, struct pt_regs * regs)
 {
-	__u32 delta, now;
-	int i, nticks;
+	const unsigned long half = 1UL << (FIX_SHIFT - 1);
+	const unsigned long mask = (1UL << (FIX_SHIFT + 1)) - 1;
+	unsigned long delta;
+	__u32 now;
+	long nticks;
 
+	/*
+	 * Estimate how many ticks have passed since the last update.
+	 * Round the result, .5 to even.  When we loose ticks due to
+	 * say using IDE, the clock has been seen to run up to 15% slow
+	 * if we truncate.
+	 */
 	now = rpcc();
 	delta = now - state.last_time;
 	state.last_time = now;
-	if(hwrpb->cycle_freq) {
-		nticks = (delta * state.scaled_ticks_per_cycle) >> (FIX_SHIFT-1);
-		nticks = (nticks+1) >> 1;
-    }
-	else nticks=1; /* No way to estimate lost ticks if we don't know
-					  the cycle frequency. */
-	for (i = 0; i < nticks; ++i) {
+	delta = delta * state.scaled_ticks_per_cycle;
+	if ((delta & mask) != half)
+		delta += half;
+	nticks = delta >> FIX_SHIFT;
+
+	do {
 		do_timer(regs);
-	}
+	} while (--nticks > 0);
 
 	/*
 	 * If we have an externally synchronized Linux clock, then update
 	 * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
 	 * called as close as possible to 500 ms before the new second starts.
 	 */
-	if (time_state != TIME_BAD && xtime.tv_sec > state.last_rtc_update + 660 &&
-	    xtime.tv_usec > 500000 - (tick >> 1) &&
-	    xtime.tv_usec < 500000 + (tick >> 1))
-	  if (set_rtc_mmss(xtime.tv_sec) == 0)
-	    state.last_rtc_update = xtime.tv_sec;
-	  else
-	    state.last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+	if (time_state != TIME_BAD
+	    && xtime.tv_sec > state.last_rtc_update + 660
+	    && xtime.tv_usec >= 500000 - (tick >> 1)
+	    && xtime.tv_usec <= 500000 + (tick >> 1)) {
+		int tmp = set_rtc_mmss(xtime.tv_sec);
+		state.last_rtc_update = xtime.tv_sec - (tmp ? 600 : 0);
+	}
 }
 
-/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+/*
+ * Converts Gregorian date to seconds since 1970-01-01 00:00:00.
  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
  * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
  *
@@ -140,25 +152,40 @@
 	unsigned char save_control;
 #endif
         void (*irq_handler)(int, void *, struct pt_regs *);
-	unsigned int year, mon, day, hour, min, sec;
+	unsigned int year, mon, day, hour, min, sec, cc1, cc2;
 
-	/* The Linux interpretation of the CMOS clock register contents:
+	/*
+	 * The Linux interpretation of the CMOS clock register contents:
 	 * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
 	 * RTC registers show the second which has precisely just started.
 	 * Let's hope other operating systems interpret the RTC the same way.
 	 */
-	/* read RTC exactly on falling edge of update flag */
-	/* Wait for rise.... (may take up to 1 second) */
-	
-	do {} while(!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP));
-	
-/* Jay Estabook <jestabro@amt.tay1.dec.com>:
- * Wait for the Update Done Interrupt bit (0x10) in reg C (12) to be set,
- * which (hopefully) indicates that the update is really done.
- */
-	
-	do {} while(!CMOS_READ(RTC_REG_C) & RTC_UIP);
-	
+	do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP));
+	do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
+
+	/* Read cycle counter exactly on falling edge of update flag */
+	cc1 = rpcc();
+
+	/* If our cycle frequency isn't valid, go another round and give
+	   a guess at what it should be.  */
+	if (hwrpb->cycle_freq == 0) {
+		printk("HWPRB cycle frequency bogus.  Estimating... ");
+
+		do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP));
+		do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
+		cc2 = rpcc();
+		hwrpb->cycle_freq = cc2 - cc1;
+		cc1 = cc2;
+
+		printk("%lu Hz\n", hwrpb->cycle_freq);
+	}
+
+	/* From John Bowman <bowman@math.ualberta.ca>: allow the values
+	   to settle, as the Update-In-Progress bit going low isn't good
+	   enough on some hardware.  2ms is our guess; we havn't found 
+	   bogomips yet, but this is close on a 500Mhz box.  */
+	__delay(1000000);
+
 	sec = CMOS_READ(RTC_SECONDS);
 	min = CMOS_READ(RTC_MINUTES);
 	hour = CMOS_READ(RTC_HOURS);
@@ -167,14 +194,14 @@
 	year = CMOS_READ(RTC_YEAR);
 
 	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
-	  {
-	    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(sec);
+		BCD_TO_BIN(min);
+		BCD_TO_BIN(hour);
+		BCD_TO_BIN(day);
+		BCD_TO_BIN(mon);
+		BCD_TO_BIN(year);
+	}
 #ifdef ALPHA_PRE_V1_2_SRM_CONSOLE
 	/*
 	 * The meaning of life, the universe, and everything. Plus
@@ -192,9 +219,10 @@
 		extern void __you_loose (void);
 		__you_loose();
 	}
-	state.last_time = rpcc();
-	if(hwrpb->cycle_freq)
-	state.scaled_ticks_per_cycle = ((unsigned long) HZ << FIX_SHIFT) / hwrpb->cycle_freq;
+
+	state.last_time = cc1;
+	state.scaled_ticks_per_cycle
+		= ((unsigned long) HZ << FIX_SHIFT) / hwrpb->cycle_freq;
 	state.last_rtc_update = 0;
 
 #ifdef CONFIG_RTC 
@@ -210,22 +238,52 @@
 	/* setup timer */ 
         irq_handler = timer_interrupt;
         if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL))
-	  panic("Could not allocate timer IRQ!");
+		panic("Could not allocate timer IRQ!");
 }
 
 /*
- * We could get better timer accuracy by using the alpha
- * time counters or something.  Now this is limited to
- * the HZ clock frequency.
+ * Use the cycle counter to estimate an displacement from the last time
+ * tick.  Unfortunately the Alpha designers made only the low 32-bits of
+ * the cycle counter active, so we overflow on 8.2 seconds on a 500MHz
+ * part.  So we can't do the "find absolute time in terms of cycles" thing
+ * that the other ports do.
  */
 void do_gettimeofday(struct timeval *tv)
 {
-	unsigned long flags;
+	unsigned long flags, now, delta_cycles, delta_usec;
+	unsigned long sec, usec;
 
-	save_flags(flags);
-	cli();
-	*tv = xtime;
+	now = rpcc();
+	save_and_cli(flags);
+	sec = xtime.tv_sec;
+	usec = xtime.tv_usec;
+	delta_cycles = now - state.last_time;
 	restore_flags(flags);
+
+	/*
+	 * usec = cycles * ticks_per_cycle * 2**48 * 1e6 / (2**48 * ticks)
+	 *	= cycles * (s_t_p_c) * 1e6 / (2**48 * ticks)
+	 *	= cycles * (s_t_p_c) * 15625 / (2**42 * ticks)
+	 *
+	 * which, given a 600MHz cycle and a 1024Hz tick, has a
+	 * dynamic range of about 1.7e17, which is less than the
+	 * 1.8e19 in an unsigned long, so we are safe from overflow.
+	 *
+	 * Round, but with .5 up always, since .5 to even is harder
+	 * with no clear gain.
+	 */
+
+	delta_usec = delta_cycles * state.scaled_ticks_per_cycle * 15625;
+	delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6)) * HZ)) + 1) / 2;
+
+	usec += delta_usec;
+	if (usec >= 1000000) {
+		sec += 1;
+		usec -= 1000000;
+	}
+
+	tv->tv_sec = sec;
+	tv->tv_usec = usec;
 }
 
 void do_settimeofday(struct timeval *tv)
@@ -252,10 +310,12 @@
 	int real_seconds, real_minutes, cmos_minutes;
 	unsigned char save_control, save_freq_select;
 
-	save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
+	/* Tell the clock it's being set */
+	save_control = CMOS_READ(RTC_CONTROL);
 	CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
 
-	save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
+	/* Stop and reset prescaler */
+	save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
 	CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
 
 	cmos_minutes = CMOS_READ(RTC_MINUTES);
@@ -270,8 +330,10 @@
 	 */
 	real_seconds = nowtime % 60;
 	real_minutes = nowtime / 60;
-	if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
-		real_minutes += 30;		/* correct for half hour time zone */
+	if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) {
+		/* correct for half hour time zone */
+		real_minutes += 30;
+	}
 	real_minutes %= 60;
 
 	if (abs(real_minutes - cmos_minutes) < 30) {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov