patch-2.4.21 linux-2.4.21/arch/x86_64/kernel/time.c

Next file: linux-2.4.21/arch/x86_64/kernel/traps.c
Previous file: linux-2.4.21/arch/x86_64/kernel/syscall.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/arch/x86_64/kernel/time.c linux-2.4.21/arch/x86_64/kernel/time.c
@@ -12,8 +12,6 @@
  *
  */
 
-#define HPET_BIOS_SUPPORT_WORKING
-
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
@@ -26,39 +24,47 @@
 
 extern rwlock_t xtime_lock;
 spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED;
 
 unsigned int cpu_khz;					/* TSC clocks / usec, not used here */
+unsigned long hpet_address;
 unsigned long hpet_period;				/* fsecs / HPET clock */
-unsigned long hpet_tick;				/* HPET clocks / interrupt */
-int hpet_report_lost_ticks;				/* command line option */
+unsigned int hpet_tick;					/* HPET clocks / interrupt */
+unsigned long vxtime_hz = 1193182;
+int report_lost_ticks;					/* command line option */
 
-struct hpet_data __hpet __section_hpet;			/* address, quotient, trigger, hz */
+struct vxtime_data __vxtime __section_vxtime;		/* data for vsyscall */
 
 volatile unsigned long __jiffies __section_jiffies;
 unsigned long __wall_jiffies __section_wall_jiffies;
 struct timeval __xtime __section_xtime;
 struct timezone __sys_tz __section_sys_tz;
 
+static inline void rdtscll_sync(unsigned long *tsc)
+{
+	sync_core();
+	rdtscll(*tsc);
+}
+
 /*
  * do_gettimeoffset() returns microseconds since last timer interrupt was
- * triggered by hardware. A memory read of HPET is slower than a register read
- * of TSC, but much more reliable. It's also synchronized to the timer
- * interrupt. Note that do_gettimeoffset() may return more than hpet_tick, if a
- * timer interrupt has happened already, but hpet.trigger wasn't updated yet.
- * This is not a problem, because jiffies hasn't updated either. They are bound
- * together by xtime_lock.
+ * triggered by hardware. 
  */
 
-static spinlock_t time_offset_lock = SPIN_LOCK_UNLOCKED;
-static unsigned long timeoffset = 0;
-
-inline unsigned int do_gettimeoffset(void)
+static unsigned int do_gettimeoffset_tsc(void)
 {
 	unsigned long t;
-	rdtscll(t);	
-	return (t  - hpet.last_tsc) * (1000000L / HZ) / hpet.ticks + hpet.offset;
+	rdtscll_sync(&t);	
+	return ((t  - vxtime.last_tsc) * vxtime.tsc_quot) >> 32;
 }
 
