x86: on x86_64, correct reading of PC RTC when update in progress in time_64.c
[sfrench/cifs-2.6.git] / arch / x86 / kernel / time_64.c
index 6d48a4e826d9d1f27dc0cf6a76b0d98b8ba3ffbb..368b1942b39aba509cfafe7fae629ef400a7a243 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *  linux/arch/x86-64/kernel/time.c
- *
  *  "High Precision Event Timer" based timekeeping.
  *
  *  Copyright (c) 1991,1992,1995  Linus Torvalds
 #include <linux/cpu.h>
 #include <linux/kallsyms.h>
 #include <linux/acpi.h>
+#include <linux/clockchips.h>
+
 #ifdef CONFIG_ACPI
 #include <acpi/achware.h>      /* for PM timer frequency */
 #include <acpi/acpi_bus.h>
 #endif
-#include <asm/8253pit.h>
 #include <asm/i8253.h>
 #include <asm/pgtable.h>
 #include <asm/vsyscall.h>
 #include <asm/nmi.h>
 #include <asm/vgtod.h>
 
-static char *timename = NULL;
-
 DEFINE_SPINLOCK(rtc_lock);
 EXPORT_SYMBOL(rtc_lock);
-DEFINE_SPINLOCK(i8253_lock);
-EXPORT_SYMBOL(i8253_lock);
 
 volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;
 
@@ -87,18 +82,15 @@ static int set_rtc_mmss(unsigned long nowtime)
        int retval = 0;
        int real_seconds, real_minutes, cmos_minutes;
        unsigned char control, freq_select;
+       unsigned long flags;
 
 /*
- * IRQs are disabled when we're called from the timer interrupt,
- * no need for spin_lock_irqsave()
+ * set_rtc_mmss is called when irqs are enabled, so disable irqs here
  */
-
-       spin_lock(&rtc_lock);
-
+       spin_lock_irqsave(&rtc_lock, flags);
 /*
  * Tell the clock it's being set and stop it.
  */
-
        control = CMOS_READ(RTC_CONTROL);
        CMOS_WRITE(control | RTC_SET, RTC_CONTROL);
 
@@ -143,7 +135,7 @@ static int set_rtc_mmss(unsigned long nowtime)
        CMOS_WRITE(control, RTC_CONTROL);
        CMOS_WRITE(freq_select, RTC_FREQ_SELECT);
 
-       spin_unlock(&rtc_lock);
+       spin_unlock_irqrestore(&rtc_lock, flags);
 
        return retval;
 }
@@ -153,45 +145,12 @@ int update_persistent_clock(struct timespec now)
        return set_rtc_mmss(now.tv_sec);
 }
 
-void main_timer_handler(void)
+static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
 {
-/*
- * Here we are in the timer irq handler. We have irqs locally disabled (so we
- * don't need spin_lock_irqsave()) but we don't know if the timer_bh is running
- * on the other CPU, so we need a lock. We also need to lock the vsyscall
- * variables, because both do_timer() and us change them -arca+vojtech
- */
-
-       write_seqlock(&xtime_lock);
-
-/*
- * Do the timer stuff.
- */
-
-       do_timer(1);
-#ifndef CONFIG_SMP
-       update_process_times(user_mode(get_irq_regs()));
-#endif
-
-/*
- * In the SMP case we use the local APIC timer interrupt to do the profiling,
- * except when we simulate SMP mode on a uniprocessor system, in that case we
- * have to call the local interrupt handler.
- */
+       add_pda(irq0_irqs, 1);
 
-       if (!using_apic_timer)
-               smp_local_timer_interrupt();
+       global_clock_event->event_handler(global_clock_event);
 
-       write_sequnlock(&xtime_lock);
-}
-
-static irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-       if (apic_runs_main_timer > 1)
-               return IRQ_HANDLED;
-       main_timer_handler();
-       if (using_apic_timer)
-               smp_send_timer_broadcast_ipi();
        return IRQ_HANDLED;
 }
 
@@ -202,21 +161,27 @@ unsigned long read_persistent_clock(void)
        unsigned century = 0;
 
        spin_lock_irqsave(&rtc_lock, flags);
+       /*
+        * if UIP is clear, then we have >= 244 microseconds before RTC
+        * registers will be updated.  Spec sheet says that this is the
+        * reliable way to read RTC - registers invalid (off bus) during update
+        */
+       while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+               cpu_relax();
 
-       do {
-               sec = CMOS_READ(RTC_SECONDS);
-               min = CMOS_READ(RTC_MINUTES);
-               hour = CMOS_READ(RTC_HOURS);
-               day = CMOS_READ(RTC_DAY_OF_MONTH);
-               mon = CMOS_READ(RTC_MONTH);
-               year = CMOS_READ(RTC_YEAR);
+
+       /* now read all RTC registers while stable with interrupts disabled */
+       sec = CMOS_READ(RTC_SECONDS);
+       min = CMOS_READ(RTC_MINUTES);
+       hour = CMOS_READ(RTC_HOURS);
+       day = CMOS_READ(RTC_DAY_OF_MONTH);
+       mon = CMOS_READ(RTC_MONTH);
+       year = CMOS_READ(RTC_YEAR);
 #ifdef CONFIG_ACPI
-               if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
-                                       acpi_gbl_FADT.century)
-                       century = CMOS_READ(acpi_gbl_FADT.century);
+       if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
+                               acpi_gbl_FADT.century)
+               century = CMOS_READ(acpi_gbl_FADT.century);
 #endif
