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:
parent
c9927c2bf4
commit
c67c36e4b8
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue