aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2007-10-17 23:18:32 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-25 18:08:03 -0400
commitc67c36e4b86872ac875716d502748b84b2541de5 (patch)
tree3b1f3d846cff947d0a12d4b3c7213192792ea675
parentc9927c2bf4f45bb85e8b502ab3fb79ad6483c244 (diff)
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>
-rw-r--r--drivers/acpi/sleep/proc.c66
1 files changed, 29 insertions, 37 deletions
diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c
index 3839efd5eaea..1538355c266b 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)
194 return result; 194 return result;
195} 195}
196 196
197/* Read a possibly BCD register, always return binary */
198static u32 cmos_bcd_read(int offset, int rtc_control)
199{
200 u32 val = CMOS_READ(offset);
201 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
202 BCD_TO_BIN(val);
203 return val;
204}
205
206/* Write binary value into possibly BCD register */
207static void cmos_bcd_write(u32 val, int offset, int rtc_control)
208{
209 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
210 BIN_TO_BCD(val);
211 CMOS_WRITE(val, offset);
212}
213
197static ssize_t 214static ssize_t
198acpi_system_write_alarm(struct file *file, 215acpi_system_write_alarm(struct file *file,
199 const char __user * buffer, size_t count, loff_t * ppos) 216 const char __user * buffer, size_t count, loff_t * ppos)
@@ -258,35 +275,18 @@ acpi_system_write_alarm(struct file *file,
258 spin_lock_irq(&rtc_lock); 275 spin_lock_irq(&rtc_lock);
259 276
260 rtc_control = CMOS_READ(RTC_CONTROL); 277 rtc_control = CMOS_READ(RTC_CONTROL);
261 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
262 BIN_TO_BCD(yr);
263 BIN_TO_BCD(mo);
264 BIN_TO_BCD(day);
265 BIN_TO_BCD(hr);
266 BIN_TO_BCD(min);
267 BIN_TO_BCD(sec);
268 }
269 278
270 if (adjust) { 279 if (adjust) {
271 yr += CMOS_READ(RTC_YEAR); 280 yr += cmos_bcd_read(RTC_YEAR, rtc_control);
272 mo += CMOS_READ(RTC_MONTH); 281 mo += cmos_bcd_read(RTC_MONTH, rtc_control);
273 day += CMOS_READ(RTC_DAY_OF_MONTH); 282 day += cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
274 hr += CMOS_READ(RTC_HOURS); 283 hr += cmos_bcd_read(RTC_HOURS, rtc_control);
275 min += CMOS_READ(RTC_MINUTES); 284 min += cmos_bcd_read(RTC_MINUTES, rtc_control);
276 sec += CMOS_READ(RTC_SECONDS); 285 sec += cmos_bcd_read(RTC_SECONDS, rtc_control);
277 } 286 }
278 287
279 spin_unlock_irq(&rtc_lock); 288 spin_unlock_irq(&rtc_lock);
280 289
281 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
282 BCD_TO_BIN(yr);
283 BCD_TO_BIN(mo);
284 BCD_TO_BIN(day);
285 BCD_TO_BIN(hr);
286 BCD_TO_BIN(min);
287 BCD_TO_BIN(sec);
288 }
289
290 if (sec > 59) { 290 if (sec > 59) {
291 min++; 291 min++;
292 sec -= 60; 292 sec -= 60;
@@ -307,14 +307,6 @@ acpi_system_write_alarm(struct file *file,
307 yr++; 307 yr++;
308 mo -= 12; 308 mo -= 12;
309 } 309 }
310 if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
311 BIN_TO_BCD(yr);
312 BIN_TO_BCD(mo);
313 BIN_TO_BCD(day);
314 BIN_TO_BCD(hr);
315 BIN_TO_BCD(min);
316 BIN_TO_BCD(sec);
317 }
318 310
319 spin_lock_irq(&rtc_lock); 311 spin_lock_irq(&rtc_lock);
320 /* 312 /*
@@ -326,9 +318,9 @@ acpi_system_write_alarm(struct file *file,
326 CMOS_READ(RTC_INTR_FLAGS); 318 CMOS_READ(RTC_INTR_FLAGS);
327 319
328 /* write the fields the rtc knows about */ 320 /* write the fields the rtc knows about */
329 CMOS_WRITE(hr, RTC_HOURS_ALARM); 321 cmos_bcd_write(hr, RTC_HOURS_ALARM, rtc_control);
330 CMOS_WRITE(min, RTC_MINUTES_ALARM); 322 cmos_bcd_write(min, RTC_MINUTES_ALARM, rtc_control);
331 CMOS_WRITE(sec, RTC_SECONDS_ALARM); 323 cmos_bcd_write(sec, RTC_SECONDS_ALARM, rtc_control);
332 324
333 /* 325 /*
334 * If the system supports an enhanced alarm it will have non-zero 326 * If the system supports an enhanced alarm it will have non-zero
@@ -336,11 +328,11 @@ acpi_system_write_alarm(struct file *file,
336 * to the RTC area of memory. 328 * to the RTC area of memory.
337 */ 329 */
338 if (acpi_gbl_FADT.day_alarm) 330 if (acpi_gbl_FADT.day_alarm)
339 CMOS_WRITE(day, acpi_gbl_FADT.day_alarm); 331 cmos_bcd_write(day, acpi_gbl_FADT.day_alarm, rtc_control);
340 if (acpi_gbl_FADT.month_alarm) 332 if (acpi_gbl_FADT.month_alarm)
341 CMOS_WRITE(mo, acpi_gbl_FADT.month_alarm); 333 cmos_bcd_write(mo, acpi_gbl_FADT.month_alarm, rtc_control);
342 if (acpi_gbl_FADT.century) 334 if (acpi_gbl_FADT.century)
343 CMOS_WRITE(yr / 100, acpi_gbl_FADT.century); 335 cmos_bcd_write(yr / 100, acpi_gbl_FADT.century, rtc_control);
344 /* enable the rtc alarm interrupt */ 336 /* enable the rtc alarm interrupt */
345 rtc_control |= RTC_AIE; 337 rtc_control |= RTC_AIE;
346 CMOS_WRITE(rtc_control, RTC_CONTROL); 338 CMOS_WRITE(rtc_control, RTC_CONTROL);