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 <mlord@pobox.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2007-10-17 23:18:32 -04:00 committed by Linus Torvalds
parent c9927c2bf4
commit c67c36e4b8
1 changed files with 29 additions and 37 deletions

View File

@ -194,6 +194,23 @@ static int get_date_field(char **p, u32 * value)
return result; 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 static ssize_t
acpi_system_write_alarm(struct file *file, acpi_system_write_alarm(struct file *file,
const char __user * buffer, size_t count, loff_t * ppos) 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); spin_lock_irq(&rtc_lock);
rtc_control = CMOS_READ(RTC_CONTROL); 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) { if (adjust) {
yr += CMOS_READ(RTC_YEAR); yr += cmos_bcd_read(RTC_YEAR, rtc_control);
mo += CMOS_READ(RTC_MONTH); mo += cmos_bcd_read(RTC_MONTH, rtc_control);
day += CMOS_READ(RTC_DAY_OF_MONTH); day += cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
hr += CMOS_READ(RTC_HOURS); hr += cmos_bcd_read(RTC_HOURS, rtc_control);
min += CMOS_READ(RTC_MINUTES); min += cmos_bcd_read(RTC_MINUTES, rtc_control);
sec += CMOS_READ(RTC_SECONDS); sec += cmos_bcd_read(RTC_SECONDS, rtc_control);
} }
spin_unlock_irq(&rtc_lock); 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) { if (sec > 59) {
min++; min++;
sec -= 60; sec -= 60;
@ -307,14 +307,6 @@ acpi_system_write_alarm(struct file *file,
yr++; yr++;
mo -= 12; 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); spin_lock_irq(&rtc_lock);
/* /*
@ -326,9 +318,9 @@ acpi_system_write_alarm(struct file *file,
CMOS_READ(RTC_INTR_FLAGS); CMOS_READ(RTC_INTR_FLAGS);
/* write the fields the rtc knows about */ /* write the fields the rtc knows about */
CMOS_WRITE(hr, RTC_HOURS_ALARM); cmos_bcd_write(hr, RTC_HOURS_ALARM, rtc_control);
CMOS_WRITE(min, RTC_MINUTES_ALARM); cmos_bcd_write(min, RTC_MINUTES_ALARM, rtc_control);
CMOS_WRITE(sec, RTC_SECONDS_ALARM); cmos_bcd_write(sec, RTC_SECONDS_ALARM, rtc_control);
/* /*
* If the system supports an enhanced alarm it will have non-zero * 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. * to the RTC area of memory.
*/ */
if (acpi_gbl_FADT.day_alarm) 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) 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) 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 */ /* enable the rtc alarm interrupt */
rtc_control |= RTC_AIE; rtc_control |= RTC_AIE;
CMOS_WRITE(rtc_control, RTC_CONTROL); CMOS_WRITE(rtc_control, RTC_CONTROL);