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
- Lines: 289
- Date:
Sun Nov 30 10:59:02 1997
- Orig file:
v2.1.67/linux/arch/alpha/kernel/time.c
- Orig date:
Wed Nov 12 13:34:25 1997
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