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 e0134d6c88da045ac23efe0f0cf439781a14ebaf..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
@@ -84,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);
 
@@ -140,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;
 }
@@ -166,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);
 
        /*