+static unsigned int do_gettimeoffset_hpet(void)
+{
+	return ((hpet_readl(HPET_COUNTER) - vxtime.last) * vxtime.quot) >> 32;
+}
+
+unsigned int (*do_gettimeoffset)(void) = do_gettimeoffset_tsc;
+
 /*
  * This version of gettimeofday() has microsecond resolution and better than
  * microsecond precision, as we're using at least a 10 MHz (usually 14.31818
@@ -67,21 +73,20 @@
 
 void do_gettimeofday(struct timeval *tv)
 {
-	unsigned long flags, t;
+	unsigned long sequence;
  	unsigned int sec, usec;
 
-	read_lock_irqsave(&xtime_lock, flags);
-	spin_lock(&time_offset_lock);
+	do { 
+		sequence = __vxtime_sequence[1];
+		rmb();
 
 	sec = xtime.tv_sec;
-	usec = xtime.tv_usec;
+	usec = xtime.tv_usec
+		+ (jiffies - wall_jiffies) * tick
+			+ do_gettimeoffset();
 
-	t = (jiffies - wall_jiffies) * (1000000L / HZ) + do_gettimeoffset();
-	if (t > timeoffset) timeoffset = t;
-	usec += timeoffset;
-
-	spin_unlock(&time_offset_lock);
-	read_unlock_irqrestore(&xtime_lock, flags);
+		rmb(); 
+	} while (sequence != __vxtime_sequence[0]);
 
 	tv->tv_sec = sec + usec / 1000000;
 	tv->tv_usec = usec % 1000000;
@@ -98,8 +103,8 @@
 	write_lock_irq(&xtime_lock);
 	vxtime_lock();
 
-	tv->tv_usec -= do_gettimeoffset() +
-		(jiffies - wall_jiffies) * tick;
+	tv->tv_usec -= (jiffies - wall_jiffies) * tick
+			+ do_gettimeoffset();
 
 	while (tv->tv_usec < 0) {
 		tv->tv_usec += 1000000;
@@ -201,15 +206,55 @@
 	vxtime_lock();
 
 	{
-		unsigned long t;
+		long tsc;
+		int delay, offset = 0;
+
+		if (hpet_address) {
+
+			offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
+			delay = hpet_readl(HPET_COUNTER) - offset;
+
+		} else {
+
+			spin_lock(&i8253_lock);
+			outb_p(0x00, 0x43);
+			delay = inb_p(0x40);
+			delay |= inb(0x40) << 8;
+			spin_unlock(&i8253_lock);
+			delay = LATCH - 1 - delay;
+		}
+
+		rdtscll_sync(&tsc);
+
+		if (vxtime.mode == VXTIME_HPET) {
+
+			if (offset - vxtime.last > hpet_tick) {
+				if (report_lost_ticks)
+					printk(KERN_WARNING "time.c: Lost %d timer tick(s)! (rip %016lx)\n",
+						(offset - vxtime.last) / hpet_tick - 1, regs->rip);
+				jiffies += (offset - vxtime.last) / hpet_tick - 1;
+			}
+
+			vxtime.last = offset;
+
+		} else {
+
+			offset = (((tsc - vxtime.last_tsc) * vxtime.tsc_quot) >> 32) - tick;
+
+			if (offset > tick) {
+				if (report_lost_ticks)
+					printk(KERN_WARNING "time.c: lost %ld tick(s) (rip %016lx)\n",
+						 offset / tick, regs->rip);
+				jiffies += offset / tick;
+				offset %= tick;
+			}
 
-		rdtscll(t);
-		hpet.offset = (t  - hpet.last_tsc) * (1000000L / HZ) / hpet.ticks + hpet.offset - 1000000L / HZ;
-		if (hpet.offset >= 1000000L / HZ)
-			hpet.offset = 0;
-		hpet.ticks = min_t(long, max_t(long, (t  - hpet.last_tsc) * (1000000L / HZ) / (1000000L / HZ - hpet.offset),
-				cpu_khz * 1000/HZ * 15 / 16), cpu_khz * 1000/HZ * 16 / 15); 
-		hpet.last_tsc = t;
+			vxtime.last_tsc = tsc - vxtime.quot * delay / vxtime.tsc_quot;
+
+			if ((((tsc - vxtime.last_tsc) * vxtime.tsc_quot) >> 32) < offset)
+				vxtime.last_tsc = tsc - (((long)offset << 32) / vxtime.tsc_quot) - 1;
+
+		}
 	}
 
 /*
@@ -318,6 +363,7 @@
 	do {
 		__cli();
 		hpet_now = hpet_readl(HPET_COUNTER);
+		sync_core();
 		rdtscl(tsc_now);
 		__restore_flags(flags);
 	} while ((tsc_now - tsc_start) < TICK_COUNT && (hpet_now - hpet_start) < TICK_COUNT);
@@ -337,12 +383,10 @@
 static unsigned int __init pit_calibrate_tsc(void)
 {
 	unsigned long start, end;
-	unsigned long flags;
 
 	outb((inb(0x61) & ~0x02) | 0x01, 0x61);
 
-	__save_flags(flags);
-	__cli();
+	spin_lock_irq(&i8253_lock);
 
 	outb(0xb0, 0x43);
 	outb((1193182 / (1000 / 50)) & 0xff, 0x42);
@@ -352,7 +396,7 @@
 	while ((inb(0x61) & 0x20) == 0);
 	rdtscll(end);
 
-	__restore_flags(flags);
+	spin_unlock_irq(&i8253_lock);
 
 	return (end - start) / 50;
 }
@@ -361,9 +405,9 @@
 {
 	unsigned int cfg, id;
 
-	if (!hpet.address)
+	if (!hpet_address)
 		return -1;
-	set_fixmap_nocache(FIX_HPET_BASE, hpet.address);
+	set_fixmap_nocache(FIX_HPET_BASE, hpet_address);
 
 /*
  * Read the period, compute tick and quotient.
@@ -411,35 +455,106 @@
 
 void __init pit_init(void)
 {
+	spin_lock_irq(&i8253_lock);
 	outb_p(0x34, 0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
 	outb_p(LATCH & 0xff, 0x40);	/* LSB */
 	outb_p(LATCH >> 8, 0x40);	/* MSB */
