aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorAdrian Huang <adrianhuang0701@gmail.com>2015-07-06 00:19:12 -0400
committerAlexandre Belloni <alexandre.belloni@free-electrons.com>2015-09-05 07:19:08 -0400
commit88b8d33b1c6aadba553c998db91c4b36be0fac52 (patch)
treea9bdfe0d2391f4d279b72b9a72935526ff602551 /drivers/rtc
parent80ca3277bc7f398e3315af996443464dac5d4b88 (diff)
rtc: cmos: Cancel alarm timer if alarm time is equal to now+1 seconds
Steps to reproduce the problem: 1) Enable RTC wake-up option in BIOS Setup 2) Issue one of these commands in the OS: "poweroff" or "shutdown -h now" 3) System will shut down and then reboot automatically Root-cause of the issue: 1) During the shutdown process, the hwclock utility is used to save the system clock to hardware clock (RTC). 2) The hwclock utility invokes ioctl() with RTC_UIE_ON. The kernel configures the RTC alarm for the periodic interrupt (every 1 second). 3) The hwclock uitlity closes the /dev/rtc0 device, and the kernel disables the RTC alarm irq (AIE bit of Register B) via ioctl() with RTC_UIE_OFF. But, the configured alarm time is the current_time + 1. 4) After the next 1 second is elapsed, the AF (alarm interrupt flag) of Register C is set. 5) The S5 handler in BIOS is invoked to configure alarm registers (enable AIE bit and configure alarm date/time). But, BIOS does not clear the previous interrupt status during alarm configuration. Therefore, "AF=AIE=1" causes the rtc device to trigger an interrupt. 6) So, the machine reboots automatically right after shutdown. This patch cancels the alarm timer if the following condictions are met (suggested by Alexandre): 1) The configured alarm time is equal to current_time + 1 seconds. 2) The AIE timer is not in use. The member 'alarm_expires' is introduced in struct cmos_rtc because of the following reasons: 1) The configured alarm time can be retrieved from cmos_read_alarm(), but we need to take the 'wrapped timestamp' and 'time rollover' into consideration. The function __rtc_read_alarm() eliminates the concerns. To avoid the duplicated code in the lower level RTC driver, invoking __rtc_read_alarm from the lower level RTC driver is not encouraged. Moreover, the compilation error 'the undefined __rtc_read_alarm" is observed if the lower level RTC driver is compiled as a kernel module. 2) The uie_rtctimer.node.expires and aie_timer.node.expires can be retrieved for the configured alarm time. But, the problem is that either of them might configure the CMOS alarm time. We cannot make sure UIE timer or AIE tiemr configured the CMOS alarm time before. (uie_rtctimer or aie_timer is enabled and then is disabled). 3) The patch introduces the member 'alarm_expires' to keep the newly configured alarm time, so the above-mentioned concerns can be eliminated. The issue goes away after 20-time shutdown tests. Signed-off-by: Adrian Huang <ahuang12@lenovo.com> Tested-by: Egbert Eich <eich@suse.de> Tested-by: Diego Ercolani <diego.ercolani@gmail.com> Cc: Borislav Petkov <bp@suse.de> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/rtc-cmos.c64
1 files changed, 60 insertions, 4 deletions
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index a82556a0757a..5ac9a5da8522 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -51,6 +51,7 @@ struct cmos_rtc {
51 struct device *dev; 51 struct device *dev;
52 int irq; 52 int irq;
53 struct resource *iomem; 53 struct resource *iomem;
54 time64_t alarm_expires;
54 55
55 void (*wake_on)(struct device *); 56 void (*wake_on)(struct device *);
56 void (*wake_off)(struct device *); 57 void (*wake_off)(struct device *);
@@ -377,6 +378,8 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
377 378
378 spin_unlock_irq(&rtc_lock); 379 spin_unlock_irq(&rtc_lock);
379 380
381 cmos->alarm_expires = rtc_tm_to_time64(&t->time);
382
380 return 0; 383 return 0;
381} 384}
382 385
@@ -860,6 +863,51 @@ static void __exit cmos_do_remove(struct device *dev)
860 cmos->dev = NULL; 863 cmos->dev = NULL;
861} 864}
862 865
866static int cmos_aie_poweroff(struct device *dev)
867{
868 struct cmos_rtc *cmos = dev_get_drvdata(dev);
869 struct rtc_time now;
870 time64_t t_now;
871 int retval = 0;
872 unsigned char rtc_control;
873
874 if (!cmos->alarm_expires)
875 return -EINVAL;
876
877 spin_lock_irq(&rtc_lock);
878 rtc_control = CMOS_READ(RTC_CONTROL);
879 spin_unlock_irq(&rtc_lock);
880
881 /* We only care about the situation where AIE is disabled. */
882 if (rtc_control & RTC_AIE)
883 return -EBUSY;
884
885 cmos_read_time(dev, &now);
886 t_now = rtc_tm_to_time64(&now);
887
888 /*
889 * When enabling "RTC wake-up" in BIOS setup, the machine reboots
890 * automatically right after shutdown on some buggy boxes.
891 * This automatic rebooting issue won't happen when the alarm
892 * time is larger than now+1 seconds.
893 *
894 * If the alarm time is equal to now+1 seconds, the issue can be
895 * prevented by cancelling the alarm.
896 */
897 if (cmos->alarm_expires == t_now + 1) {
898 struct rtc_wkalrm alarm;
899
900 /* Cancel the AIE timer by configuring the past time. */
901 rtc_time64_to_tm(t_now - 1, &alarm.time);
902 alarm.enabled = 0;
903 retval = cmos_set_alarm(dev, &alarm);
904 } else if (cmos->alarm_expires > t_now + 1) {
905 retval = -EBUSY;
906 }
907
908 return retval;
909}
910
863#ifdef CONFIG_PM 911#ifdef CONFIG_PM
864 912
865static int cmos_suspend(struct device *dev) 913static int cmos_suspend(struct device *dev)
@@ -1094,8 +1142,12 @@ static void cmos_pnp_shutdown(struct pnp_dev *pnp)
1094 struct device *dev = &pnp->dev; 1142 struct device *dev = &pnp->dev;
1095 struct cmos_rtc *cmos = dev_get_drvdata(dev); 1143 struct cmos_rtc *cmos = dev_get_drvdata(dev);
1096 1144
1097 if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(dev)) 1145 if (system_state == SYSTEM_POWER_OFF) {
1098 return; 1146 int retval = cmos_poweroff(dev);
1147
1148 if (cmos_aie_poweroff(dev) < 0 && !retval)
1149 return;
1150 }
1099 1151
1100 cmos_do_shutdown(cmos->irq); 1152 cmos_do_shutdown(cmos->irq);
1101} 1153}
@@ -1200,8 +1252,12 @@ static void cmos_platform_shutdown(struct platform_device *pdev)
1200 struct device *dev = &pdev->dev; 1252 struct device *dev = &pdev->dev;
1201 struct cmos_rtc *cmos = dev_get_drvdata(dev); 1253 struct cmos_rtc *cmos = dev_get_drvdata(dev);
1202 1254
1203 if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(dev)) 1255 if (system_state == SYSTEM_POWER_OFF) {
1204 return; 1256 int retval = cmos_poweroff(dev);
1257
1258 if (cmos_aie_poweroff(dev) < 0 && !retval)
1259 return;
1260 }
1205 1261
1206 cmos_do_shutdown(cmos->irq); 1262 cmos_do_shutdown(cmos->irq);
1207} 1263}