-       } while (sec != CMOS_READ(RTC_SECONDS));
-
        spin_unlock_irqrestore(&rtc_lock, flags);
 
        /*
@@ -292,97 +257,21 @@ static unsigned int __init tsc_calibrate_cpu_khz(void)
        return pmc_now * tsc_khz / (tsc_now - tsc_start);
 }
 
-/*
- * pit_calibrate_tsc() uses the speaker output (channel 2) of
- * the PIT. This is better than using the timer interrupt output,
- * because we can read the value of the speaker with just one inb(),
- * where we need three i/o operations for the interrupt channel.
- * We count how many ticks the TSC does in 50 ms.
- */
-
-static unsigned int __init pit_calibrate_tsc(void)
-{
-       unsigned long start, end;
-       unsigned long flags;
-
-       spin_lock_irqsave(&i8253_lock, flags);
-
-       outb((inb(0x61) & ~0x02) | 0x01, 0x61);
-
-       outb(0xb0, 0x43);
-       outb((PIT_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
-       outb((PIT_TICK_RATE / (1000 / 50)) >> 8, 0x42);
-       start = get_cycles_sync();
-       while ((inb(0x61) & 0x20) == 0);
-       end = get_cycles_sync();
-
-       spin_unlock_irqrestore(&i8253_lock, flags);
-
-       return (end - start) / 50;
-}
-
-#define PIT_MODE 0x43
-#define PIT_CH0  0x40
-
-static void __pit_init(int val, u8 mode)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&i8253_lock, flags);
-       outb_p(mode, PIT_MODE);
-       outb_p(val & 0xff, PIT_CH0);    /* LSB */
-       outb_p(val >> 8, PIT_CH0);      /* MSB */
-       spin_unlock_irqrestore(&i8253_lock, flags);
-}
-
-void __init pit_init(void)
-{
-       __pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
-}
-
-void pit_stop_interrupt(void)
-{
-       __pit_init(0, 0x30); /* mode 0 */
-}
-
-void stop_timer_interrupt(void)
-{
-       char *name;
-       if (hpet_address) {
-               name = "HPET";
-               hpet_timer_stop_set_go(0);
-       } else {
-               name = "PIT";
-               pit_stop_interrupt();
-       }
-       printk(KERN_INFO "timer: %s interrupt stopped.\n", name);
-}
-
 static struct irqaction irq0 = {
-       .handler        = timer_interrupt,
-       .flags          = IRQF_DISABLED | IRQF_IRQPOLL,
+       .handler        = timer_event_interrupt,
+       .flags          = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING,
        .mask           = CPU_MASK_NONE,
        .name           = "timer"
 };
 
 void __init time_init(void)
 {
-       if (nohpet)
-               hpet_address = 0;
+       if (!hpet_enable())
+               setup_pit_timer();
 
-       if (hpet_arch_init())
-               hpet_address = 0;
+       setup_irq(0, &irq0);
 
-       if (hpet_use_timer) {
-               /* set tick_nsec to use the proper rate for HPET */
-               tick_nsec = TICK_NSEC_HPET;
-               tsc_khz = hpet_calibrate_tsc();
-               timename = "HPET";
-       } else {
-               pit_init();
-               tsc_khz = pit_calibrate_tsc();
-               timename = "PIT";
-       }
+       tsc_calibrate();
 
        cpu_khz = tsc_khz;
        if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) &&
@@ -398,50 +287,7 @@ void __init time_init(void)
        else
                vgetcpu_mode = VGETCPU_LSL;
 
-       set_cyc2ns_scale(tsc_khz);
        printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
                cpu_khz / 1000, cpu_khz % 1000);
        init_tsc_clocksource();
-
-       setup_irq(0, &irq0);
-}
-
-/*
- * sysfs support for the timer.
- */
-
-static int timer_suspend(struct sys_device *dev, pm_message_t state)
-{
-       return 0;
-}
-
-static int timer_resume(struct sys_device *dev)
-{
-       if (hpet_address)
-               hpet_reenable();
-       else
-               i8254_timer_resume();
-       return 0;
-}
-
-static struct sysdev_class timer_sysclass = {
-       .resume = timer_resume,
-       .suspend = timer_suspend,
-       set_kset_name("timer"),
-};
-
-/* XXX this sysfs stuff should probably go elsewhere later -john */
-static struct sys_device device_timer = {
-       .id     = 0,
-       .cls    = &timer_sysclass,
-};
-
-static int time_init_device(void)
-{
-       int error = sysdev_class_register(&timer_sysclass);
-       if (!error)
-               error = sysdev_register(&device_timer);
-       return error;
 }
-
-device_initcall(time_init_device);