#include <linux/pm.h>
#include <linux/of.h>
#include <linux/of_platform.h>
-#include <linux/dmi.h>
/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
#include <asm-generic/rtc.h>
struct device *dev;
int irq;
struct resource *iomem;
+ time64_t alarm_expires;
void (*wake_on)(struct device *);
void (*wake_off)(struct device *);
spin_unlock_irq(&rtc_lock);
- return 0;
-}
-
-/*
- * Do not disable RTC alarm on shutdown - workaround for b0rked BIOSes.
- */
-static bool alarm_disable_quirk;
+ cmos->alarm_expires = rtc_tm_to_time64(&t->time);
-static int __init set_alarm_disable_quirk(const struct dmi_system_id *id)
-{
- alarm_disable_quirk = true;
- pr_info("BIOS has alarm-disable quirk - RTC alarms disabled\n");
return 0;
}
-static const struct dmi_system_id rtc_quirks[] __initconst = {
- /* https://bugzilla.novell.com/show_bug.cgi?id=805740 */
- {
- .callback = set_alarm_disable_quirk,
- .ident = "IBM Truman",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
- DMI_MATCH(DMI_PRODUCT_NAME, "4852570"),
- },
- },
- /* https://bugzilla.novell.com/show_bug.cgi?id=812592 */
- {
- .callback = set_alarm_disable_quirk,
- .ident = "Gigabyte GA-990XA-UD3",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "Gigabyte Technology Co., Ltd."),
- DMI_MATCH(DMI_PRODUCT_NAME, "GA-990XA-UD3"),
- },
- },
- /* http://permalink.gmane.org/gmane.linux.kernel/1604474 */
- {
- .callback = set_alarm_disable_quirk,
- .ident = "Toshiba Satellite L300",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Satellite L300"),
- },
- },
- {}
-};
-
static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
if (!is_valid_irq(cmos->irq))
return -EINVAL;
- if (alarm_disable_quirk)
- return 0;
-
spin_lock_irqsave(&rtc_lock, flags);
if (enabled)
{
int retval;
- if (unlikely(off >= attr->size))
- return 0;
- if (unlikely(off < 0))
- return -EINVAL;
- if ((off + count) > attr->size)
- count = attr->size - off;
-
off += NVRAM_OFFSET;
spin_lock_irq(&rtc_lock);
for (retval = 0; count; count--, off++, retval++) {
int retval;
cmos = dev_get_drvdata(container_of(kobj, struct device, kobj));
- if (unlikely(off >= attr->size))
- return -EFBIG;
- if (unlikely(off < 0))
- return -EINVAL;
- if ((off + count) > attr->size)
- count = attr->size - off;
/* NOTE: on at least PCs and Ataris, the boot firmware uses a
* checksum on part of the NVRAM data. That's currently ignored
cmos->dev = NULL;
}
+static int cmos_aie_poweroff(struct device *dev)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ struct rtc_time now;
+ time64_t t_now;
+ int retval = 0;
+ unsigned char rtc_control;
+
+ if (!cmos->alarm_expires)
+ return -EINVAL;
+
+ spin_lock_irq(&rtc_lock);
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ spin_unlock_irq(&rtc_lock);
+
+ /* We only care about the situation where AIE is disabled. */
+ if (rtc_control & RTC_AIE)
+ return -EBUSY;
+
+ cmos_read_time(dev, &now);
+ t_now = rtc_tm_to_time64(&now);
+
+ /*
+ * When enabling "RTC wake-up" in BIOS setup, the machine reboots
+ * automatically right after shutdown on some buggy boxes.
+ * This automatic rebooting issue won't happen when the alarm
+ * time is larger than now+1 seconds.
+ *
+ * If the alarm time is equal to now+1 seconds, the issue can be
+ * prevented by cancelling the alarm.
+ */
+ if (cmos->alarm_expires == t_now + 1) {
+ struct rtc_wkalrm alarm;
+
+ /* Cancel the AIE timer by configuring the past time. */
+ rtc_time64_to_tm(t_now - 1, &alarm.time);
+ alarm.enabled = 0;
+ retval = cmos_set_alarm(dev, &alarm);
+ } else if (cmos->alarm_expires > t_now + 1) {
+ retval = -EBUSY;
+ }
+
+ return retval;
+}
+
#ifdef CONFIG_PM
static int cmos_suspend(struct device *dev)
struct device *dev = &pnp->dev;
struct cmos_rtc *cmos = dev_get_drvdata(dev);
- if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(dev))
- return;
+ if (system_state == SYSTEM_POWER_OFF) {
+ int retval = cmos_poweroff(dev);
+
+ if (cmos_aie_poweroff(dev) < 0 && !retval)
+ return;
+ }
cmos_do_shutdown(cmos->irq);
}
struct device *dev = &pdev->dev;
struct cmos_rtc *cmos = dev_get_drvdata(dev);
- if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(dev))
- return;
+ if (system_state == SYSTEM_POWER_OFF) {
+ int retval = cmos_poweroff(dev);
+
+ if (cmos_aie_poweroff(dev) < 0 && !retval)
+ return;
+ }
cmos_do_shutdown(cmos->irq);
}
platform_driver_registered = true;
}
- dmi_check_system(rtc_quirks);
-
if (retval == 0)
return 0;