Fix /proc/acpi/alarm BCD alarm encodings
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 18 Oct 2007 03:18:32 +0000 (23:18 -0400)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Thu, 25 Oct 2007 22:08:03 +0000 (15:08 -0700)
This fixes some totally illogical and wrong code that converts things to
and from BCD mode essentially randomly, does math on values in BCD mode
etc etc.  Introduce a few helper functions to make it a bit more obvious
what is going on, and make sure that we always do all the arithmetic
(and anythign else, for that matter) in binary, not BCD.

Tested by Mark Lord, who found the problem originally, and also pushed
the patch back and reminded me about it.

Signed-off-by: Mark Lord <mlord@pobox.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/acpi/sleep/proc.c

index 3839efd..1538355 100644 (file)
@@ -194,6 +194,23 @@ static int get_date_field(char **p, u32 * value)
        return result;
 }
 
+/* Read a possibly BCD register, always return binary */
+static u32 cmos_bcd_read(int offset, int rtc_control)
+{
+       u32 val = CMOS_READ(offset);
+       if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+               BCD_TO_BIN(val);
+       return val;
+}
+
+/* Write binary value into possibly BCD register */
+static void cmos_bcd_write(u32 val, int offset, int rtc_control)
+{
+       if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+               BIN_TO_BCD(val);
+       CMOS_WRITE(val, offset);
+}
+
 static ssize_t
 acpi_system_write_alarm(struct file *file,
                        const char __user * buffer, size_t count, loff_t * ppos)
@@ -258,35 +275,18 @@ acpi_system_write_alarm(struct file *file,
        spin_lock_irq(&rtc_lock);
 
        rtc_control = CMOS_READ(RTC_CONTROL);
-       if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
-               BIN_TO_BCD(yr);
-               BIN_TO_BCD(mo);
-               BIN_TO_BCD(day);
-               BIN_TO_BCD(hr);
-               BIN_TO_BCD(min);
-               BIN_TO_BCD(sec);
-       }
 
        if (adjust) {
-               yr += CMOS_READ(RTC_YEAR);
-               mo += CMOS_READ(RTC_MONTH);
-               day += CMOS_READ(RTC_DAY_OF_MONTH);
-               hr += CMOS_READ(RTC_HOURS);
-               min += CMOS_READ(RTC_MINUTES);
-               sec += CMOS_READ(RTC_SECONDS);
+               yr += cmos_bcd_read(RTC_YEAR, rtc_control);
+               mo += cmos_bcd_read(RTC_MONTH, rtc_control);
+               day += cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
+               hr += cmos_bcd_read(RTC_HOURS, rtc_control);
+               min += cmos_bcd_read(RTC_MINUTES, rtc_control);
+               sec += cmos_bcd_read(RTC_SECONDS, rtc_control);
        }
 
        spin_unlock_irq(&rtc_lock);
 
-       if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
-               BCD_TO_BIN(yr);
-               BCD_TO_BIN(mo);
-               BCD_TO_BIN(day);
-               BCD_TO_BIN(hr);
-               BCD_TO_BIN(min);
-               BCD_TO_BIN(sec);
-       }
-
        if (sec > 59) {
                min++;
                sec -= 60;
@@ -307,14 +307,6 @@ acpi_system_write_alarm(struct file *file,
                yr++;
                mo -= 12;
        }
-       if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
-               BIN_TO_BCD(yr);
-               BIN_TO_BCD(mo);
-               BIN_TO_BCD(day);
-               BIN_TO_BCD(hr);
-               BIN_TO_BCD(min);
-               BIN_TO_BCD(sec);
-       }
 
        spin_lock_irq(&rtc_lock);
        /*
@@ -326,9 +318,9 @@ acpi_system_write_alarm(struct file *file,
        CMOS_READ(RTC_INTR_FLAGS);
 
        /* write the fields the rtc knows about */
-       CMOS_WRITE(hr, RTC_HOURS_ALARM);
-       CMOS_WRITE(min, RTC_MINUTES_ALARM);
-       CMOS_WRITE(sec, RTC_SECONDS_ALARM);
+       cmos_bcd_write(hr, RTC_HOURS_ALARM, rtc_control);
+       cmos_bcd_write(min, RTC_MINUTES_ALARM, rtc_control);
+       cmos_bcd_write(sec, RTC_SECONDS_ALARM, rtc_control);
 
        /*
         * If the system supports an enhanced alarm it will have non-zero
@@ -336,11 +328,11 @@ acpi_system_write_alarm(struct file *file,
         * to the RTC area of memory.
         */
        if (acpi_gbl_FADT.day_alarm)
-               CMOS_WRITE(day, acpi_gbl_FADT.day_alarm);
+               cmos_bcd_write(day, acpi_gbl_FADT.day_alarm, rtc_control);
        if (acpi_gbl_FADT.month_alarm)
-               CMOS_WRITE(mo, acpi_gbl_FADT.month_alarm);
+               cmos_bcd_write(mo, acpi_gbl_FADT.month_alarm, rtc_control);
        if (acpi_gbl_FADT.century)
-               CMOS_WRITE(yr / 100, acpi_gbl_FADT.century);
+               cmos_bcd_write(yr / 100, acpi_gbl_FADT.century, rtc_control);
        /* enable the rtc alarm interrupt */
        rtc_control |= RTC_AIE;
        CMOS_WRITE(rtc_control, RTC_CONTROL);