| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * RTC class driver for "CMOS RTC": PCs, ACPI, etc |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 1996 Paul Gortmaker (drivers/char/rtc.c) |
|---|
| 5 | 6 | * Copyright (C) 2006 David Brownell (convert to new framework) |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or |
|---|
| 8 | | - * modify it under the terms of the GNU General Public License |
|---|
| 9 | | - * as published by the Free Software Foundation; either version |
|---|
| 10 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 11 | 7 | */ |
|---|
| 12 | 8 | |
|---|
| 13 | 9 | /* |
|---|
| .. | .. |
|---|
| 226 | 222 | |
|---|
| 227 | 223 | static int cmos_read_time(struct device *dev, struct rtc_time *t) |
|---|
| 228 | 224 | { |
|---|
| 225 | + int ret; |
|---|
| 226 | + |
|---|
| 229 | 227 | /* |
|---|
| 230 | 228 | * If pm_trace abused the RTC for storage, set the timespec to 0, |
|---|
| 231 | 229 | * which tells the caller that this RTC value is unusable. |
|---|
| .. | .. |
|---|
| 233 | 231 | if (!pm_trace_rtc_valid()) |
|---|
| 234 | 232 | return -EIO; |
|---|
| 235 | 233 | |
|---|
| 236 | | - /* REVISIT: if the clock has a "century" register, use |
|---|
| 237 | | - * that instead of the heuristic in mc146818_get_time(). |
|---|
| 238 | | - * That'll make Y3K compatility (year > 2070) easy! |
|---|
| 239 | | - */ |
|---|
| 240 | | - mc146818_get_time(t); |
|---|
| 234 | + ret = mc146818_get_time(t); |
|---|
| 235 | + if (ret < 0) { |
|---|
| 236 | + dev_err_ratelimited(dev, "unable to read current time\n"); |
|---|
| 237 | + return ret; |
|---|
| 238 | + } |
|---|
| 239 | + |
|---|
| 241 | 240 | return 0; |
|---|
| 242 | 241 | } |
|---|
| 243 | 242 | |
|---|
| 244 | 243 | static int cmos_set_time(struct device *dev, struct rtc_time *t) |
|---|
| 245 | 244 | { |
|---|
| 246 | | - /* REVISIT: set the "century" register if available |
|---|
| 247 | | - * |
|---|
| 248 | | - * NOTE: this ignores the issue whereby updating the seconds |
|---|
| 245 | + /* NOTE: this ignores the issue whereby updating the seconds |
|---|
| 249 | 246 | * takes effect exactly 500ms after we write the register. |
|---|
| 250 | 247 | * (Also queueing and other delays before we get this far.) |
|---|
| 251 | 248 | */ |
|---|
| 252 | 249 | return mc146818_set_time(t); |
|---|
| 253 | 250 | } |
|---|
| 254 | 251 | |
|---|
| 252 | +struct cmos_read_alarm_callback_param { |
|---|
| 253 | + struct cmos_rtc *cmos; |
|---|
| 254 | + struct rtc_time *time; |
|---|
| 255 | + unsigned char rtc_control; |
|---|
| 256 | +}; |
|---|
| 257 | + |
|---|
| 258 | +static void cmos_read_alarm_callback(unsigned char __always_unused seconds, |
|---|
| 259 | + void *param_in) |
|---|
| 260 | +{ |
|---|
| 261 | + struct cmos_read_alarm_callback_param *p = |
|---|
| 262 | + (struct cmos_read_alarm_callback_param *)param_in; |
|---|
| 263 | + struct rtc_time *time = p->time; |
|---|
| 264 | + |
|---|
| 265 | + time->tm_sec = CMOS_READ(RTC_SECONDS_ALARM); |
|---|
| 266 | + time->tm_min = CMOS_READ(RTC_MINUTES_ALARM); |
|---|
| 267 | + time->tm_hour = CMOS_READ(RTC_HOURS_ALARM); |
|---|
| 268 | + |
|---|
| 269 | + if (p->cmos->day_alrm) { |
|---|
| 270 | + /* ignore upper bits on readback per ACPI spec */ |
|---|
| 271 | + time->tm_mday = CMOS_READ(p->cmos->day_alrm) & 0x3f; |
|---|
| 272 | + if (!time->tm_mday) |
|---|
| 273 | + time->tm_mday = -1; |
|---|
| 274 | + |
|---|
| 275 | + if (p->cmos->mon_alrm) { |
|---|
| 276 | + time->tm_mon = CMOS_READ(p->cmos->mon_alrm); |
|---|
| 277 | + if (!time->tm_mon) |
|---|
| 278 | + time->tm_mon = -1; |
|---|
| 279 | + } |
|---|
| 280 | + } |
|---|
| 281 | + |
|---|
| 282 | + p->rtc_control = CMOS_READ(RTC_CONTROL); |
|---|
| 283 | +} |
|---|
| 284 | + |
|---|
| 255 | 285 | static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t) |
|---|
| 256 | 286 | { |
|---|
| 257 | 287 | struct cmos_rtc *cmos = dev_get_drvdata(dev); |
|---|
| 258 | | - unsigned char rtc_control; |
|---|
| 288 | + struct cmos_read_alarm_callback_param p = { |
|---|
| 289 | + .cmos = cmos, |
|---|
| 290 | + .time = &t->time, |
|---|
| 291 | + }; |
|---|
| 259 | 292 | |
|---|
| 260 | 293 | /* This not only a rtc_op, but also called directly */ |
|---|
| 261 | 294 | if (!is_valid_irq(cmos->irq)) |
|---|
| .. | .. |
|---|
| 266 | 299 | * the future. |
|---|
| 267 | 300 | */ |
|---|
| 268 | 301 | |
|---|
| 269 | | - spin_lock_irq(&rtc_lock); |
|---|
| 270 | | - t->time.tm_sec = CMOS_READ(RTC_SECONDS_ALARM); |
|---|
| 271 | | - t->time.tm_min = CMOS_READ(RTC_MINUTES_ALARM); |
|---|
| 272 | | - t->time.tm_hour = CMOS_READ(RTC_HOURS_ALARM); |
|---|
| 302 | + /* Some Intel chipsets disconnect the alarm registers when the clock |
|---|
| 303 | + * update is in progress - during this time reads return bogus values |
|---|
| 304 | + * and writes may fail silently. See for example "7th Generation IntelĀ® |
|---|
| 305 | + * Processor Family I/O for U/Y Platforms [...] Datasheet", section |
|---|
| 306 | + * 27.7.1 |
|---|
| 307 | + * |
|---|
| 308 | + * Use the mc146818_avoid_UIP() function to avoid this. |
|---|
| 309 | + */ |
|---|
| 310 | + if (!mc146818_avoid_UIP(cmos_read_alarm_callback, &p)) |
|---|
| 311 | + return -EIO; |
|---|
| 273 | 312 | |
|---|
| 274 | | - if (cmos->day_alrm) { |
|---|
| 275 | | - /* ignore upper bits on readback per ACPI spec */ |
|---|
| 276 | | - t->time.tm_mday = CMOS_READ(cmos->day_alrm) & 0x3f; |
|---|
| 277 | | - if (!t->time.tm_mday) |
|---|
| 278 | | - t->time.tm_mday = -1; |
|---|
| 279 | | - |
|---|
| 280 | | - if (cmos->mon_alrm) { |
|---|
| 281 | | - t->time.tm_mon = CMOS_READ(cmos->mon_alrm); |
|---|
| 282 | | - if (!t->time.tm_mon) |
|---|
| 283 | | - t->time.tm_mon = -1; |
|---|
| 284 | | - } |
|---|
| 285 | | - } |
|---|
| 286 | | - |
|---|
| 287 | | - rtc_control = CMOS_READ(RTC_CONTROL); |
|---|
| 288 | | - spin_unlock_irq(&rtc_lock); |
|---|
| 289 | | - |
|---|
| 290 | | - if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { |
|---|
| 313 | + if (!(p.rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { |
|---|
| 291 | 314 | if (((unsigned)t->time.tm_sec) < 0x60) |
|---|
| 292 | 315 | t->time.tm_sec = bcd2bin(t->time.tm_sec); |
|---|
| 293 | 316 | else |
|---|
| .. | .. |
|---|
| 316 | 339 | } |
|---|
| 317 | 340 | } |
|---|
| 318 | 341 | |
|---|
| 319 | | - t->enabled = !!(rtc_control & RTC_AIE); |
|---|
| 342 | + t->enabled = !!(p.rtc_control & RTC_AIE); |
|---|
| 320 | 343 | t->pending = 0; |
|---|
| 321 | 344 | |
|---|
| 322 | 345 | return 0; |
|---|
| .. | .. |
|---|
| 447 | 470 | return 0; |
|---|
| 448 | 471 | } |
|---|
| 449 | 472 | |
|---|
| 473 | +struct cmos_set_alarm_callback_param { |
|---|
| 474 | + struct cmos_rtc *cmos; |
|---|
| 475 | + unsigned char mon, mday, hrs, min, sec; |
|---|
| 476 | + struct rtc_wkalrm *t; |
|---|
| 477 | +}; |
|---|
| 478 | + |
|---|
| 479 | +/* Note: this function may be executed by mc146818_avoid_UIP() more then |
|---|
| 480 | + * once |
|---|
| 481 | + */ |
|---|
| 482 | +static void cmos_set_alarm_callback(unsigned char __always_unused seconds, |
|---|
| 483 | + void *param_in) |
|---|
| 484 | +{ |
|---|
| 485 | + struct cmos_set_alarm_callback_param *p = |
|---|
| 486 | + (struct cmos_set_alarm_callback_param *)param_in; |
|---|
| 487 | + |
|---|
| 488 | + /* next rtc irq must not be from previous alarm setting */ |
|---|
| 489 | + cmos_irq_disable(p->cmos, RTC_AIE); |
|---|
| 490 | + |
|---|
| 491 | + /* update alarm */ |
|---|
| 492 | + CMOS_WRITE(p->hrs, RTC_HOURS_ALARM); |
|---|
| 493 | + CMOS_WRITE(p->min, RTC_MINUTES_ALARM); |
|---|
| 494 | + CMOS_WRITE(p->sec, RTC_SECONDS_ALARM); |
|---|
| 495 | + |
|---|
| 496 | + /* the system may support an "enhanced" alarm */ |
|---|
| 497 | + if (p->cmos->day_alrm) { |
|---|
| 498 | + CMOS_WRITE(p->mday, p->cmos->day_alrm); |
|---|
| 499 | + if (p->cmos->mon_alrm) |
|---|
| 500 | + CMOS_WRITE(p->mon, p->cmos->mon_alrm); |
|---|
| 501 | + } |
|---|
| 502 | + |
|---|
| 503 | + if (use_hpet_alarm()) { |
|---|
| 504 | + /* |
|---|
| 505 | + * FIXME the HPET alarm glue currently ignores day_alrm |
|---|
| 506 | + * and mon_alrm ... |
|---|
| 507 | + */ |
|---|
| 508 | + hpet_set_alarm_time(p->t->time.tm_hour, p->t->time.tm_min, |
|---|
| 509 | + p->t->time.tm_sec); |
|---|
| 510 | + } |
|---|
| 511 | + |
|---|
| 512 | + if (p->t->enabled) |
|---|
| 513 | + cmos_irq_enable(p->cmos, RTC_AIE); |
|---|
| 514 | +} |
|---|
| 515 | + |
|---|
| 450 | 516 | static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) |
|---|
| 451 | 517 | { |
|---|
| 452 | 518 | struct cmos_rtc *cmos = dev_get_drvdata(dev); |
|---|
| 453 | | - unsigned char mon, mday, hrs, min, sec, rtc_control; |
|---|
| 519 | + struct cmos_set_alarm_callback_param p = { |
|---|
| 520 | + .cmos = cmos, |
|---|
| 521 | + .t = t |
|---|
| 522 | + }; |
|---|
| 523 | + unsigned char rtc_control; |
|---|
| 454 | 524 | int ret; |
|---|
| 455 | 525 | |
|---|
| 456 | 526 | /* This not only a rtc_op, but also called directly */ |
|---|
| .. | .. |
|---|
| 461 | 531 | if (ret < 0) |
|---|
| 462 | 532 | return ret; |
|---|
| 463 | 533 | |
|---|
| 464 | | - mon = t->time.tm_mon + 1; |
|---|
| 465 | | - mday = t->time.tm_mday; |
|---|
| 466 | | - hrs = t->time.tm_hour; |
|---|
| 467 | | - min = t->time.tm_min; |
|---|
| 468 | | - sec = t->time.tm_sec; |
|---|
| 534 | + p.mon = t->time.tm_mon + 1; |
|---|
| 535 | + p.mday = t->time.tm_mday; |
|---|
| 536 | + p.hrs = t->time.tm_hour; |
|---|
| 537 | + p.min = t->time.tm_min; |
|---|
| 538 | + p.sec = t->time.tm_sec; |
|---|
| 469 | 539 | |
|---|
| 470 | 540 | spin_lock_irq(&rtc_lock); |
|---|
| 471 | 541 | rtc_control = CMOS_READ(RTC_CONTROL); |
|---|
| .. | .. |
|---|
| 473 | 543 | |
|---|
| 474 | 544 | if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { |
|---|
| 475 | 545 | /* Writing 0xff means "don't care" or "match all". */ |
|---|
| 476 | | - mon = (mon <= 12) ? bin2bcd(mon) : 0xff; |
|---|
| 477 | | - mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff; |
|---|
| 478 | | - hrs = (hrs < 24) ? bin2bcd(hrs) : 0xff; |
|---|
| 479 | | - min = (min < 60) ? bin2bcd(min) : 0xff; |
|---|
| 480 | | - sec = (sec < 60) ? bin2bcd(sec) : 0xff; |
|---|
| 546 | + p.mon = (p.mon <= 12) ? bin2bcd(p.mon) : 0xff; |
|---|
| 547 | + p.mday = (p.mday >= 1 && p.mday <= 31) ? bin2bcd(p.mday) : 0xff; |
|---|
| 548 | + p.hrs = (p.hrs < 24) ? bin2bcd(p.hrs) : 0xff; |
|---|
| 549 | + p.min = (p.min < 60) ? bin2bcd(p.min) : 0xff; |
|---|
| 550 | + p.sec = (p.sec < 60) ? bin2bcd(p.sec) : 0xff; |
|---|
| 481 | 551 | } |
|---|
| 482 | 552 | |
|---|
| 483 | | - spin_lock_irq(&rtc_lock); |
|---|
| 484 | | - |
|---|
| 485 | | - /* next rtc irq must not be from previous alarm setting */ |
|---|
| 486 | | - cmos_irq_disable(cmos, RTC_AIE); |
|---|
| 487 | | - |
|---|
| 488 | | - /* update alarm */ |
|---|
| 489 | | - CMOS_WRITE(hrs, RTC_HOURS_ALARM); |
|---|
| 490 | | - CMOS_WRITE(min, RTC_MINUTES_ALARM); |
|---|
| 491 | | - CMOS_WRITE(sec, RTC_SECONDS_ALARM); |
|---|
| 492 | | - |
|---|
| 493 | | - /* the system may support an "enhanced" alarm */ |
|---|
| 494 | | - if (cmos->day_alrm) { |
|---|
| 495 | | - CMOS_WRITE(mday, cmos->day_alrm); |
|---|
| 496 | | - if (cmos->mon_alrm) |
|---|
| 497 | | - CMOS_WRITE(mon, cmos->mon_alrm); |
|---|
| 498 | | - } |
|---|
| 499 | | - |
|---|
| 500 | | - if (use_hpet_alarm()) { |
|---|
| 501 | | - /* |
|---|
| 502 | | - * FIXME the HPET alarm glue currently ignores day_alrm |
|---|
| 503 | | - * and mon_alrm ... |
|---|
| 504 | | - */ |
|---|
| 505 | | - hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, |
|---|
| 506 | | - t->time.tm_sec); |
|---|
| 507 | | - } |
|---|
| 508 | | - |
|---|
| 509 | | - if (t->enabled) |
|---|
| 510 | | - cmos_irq_enable(cmos, RTC_AIE); |
|---|
| 511 | | - |
|---|
| 512 | | - spin_unlock_irq(&rtc_lock); |
|---|
| 553 | + /* |
|---|
| 554 | + * Some Intel chipsets disconnect the alarm registers when the clock |
|---|
| 555 | + * update is in progress - during this time writes fail silently. |
|---|
| 556 | + * |
|---|
| 557 | + * Use mc146818_avoid_UIP() to avoid this. |
|---|
| 558 | + */ |
|---|
| 559 | + if (!mc146818_avoid_UIP(cmos_set_alarm_callback, &p)) |
|---|
| 560 | + return -EIO; |
|---|
| 513 | 561 | |
|---|
| 514 | 562 | cmos->alarm_expires = rtc_tm_to_time64(&t->time); |
|---|
| 515 | 563 | |
|---|
| .. | .. |
|---|
| 809 | 857 | |
|---|
| 810 | 858 | rename_region(ports, dev_name(&cmos_rtc.rtc->dev)); |
|---|
| 811 | 859 | |
|---|
| 860 | + if (!mc146818_does_rtc_work()) { |
|---|
| 861 | + dev_warn(dev, "broken or not accessible\n"); |
|---|
| 862 | + retval = -ENXIO; |
|---|
| 863 | + goto cleanup1; |
|---|
| 864 | + } |
|---|
| 865 | + |
|---|
| 812 | 866 | spin_lock_irq(&rtc_lock); |
|---|
| 813 | 867 | |
|---|
| 814 | 868 | if (!(flags & CMOS_RTC_FLAGS_NOFREQ)) { |
|---|
| .. | .. |
|---|
| 1012 | 1066 | enable_irq_wake(cmos->irq); |
|---|
| 1013 | 1067 | } |
|---|
| 1014 | 1068 | |
|---|
| 1069 | + memset(&cmos->saved_wkalrm, 0, sizeof(struct rtc_wkalrm)); |
|---|
| 1015 | 1070 | cmos_read_alarm(dev, &cmos->saved_wkalrm); |
|---|
| 1016 | 1071 | |
|---|
| 1017 | 1072 | dev_dbg(dev, "suspend%s, ctrl %02x\n", |
|---|
| .. | .. |
|---|
| 1056 | 1111 | * ACK the rtc irq here |
|---|
| 1057 | 1112 | */ |
|---|
| 1058 | 1113 | if (t_now >= cmos->alarm_expires && cmos_use_acpi_alarm()) { |
|---|
| 1114 | + local_irq_disable(); |
|---|
| 1059 | 1115 | cmos_interrupt(0, (void *)cmos->rtc); |
|---|
| 1116 | + local_irq_enable(); |
|---|
| 1060 | 1117 | return; |
|---|
| 1061 | 1118 | } |
|---|
| 1062 | 1119 | |
|---|
| 1120 | + memset(¤t_alarm, 0, sizeof(struct rtc_wkalrm)); |
|---|
| 1063 | 1121 | cmos_read_alarm(dev, ¤t_alarm); |
|---|
| 1064 | 1122 | t_current_expires = rtc_tm_to_time64(¤t_alarm.time); |
|---|
| 1065 | 1123 | t_saved_expires = rtc_tm_to_time64(&cmos->saved_wkalrm.time); |
|---|
| .. | .. |
|---|
| 1204 | 1262 | /* Enable use_acpi_alarm mode for Intel platforms no earlier than 2015 */ |
|---|
| 1205 | 1263 | static void use_acpi_alarm_quirks(void) |
|---|
| 1206 | 1264 | { |
|---|
| 1207 | | - int year; |
|---|
| 1208 | | - |
|---|
| 1209 | 1265 | if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) |
|---|
| 1210 | 1266 | return; |
|---|
| 1211 | 1267 | |
|---|
| .. | .. |
|---|
| 1215 | 1271 | if (!is_hpet_enabled()) |
|---|
| 1216 | 1272 | return; |
|---|
| 1217 | 1273 | |
|---|
| 1218 | | - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year >= 2015) |
|---|
| 1219 | | - use_acpi_alarm = true; |
|---|
| 1274 | + if (dmi_get_bios_year() < 2015) |
|---|
| 1275 | + return; |
|---|
| 1276 | + |
|---|
| 1277 | + use_acpi_alarm = true; |
|---|
| 1220 | 1278 | } |
|---|
| 1221 | 1279 | #else |
|---|
| 1222 | 1280 | static inline void use_acpi_alarm_quirks(void) { } |
|---|
| .. | .. |
|---|
| 1312 | 1370 | * hardcode it on systems with a legacy PIC. |
|---|
| 1313 | 1371 | */ |
|---|
| 1314 | 1372 | if (nr_legacy_irqs()) |
|---|
| 1315 | | - irq = 8; |
|---|
| 1373 | + irq = RTC_IRQ; |
|---|
| 1316 | 1374 | #endif |
|---|
| 1317 | 1375 | return cmos_do_probe(&pnp->dev, |
|---|
| 1318 | 1376 | pnp_get_resource(pnp, IORESOURCE_IO, 0), irq); |
|---|
| .. | .. |
|---|
| 1352 | 1410 | MODULE_DEVICE_TABLE(pnp, rtc_ids); |
|---|
| 1353 | 1411 | |
|---|
| 1354 | 1412 | static struct pnp_driver cmos_pnp_driver = { |
|---|
| 1355 | | - .name = (char *) driver_name, |
|---|
| 1413 | + .name = driver_name, |
|---|
| 1356 | 1414 | .id_table = rtc_ids, |
|---|
| 1357 | 1415 | .probe = cmos_pnp_probe, |
|---|
| 1358 | 1416 | .remove = cmos_pnp_remove, |
|---|