Commit c67c36e4 authored by Linus Torvalds's avatar Linus Torvalds Committed by Linus Torvalds

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: default avatarMark Lord <mlord@pobox.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent c9927c2b
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment