From c67c36e4b86872ac875716d502748b84b2541de5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 17 Oct 2007 23:18:32 -0400 Subject: Fix /proc/acpi/alarm BCD alarm encodings 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 Signed-off-by: Linus Torvalds --- drivers/acpi/sleep/proc.c | 66 +++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 37 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 3839efd5eae..1538355c266 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -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); -- cgit v1.2.3