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
- Lines: 336
- Date:
2003-06-13 07:51:32.000000000 -0700
- Orig file:
linux-2.4.20/arch/x86_64/kernel/time.c
- Orig date:
2002-11-28 15:53:12.000000000 -0800
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)