X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fx86_64%2Fkernel%2Ftime.c;h=b0df27f80bacadf3e9d5a30c9d3ae98714d6ffd7;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=ee941842d7102acb06ad7e6d61e951d2430f53eb;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index ee941842d..b0df27f80 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef CONFIG_X86_LOCAL_APIC #include #endif @@ -48,14 +49,14 @@ static void cpufreq_delayed_get(void); extern int using_apic_timer; -spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; -spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(rtc_lock); +DEFINE_SPINLOCK(i8253_lock); static int nohpet __initdata = 0; +static int notsc __initdata = 0; #undef HPET_HACK_ENABLE_DANGEROUS - unsigned int cpu_khz; /* TSC clocks / usec, not used here */ unsigned long hpet_period; /* fsecs / HPET clock */ unsigned long hpet_tick; /* HPET clocks / interrupt */ @@ -330,7 +331,7 @@ static noinline void handle_lost_ticks(int lost, struct pt_regs *regs) print_symbol("rip %s)\n", regs->rip); } - if (lost_count == 100 && !warned) { + if (lost_count == 1000 && !warned) { printk(KERN_WARNING "warning: many lost ticks.\n" KERN_WARNING "Your time source seems to be instable or " @@ -427,6 +428,9 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) */ do_timer(regs); +#ifndef CONFIG_SMP + update_process_times(user_mode(regs)); +#endif /* * In the SMP case we use the local APIC timer interrupt to do the profiling, @@ -723,31 +727,59 @@ static unsigned int __init pit_calibrate_tsc(void) return (end - start) / 50; } -static int hpet_init(void) +#ifdef CONFIG_HPET +static __init int late_hpet_init(void) { - unsigned int cfg, id; + struct hpet_data hd; + unsigned int ntimer; if (!vxtime.hpet_address) - return -1; - set_fixmap_nocache(FIX_HPET_BASE, vxtime.hpet_address); - __set_fixmap(VSYSCALL_HPET, vxtime.hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE); + return -1; -/* - * Read the period, compute tick and quotient. - */ + memset(&hd, 0, sizeof (hd)); - id = hpet_readl(HPET_ID); + ntimer = hpet_readl(HPET_ID); + ntimer = (ntimer & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT; + ntimer++; - if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER) || - !(id & HPET_ID_LEGSUP)) - return -1; + /* + * Register with driver. + * Timer0 and Timer1 is used by platform. + */ + hd.hd_phys_address = vxtime.hpet_address; + hd.hd_address = (void *)fix_to_virt(FIX_HPET_BASE); + hd.hd_nirqs = ntimer; + hd.hd_flags = HPET_DATA_PLATFORM; + hpet_reserve_timer(&hd, 0); +#ifdef CONFIG_HPET_EMULATE_RTC + hpet_reserve_timer(&hd, 1); +#endif + hd.hd_irq[0] = HPET_LEGACY_8254; + hd.hd_irq[1] = HPET_LEGACY_RTC; + if (ntimer > 2) { + struct hpet *hpet; + struct hpet_timer *timer; + int i; - hpet_period = hpet_readl(HPET_PERIOD); - if (hpet_period < 100000 || hpet_period > 100000000) - return -1; + hpet = (struct hpet *) fix_to_virt(FIX_HPET_BASE); - hpet_tick = (1000000000L * (USEC_PER_SEC / HZ) + hpet_period / 2) / - hpet_period; + for (i = 2, timer = &hpet->hpet_timers[2]; i < ntimer; + timer++, i++) + hd.hd_irq[i] = (timer->hpet_config & + Tn_INT_ROUTE_CNF_MASK) >> + Tn_INT_ROUTE_CNF_SHIFT; + + } + + hpet_alloc(&hd); + return 0; +} +fs_initcall(late_hpet_init); +#endif + +static int hpet_timer_stop_set_go(unsigned long tick) +{ + unsigned int cfg; /* * Stop the timers and reset the main counter. @@ -779,6 +811,40 @@ static int hpet_init(void) return 0; } +static int hpet_init(void) +{ + unsigned int id; + + if (!vxtime.hpet_address) + return -1; + set_fixmap_nocache(FIX_HPET_BASE, vxtime.hpet_address); + __set_fixmap(VSYSCALL_HPET, vxtime.hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE); + +/* + * Read the period, compute tick and quotient. + */ + + id = hpet_readl(HPET_ID); + + if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER) || + !(id & HPET_ID_LEGSUP)) + return -1; + + hpet_period = hpet_readl(HPET_PERIOD); + if (hpet_period < 100000 || hpet_period > 100000000) + return -1; + + hpet_tick = (1000000000L * (USEC_PER_SEC / HZ) + hpet_period / 2) / + hpet_period; + + return hpet_timer_stop_set_go(hpet_tick); +} + +static int hpet_reenable(void) +{ + return hpet_timer_stop_set_go(hpet_tick); +} + void __init pit_init(void) { unsigned long flags; @@ -856,61 +922,93 @@ void __init time_init_smp(void) { char *timetype; - if (vxtime.hpet_address) { + /* + * AMD systems with more than one CPU don't have fully synchronized + * TSCs. Always use HPET gettimeofday for these, although it is slower. + * Intel SMP systems usually have synchronized TSCs, so use always + * the TSC. + * + * Exceptions: + * IBM Summit2 checked by oem_force_hpet_timer(). + * AMD dual core may also not need HPET. Check me. + * + * Can be turned off with "notsc". + */ + if (num_online_cpus() > 1 && + boot_cpu_data.x86_vendor == X86_VENDOR_AMD) + notsc = 1; + /* Some systems will want to disable TSC and use HPET. */ + if (oem_force_hpet_timer()) + notsc = 1; + if (vxtime.hpet_address && notsc) { timetype = "HPET"; vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick; vxtime.mode = VXTIME_HPET; do_gettimeoffset = do_gettimeoffset_hpet; } else { - timetype = "PIT/TSC"; + timetype = vxtime.hpet_address ? "HPET/TSC" : "PIT/TSC"; vxtime.mode = VXTIME_TSC; } + printk(KERN_INFO "time.c: Using %s based timekeeping.\n", timetype); } __setup("report_lost_ticks", time_setup); static long clock_cmos_diff; +static unsigned long sleep_start; -static int time_suspend(struct sys_device *dev, u32 state) +static int timer_suspend(struct sys_device *dev, u32 state) { /* * Estimate time zone so that set_time can update the clock */ - clock_cmos_diff = -get_cmos_time(); + long cmos_time = get_cmos_time(); + + clock_cmos_diff = -cmos_time; clock_cmos_diff += get_seconds(); + sleep_start = cmos_time; return 0; } -static int time_resume(struct sys_device *dev) +static int timer_resume(struct sys_device *dev) { unsigned long flags; - unsigned long sec = get_cmos_time() + clock_cmos_diff; + unsigned long sec; + unsigned long ctime = get_cmos_time(); + unsigned long sleep_length = (ctime - sleep_start) * HZ; + + if (vxtime.hpet_address) + hpet_reenable(); + + sec = ctime + clock_cmos_diff; write_seqlock_irqsave(&xtime_lock,flags); xtime.tv_sec = sec; xtime.tv_nsec = 0; write_sequnlock_irqrestore(&xtime_lock,flags); + jiffies += sleep_length; + wall_jiffies += sleep_length; return 0; } -static struct sysdev_class pit_sysclass = { - .resume = time_resume, - .suspend = time_suspend, - set_kset_name("pit"), +static struct sysdev_class timer_sysclass = { + .resume = timer_resume, + .suspend = timer_suspend, + set_kset_name("timer"), }; /* XXX this driverfs stuff should probably go elsewhere later -john */ -static struct sys_device device_i8253 = { +static struct sys_device device_timer = { .id = 0, - .cls = &pit_sysclass, + .cls = &timer_sysclass, }; static int time_init_device(void) { - int error = sysdev_class_register(&pit_sysclass); + int error = sysdev_class_register(&timer_sysclass); if (!error) - error = sysdev_register(&device_i8253); + error = sysdev_register(&device_timer); return error; } @@ -931,7 +1029,6 @@ device_initcall(time_init_device); * For (3), we use interrupts at 64Hz or user specified periodic * frequency, whichever is higher. */ -#include #include extern irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs); @@ -1151,3 +1248,14 @@ static int __init nohpet_setup(char *s) } __setup("nohpet", nohpet_setup); + + +static int __init notsc_setup(char *s) +{ + notsc = 1; + return 0; +} + +__setup("notsc", notsc_setup); + +