+	spin_unlock_irq(&i8253_lock);
 }
 
-int __init time_setup(char *str)
+static int __init time_setup(char *str)
 {
-	hpet_report_lost_ticks = 1;
+	report_lost_ticks = 1;
 	return 1;
 }
 
+/* Only used on SMP */
+static int notsc __initdata = 0; 
+
+static int __init notsc_setup(char *str)
+{ 
+#ifdef CONFIG_SMP
+	printk(KERN_INFO "notsc ignored on non SMP kernel\n"); 
+#endif
+	notsc = 1;
+	return 1;
+} 
+
 static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};
 
 extern void __init config_acpi_tables(void);
 
 void __init time_init(void)
 {
+	char *timename;
+
+	config_acpi_tables();
+
+#ifdef HPET_HACK_ENABLE_DANGEROUS
+        if (!hpet_address) {
+		printk(KERN_WARNING "time.c: WARNING: Enabling HPET base manually!\n");
+                outl(0x800038a0, 0xcf8);
+                outl(0xff000001, 0xcfc);
+                outl(0x800038a0, 0xcf8);
+                hpet_address = inl(0xcfc) & 0xfffffffe;
+		printk(KERN_WARNING "time.c: WARNING: Enabled HPET at at %#lx.\n", hpet_address);
+        }
+#endif
+
+#ifndef CONFIG_HPET_TIMER
+        hpet_address = 0;
+#endif
+
+	write_lock(&xtime_lock);
 	xtime.tv_sec = get_cmos_time();
 	xtime.tv_usec = 0;
+	write_unlock(&xtime_lock);
 
-		printk(KERN_WARNING "time.c: HPET timer not found, precise timing unavailable.\n");
+	if (!hpet_init()) {
+                vxtime_hz = (1000000000000000L + hpet_period / 2) / hpet_period;
+                cpu_khz = hpet_calibrate_tsc();
+		timename = "HPET";
+	} else {
 		pit_init();
-		printk(KERN_INFO "time.c: Using 1.1931816 MHz PIT timer.\n");
-		setup_irq(0, &irq0);
 		cpu_khz = pit_calibrate_tsc();
-		printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
+		timename = "PIT";
+	}
+
+	vxtime.mode = VXTIME_TSC;
+	vxtime.quot = (1000000L << 32) / vxtime_hz;
+	vxtime.tsc_quot = (1000L << 32) / cpu_khz;
+	rdtscll_sync(&vxtime.last_tsc);
+
+	setup_irq(0, &irq0);
+
+        printk(KERN_INFO "time.c: Detected %ld.%06ld MHz %s timer.\n",
+		vxtime_hz / 1000000, vxtime_hz % 1000000, timename);
+	printk(KERN_INFO "time.c: Detected %d.%03d MHz TSC timer.\n",
 			cpu_khz / 1000, cpu_khz % 1000);
-	hpet.ticks = cpu_khz * (1000 / HZ);
-	rdtscll(hpet.last_tsc);
 }
 
+void __init time_init_smp(void)
+{
+	char *timetype;
+
+	if (hpet_address) {
+		if (notsc) { 
+			timetype = "HPET";
+			vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick;
+			vxtime.mode = VXTIME_HPET;
+			do_gettimeoffset = do_gettimeoffset_hpet;
+		} else {
+			timetype = "HPET/TSC";
+			vxtime.mode = VXTIME_TSC;
+			do_gettimeoffset = do_gettimeoffset_tsc;
+		}		
+	} else {
+			timetype = "PIT/TSC";
+			vxtime.mode = VXTIME_TSC;
+	}
+	printk(KERN_INFO "time.c: Using %s based timekeeping.\n", timetype);
+}
+
+__setup("notsc", notsc_setup);
 __setup("report_lost_ticks", time_setup);
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)