patch-2.1.126 linux/arch/i386/kernel/time.c
Next file: linux/arch/mips/Makefile
Previous file: linux/arch/i386/kernel/setup.c
Back to the patch index
Back to the overall index
- Lines: 422
- Date:
Fri Oct 9 12:16:19 1998
- Orig file:
v2.1.125/linux/arch/i386/kernel/time.c
- Orig date:
Thu Jul 16 18:09:23 1998
diff -u --recursive --new-file v2.1.125/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
@@ -12,7 +12,29 @@
* precision CMOS clock update
* 1996-05-03 Ingo Molnar
* fixed time warps in do_[slow|fast]_gettimeoffset()
+ * 1998-09-05 (Various)
+ * More robust do_fast_gettimeoffset() algorithm implemented
+ * (works with APM, Cyrix 6x86MX and Centaur C6),
+ * monotonic gettimeofday() with fast_get_timeoffset(),
+ * drift-proof precision TSC calibration on boot
+ * (C. Scott Ananian <cananian@alumni.princeton.edu>, Andrew D.
+ * Balsa <andrebalsa@altern.org>, Philip Gladstone <philip@raptor.com>;
+ * ported from 2.0.35 Jumbo-9 by Michael Krause <m.krause@tu-harburg.de>).
*/
+
+/* What about the "updated NTP code" stuff in 2.0 time.c? It's not in
+ * 2.1, perhaps it should be ported, too.
+ *
+ * What about the BUGGY_NEPTUN_TIMER stuff in do_slow_gettimeoffset()?
+ * Whatever it fixes, is it also fixed in the new code from the Jumbo
+ * patch, so that that code can be used instead?
+ *
+ * The CPU Hz should probably be displayed in check_bugs() together
+ * with the CPU vendor and type. Perhaps even only in MHz, though that
+ * takes away some of the fun of the new code :)
+ *
+ * - Michael Krause */
+
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -41,100 +63,51 @@
#include "irq.h"
extern int setup_x86_irq(int, struct irqaction *);
-extern volatile unsigned long lost_ticks;
-/* change this if you have some constant time drift */
-#define USECS_PER_JIFFY (1000020/HZ)
+unsigned long cpu_hz; /* Detected as we calibrate the TSC */
+
+/* Number of usecs that the last interrupt was delayed */
+static int delay_at_last_interrupt;
+
+static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
-#ifndef CONFIG_APM /* cycle counter may be unreliable */
-/* Cycle counter value at the previous timer interrupt.. */
-static struct {
- unsigned long low;
- unsigned long high;
-} init_timer_cc, last_timer_cc;
+/* Cached *multiplier* to convert TSC counts to microseconds.
+ * (see the equation below).
+ * Equal to 2^32 * (1 / (clocks per usec) ).
+ * Initialized in time_init.
+ */
+static unsigned long fast_gettimeoffset_quotient=0;
static unsigned long do_fast_gettimeoffset(void)
{
register unsigned long eax asm("ax");
register unsigned long edx asm("dx");
- unsigned long tmp, quotient, low_timer;
-
- /* Last jiffy when do_fast_gettimeoffset() was called. */
- static unsigned long last_jiffies=0;
-
- /*
- * Cached "1/(clocks per usec)*2^32" value.
- * It has to be recalculated once each jiffy.
- */
- static unsigned long cached_quotient=0;
-
- tmp = jiffies;
-
- quotient = cached_quotient;
- low_timer = last_timer_cc.low;
- if (last_jiffies != tmp) {
- last_jiffies = tmp;
-
- /* Get last timer tick in absolute kernel time */
- eax = low_timer;
- edx = last_timer_cc.high;
- __asm__("subl "SYMBOL_NAME_STR(init_timer_cc)",%0\n\t"
- "sbbl "SYMBOL_NAME_STR(init_timer_cc)"+4,%1"
- :"=a" (eax), "=d" (edx)
- :"0" (eax), "1" (edx));
-
- /*
- * Divide the 64-bit time with the 32-bit jiffy counter,
- * getting the quotient in clocks.
- *
- * Giving quotient = "1/(average internal clocks per usec)*2^32"
- * we do this '1/...' trick to get the 'mull' into the critical
- * path. 'mull' is much faster than divl (10 vs. 41 clocks)
- */
- __asm__("divl %2"
- :"=a" (eax), "=d" (edx)
- :"r" (tmp),
- "0" (eax), "1" (edx));
-
- edx = USECS_PER_JIFFY;
- tmp = eax;
- eax = 0;
-
- __asm__("divl %2"
- :"=a" (eax), "=d" (edx)
- :"r" (tmp),
- "0" (eax), "1" (edx));
- cached_quotient = eax;
- quotient = eax;
- }
-
- /* Read the time counter */
- __asm__("rdtsc" : "=a" (eax), "=d" (edx));
+ /* Read the Time Stamp Counter */
+ __asm__("rdtsc"
+ :"=a" (eax), "=d" (edx));
/* .. relative to previous jiffy (32 bits is enough) */
edx = 0;
- eax -= low_timer;
+ eax -= last_tsc_low; /* tsc_low delta */
/*
- * Time offset = (USECS_PER_JIFFY * time_low) * quotient.
- */
+ * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient.
+ * = (tsc_low delta) / (clocks_per_usec)
+ * = (tsc_low delta) / (clocks_per_jiffy / usecs_per_jiffy)
+ *
+ * Using a mull instead of a divl saves up to 31 clock cycles
+ * in the critical path.
+ */
__asm__("mull %2"
:"=a" (eax), "=d" (edx)
- :"r" (quotient),
+ :"r" (fast_gettimeoffset_quotient),
"0" (eax), "1" (edx));
- /*
- * Due to possible jiffies inconsistencies, we need to check
- * the result so that we'll get a timer that is monotonic.
- */
- if (edx >= USECS_PER_JIFFY)
- edx = USECS_PER_JIFFY-1;
-
- return edx;
+ /* our adjusted time offset in microseconds */
+ return edx + delay_at_last_interrupt;
}
-#endif
/* This function must be called with interrupts disabled
* It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
@@ -249,20 +222,11 @@
return count;
}
-#ifndef CONFIG_APM
-/*
- * this is only used if we have fast gettimeoffset:
- */
-static void do_x86_get_fast_time(struct timeval * tv)
-{
- do_gettimeofday(tv);
-}
-#endif
-
static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
/*
- * This version of gettimeofday has near microsecond resolution.
+ * This version of gettimeofday has microsecond resolution
+ * and better than microsecond precision on fast x86 machines with TSC.
*/
void do_gettimeofday(struct timeval *tv)
{
@@ -272,20 +236,11 @@
cli();
*tv = xtime;
tv->tv_usec += do_gettimeoffset();
-
- /*
- * xtime is atomically updated in timer_bh. lost_ticks is
- * nonzero if the timer bottom half hasnt executed yet.
- */
- if (lost_ticks)
- tv->tv_usec += USECS_PER_JIFFY;
-
- restore_flags(flags);
-
if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000;
tv->tv_sec++;
}
+ restore_flags(flags);
}
void do_settimeofday(struct timeval *tv)
@@ -311,13 +266,15 @@
sti();
}
-
/*
* In order to set the CMOS clock precisely, set_rtc_mmss has to be
* called 500 ms after the second nowtime has started, because when
* nowtime is written into the registers of the CMOS clock, it will
* jump to the next second precisely 500 ms later. Check the Motorola
* MC146818A or Dallas DS12887 data sheet for details.
+ *
+ * BUG: This routine does not handle hour overflow properly; it just
+ * sets the minutes. Usually you'll only notice that after reboot!
*/
static int set_rtc_mmss(unsigned long nowtime)
{
@@ -354,8 +311,12 @@
}
CMOS_WRITE(real_seconds,RTC_SECONDS);
CMOS_WRITE(real_minutes,RTC_MINUTES);
- } else
+ } else {
+ printk(KERN_WARNING
+ "set_rtc_mmss: can't update from %d to %d\n",
+ cmos_minutes, real_minutes);
retval = -1;
+ }
/* The following flags have to be released exactly in this order,
* otherwise the DS12887 (popular MC146818A clone with integrated
@@ -431,21 +392,37 @@
#endif
}
-#ifndef CONFIG_APM /* cycle counter may be unreliable */
/*
* This is the same as the above, except we _also_ save the current
- * cycle counter value at the time of the timer interrupt, so that
+ * Time Stamp Counter value at the time of the timer interrupt, so that
* we later on can estimate the time of day more exactly.
*/
static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
+ int count, flags;
+
+ /* It is important that these two operations happen almost at the
+ * same time. We do the RDTSC stuff first, since it's faster. To
+ * avoid any inconsistencies, we disable interrupts locally.
+ */
+
+ __save_flags(flags);
+ __cli();
/* read Pentium cycle counter */
__asm__("rdtsc"
- :"=a" (last_timer_cc.low),
- "=d" (last_timer_cc.high));
+ :"=a" (last_tsc_low):: "eax", "edx");
+
+ outb_p(0x00, 0x43); /* latch the count ASAP */
+
+ count = inb_p(0x40); /* read the latched count */
+ count |= inb(0x40) << 8;
+
+ count = ((LATCH-1) - count) * TICK_SIZE;
+ delay_at_last_interrupt = (count + LATCH/2) / LATCH;
+ __restore_flags(flags);
+
timer_interrupt(irq, NULL, regs);
}
-#endif
/* 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
@@ -520,6 +497,80 @@
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};
+/* ------ Calibrate the TSC -------
+ * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
+ * Too much 64-bit arithmetic here to do this cleanly in C, and for
+ * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
+ * output busy loop as low as possible. We avoid reading the CTC registers
+ * directly because of the awkward 8-bit access mechanism of the 82C54
+ * device.
+ */
+
+__initfunc(static unsigned long calibrate_tsc(void))
+{
+ unsigned long retval;
+
+ __asm__( /* set the Gate high, program CTC channel 2 for mode 0
+ * (interrupt on terminal count mode), binary count,
+ * load 5 * LATCH count, (LSB and MSB)
+ * to begin countdown, read the TSC and busy wait.
+ * BTW LATCH is calculated in timex.h from the HZ value
+ */
+
+ /* Set the Gate high, disable speaker */
+ "inb $0x61, %%al\n\t" /* Read port */
+ "andb $0xfd, %%al\n\t" /* Turn off speaker Data */
+ "orb $0x01, %%al\n\t" /* Set Gate high */
+ "outb %%al, $0x61\n\t" /* Write port */
+
+ /* Now let's take care of CTC channel 2 */
+ "movb $0xb0, %%al\n\t" /* binary, mode 0, LSB/MSB, ch 2*/
+ "outb %%al, $0x43\n\t" /* Write to CTC command port */
+ "movb $0x0c, %%al\n\t"
+ "outb %%al, $0x42\n\t" /* LSB of count */
+ "movb $0xe9, %%al\n\t"
+ "outb %%al, $0x42\n\t" /* MSB of count */
+
+ /* Read the TSC; counting has just started */
+ "rdtsc\n\t"
+ /* Move the value for safe-keeping. */
+ "movl %%eax, %%ebx\n\t"
+ "movl %%edx, %%ecx\n\t"
+
+ /* Busy wait. Only 50 ms wasted at boot time. */
+ "0: inb $0x61, %%al\n\t" /* Read Speaker Output Port */
+ "testb $0x20, %%al\n\t" /* Check CTC channel 2 output (bit 5) */
+ "jz 0b\n\t"
+
+ /* And read the TSC. 5 jiffies (50.00077ms) have elapsed. */
+ "rdtsc\n\t"
+
+ /* Great. So far so good. Store last TSC reading in
+ * last_tsc_low (only 32 lsb bits needed) */
+ "movl %%eax, last_tsc_low\n\t"
+ /* And now calculate the difference between the readings. */
+ "subl %%ebx, %%eax\n\t"
+ "sbbl %%ecx, %%edx\n\t" /* 64-bit subtract */
+ /* but probably edx = 0 at this point (see below). */
+ /* Now we have 5 * (TSC counts per jiffy) in eax. We want
+ * to calculate TSC->microsecond conversion factor. */
+
+ /* Note that edx (high 32-bits of difference) will now be
+ * zero iff CPU clock speed is less than 85 GHz. Moore's
+ * law says that this is likely to be true for the next
+ * 12 years or so. You will have to change this code to
+ * do a real 64-by-64 divide before that time's up. */
+ "movl %%eax, %%ecx\n\t"
+ "xorl %%eax, %%eax\n\t"
+ "movl %1, %%edx\n\t"
+ "divl %%ecx\n\t" /* eax= 2^32 / (1 * TSC counts per microsecond) */
+ /* Return eax for the use of fast_gettimeoffset */
+ "movl %%eax, %0\n\t"
+ : "=r" (retval)
+ : "r" (5 * 1000020/HZ)
+ : /* we clobber: */ "ax", "bx", "cx", "dx", "cc", "memory");
+ return retval;
+}
__initfunc(void time_init(void))
{
@@ -527,36 +578,36 @@
xtime.tv_usec = 0;
/*
- * If we have APM enabled we can't currently depend
- * on the cycle counter, because a suspend to disk
- * will reset it. Somebody should come up with a
- * better solution than to just disable the fast time
- * code..
- */
-#ifndef CONFIG_APM
- /* If we have the CPU hardware time counters, use them */
- if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
+ * If we have APM enabled or the CPU clock speed is variable
+ * (CPU stops clock on HLT or slows clock to save power)
+ * then the TSC timestamps may diverge by up to 1 jiffy from
+ * 'real time' but nothing will break.
+ * The most frequent case is that the CPU is "woken" from a halt
+ * state by the timer interrupt itself, so we get 0 error. In the
+ * rare cases where a driver would "wake" the CPU and request a
+ * timestamp, the maximum error is < 1 jiffy. But timestamps are
+ * still perfectly ordered.
+ * Note that the TSC counter will be reset if APM suspends
+ * to disk; this won't break the kernel, though, 'cuz we're
+ * smart. See devices/char/apm_bios.c.
+ */
+ if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
do_gettimeoffset = do_fast_gettimeoffset;
- do_get_fast_time = do_x86_get_fast_time;
-
- if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
- boot_cpu_data.x86 == 5 &&
- boot_cpu_data.x86_model == 0) {
- /* turn on cycle counters during power down */
- __asm__ __volatile__ (" movl $0x83, %%ecx \n \
- rdmsr \n \
- orl $1,%%eax \n \
- wrmsr \n "
- : : : "ax", "cx", "dx" );
- udelay(500);
- }
-
- /* read Pentium cycle counter */
- __asm__("rdtsc"
- :"=a" (init_timer_cc.low),
- "=d" (init_timer_cc.high));
+ do_get_fast_time = do_gettimeofday;
irq0.handler = pentium_timer_interrupt;
+ fast_gettimeoffset_quotient = calibrate_tsc();
+
+ /* report CPU clock rate in Hz.
+ * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
+ * clock/second. Our precision is about 100 ppm.
+ */
+ { unsigned long eax=0, edx=1000000;
+ __asm__("divl %2"
+ :"=a" (cpu_hz), "=d" (edx)
+ :"r" (fast_gettimeoffset_quotient),
+ "0" (eax), "1" (edx));
+ printk("Detected %ld Hz processor.\n", cpu_hz);
+ }
}
-#endif
setup_x86_irq(0, &irq0);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov