|
@@ -48,6 +48,17 @@
|
|
/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
|
|
/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
|
|
#include <linux/mc146818rtc.h>
|
|
#include <linux/mc146818rtc.h>
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Use ACPI SCI to replace HPET interrupt for RTC Alarm event
|
|
|
|
+ *
|
|
|
|
+ * If cleared, ACPI SCI is only used to wake up the system from suspend
|
|
|
|
+ *
|
|
|
|
+ * If set, ACPI SCI is used to handle UIE/AIE and system wakeup
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static bool use_acpi_alarm;
|
|
|
|
+module_param(use_acpi_alarm, bool, 0444);
|
|
|
|
+
|
|
struct cmos_rtc {
|
|
struct cmos_rtc {
|
|
struct rtc_device *rtc;
|
|
struct rtc_device *rtc;
|
|
struct device *dev;
|
|
struct device *dev;
|
|
@@ -153,6 +164,12 @@ static inline int hpet_unregister_irq_handler(irq_handler_t handler)
|
|
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
+/* Don't use HPET for RTC Alarm event if ACPI Fixed event is used */
|
|
|
|
+static int use_hpet_alarm(void)
|
|
|
|
+{
|
|
|
|
+ return is_hpet_enabled() && !use_acpi_alarm;
|
|
|
|
+}
|
|
|
|
+
|
|
/*----------------------------------------------------------------*/
|
|
/*----------------------------------------------------------------*/
|
|
|
|
|
|
#ifdef RTC_PORT
|
|
#ifdef RTC_PORT
|
|
@@ -298,7 +315,7 @@ static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control)
|
|
*/
|
|
*/
|
|
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
|
|
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
|
|
|
|
|
|
- if (is_hpet_enabled())
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
return;
|
|
return;
|
|
|
|
|
|
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
|
|
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
|
|
@@ -318,7 +335,13 @@ static void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask)
|
|
|
|
|
|
rtc_control |= mask;
|
|
rtc_control |= mask;
|
|
CMOS_WRITE(rtc_control, RTC_CONTROL);
|
|
CMOS_WRITE(rtc_control, RTC_CONTROL);
|
|
- hpet_set_rtc_irq_bit(mask);
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
|
|
+ hpet_set_rtc_irq_bit(mask);
|
|
|
|
+
|
|
|
|
+ if ((mask & RTC_AIE) && use_acpi_alarm) {
|
|
|
|
+ if (cmos->wake_on)
|
|
|
|
+ cmos->wake_on(cmos->dev);
|
|
|
|
+ }
|
|
|
|
|
|
cmos_checkintr(cmos, rtc_control);
|
|
cmos_checkintr(cmos, rtc_control);
|
|
}
|
|
}
|
|
@@ -330,7 +353,13 @@ static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
|
|
rtc_control = CMOS_READ(RTC_CONTROL);
|
|
rtc_control = CMOS_READ(RTC_CONTROL);
|
|
rtc_control &= ~mask;
|
|
rtc_control &= ~mask;
|
|
CMOS_WRITE(rtc_control, RTC_CONTROL);
|
|
CMOS_WRITE(rtc_control, RTC_CONTROL);
|
|
- hpet_mask_rtc_irq_bit(mask);
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
|
|
+ hpet_mask_rtc_irq_bit(mask);
|
|
|
|
+
|
|
|
|
+ if ((mask & RTC_AIE) && use_acpi_alarm) {
|
|
|
|
+ if (cmos->wake_off)
|
|
|
|
+ cmos->wake_off(cmos->dev);
|
|
|
|
+ }
|
|
|
|
|
|
cmos_checkintr(cmos, rtc_control);
|
|
cmos_checkintr(cmos, rtc_control);
|
|
}
|
|
}
|
|
@@ -448,10 +477,14 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|
CMOS_WRITE(mon, cmos->mon_alrm);
|
|
CMOS_WRITE(mon, cmos->mon_alrm);
|
|
}
|
|
}
|
|
|
|
|
|
- /* FIXME the HPET alarm glue currently ignores day_alrm
|
|
|
|
- * and mon_alrm ...
|
|
|
|
- */
|
|
|
|
- hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec);
|
|
|
|
|
|
+ if (use_hpet_alarm()) {
|
|
|
|
+ /*
|
|
|
|
+ * FIXME the HPET alarm glue currently ignores day_alrm
|
|
|
|
+ * and mon_alrm ...
|
|
|
|
+ */
|
|
|
|
+ hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min,
|
|
|
|
+ t->time.tm_sec);
|
|
|
|
+ }
|
|
|
|
|
|
if (t->enabled)
|
|
if (t->enabled)
|
|
cmos_irq_enable(cmos, RTC_AIE);
|
|
cmos_irq_enable(cmos, RTC_AIE);
|
|
@@ -508,7 +541,7 @@ static int cmos_procfs(struct device *dev, struct seq_file *seq)
|
|
"batt_status\t: %s\n",
|
|
"batt_status\t: %s\n",
|
|
(rtc_control & RTC_PIE) ? "yes" : "no",
|
|
(rtc_control & RTC_PIE) ? "yes" : "no",
|
|
(rtc_control & RTC_UIE) ? "yes" : "no",
|
|
(rtc_control & RTC_UIE) ? "yes" : "no",
|
|
- is_hpet_enabled() ? "yes" : "no",
|
|
|
|
|
|
+ use_hpet_alarm() ? "yes" : "no",
|
|
// (rtc_control & RTC_SQWE) ? "yes" : "no",
|
|
// (rtc_control & RTC_SQWE) ? "yes" : "no",
|
|
(rtc_control & RTC_DM_BINARY) ? "no" : "yes",
|
|
(rtc_control & RTC_DM_BINARY) ? "no" : "yes",
|
|
(rtc_control & RTC_DST_EN) ? "yes" : "no",
|
|
(rtc_control & RTC_DST_EN) ? "yes" : "no",
|
|
@@ -614,7 +647,7 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
|
|
*/
|
|
*/
|
|
irqstat = CMOS_READ(RTC_INTR_FLAGS);
|
|
irqstat = CMOS_READ(RTC_INTR_FLAGS);
|
|
rtc_control = CMOS_READ(RTC_CONTROL);
|
|
rtc_control = CMOS_READ(RTC_CONTROL);
|
|
- if (is_hpet_enabled())
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
irqstat = (unsigned long)irq & 0xF0;
|
|
irqstat = (unsigned long)irq & 0xF0;
|
|
|
|
|
|
/* If we were suspended, RTC_CONTROL may not be accurate since the
|
|
/* If we were suspended, RTC_CONTROL may not be accurate since the
|
|
@@ -633,7 +666,8 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
|
|
cmos_rtc.suspend_ctrl &= ~RTC_AIE;
|
|
cmos_rtc.suspend_ctrl &= ~RTC_AIE;
|
|
rtc_control &= ~RTC_AIE;
|
|
rtc_control &= ~RTC_AIE;
|
|
CMOS_WRITE(rtc_control, RTC_CONTROL);
|
|
CMOS_WRITE(rtc_control, RTC_CONTROL);
|
|
- hpet_mask_rtc_irq_bit(RTC_AIE);
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
|
|
+ hpet_mask_rtc_irq_bit(RTC_AIE);
|
|
CMOS_READ(RTC_INTR_FLAGS);
|
|
CMOS_READ(RTC_INTR_FLAGS);
|
|
}
|
|
}
|
|
spin_unlock(&rtc_lock);
|
|
spin_unlock(&rtc_lock);
|
|
@@ -762,7 +796,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
|
|
* need to do something about other clock frequencies.
|
|
* need to do something about other clock frequencies.
|
|
*/
|
|
*/
|
|
cmos_rtc.rtc->irq_freq = 1024;
|
|
cmos_rtc.rtc->irq_freq = 1024;
|
|
- hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq);
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
|
|
+ hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq);
|
|
CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
|
|
CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -780,12 +815,13 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
|
|
goto cleanup1;
|
|
goto cleanup1;
|
|
}
|
|
}
|
|
|
|
|
|
- hpet_rtc_timer_init();
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
|
|
+ hpet_rtc_timer_init();
|
|
|
|
|
|
if (is_valid_irq(rtc_irq)) {
|
|
if (is_valid_irq(rtc_irq)) {
|
|
irq_handler_t rtc_cmos_int_handler;
|
|
irq_handler_t rtc_cmos_int_handler;
|
|
|
|
|
|
- if (is_hpet_enabled()) {
|
|
|
|
|
|
+ if (use_hpet_alarm()) {
|
|
rtc_cmos_int_handler = hpet_rtc_interrupt;
|
|
rtc_cmos_int_handler = hpet_rtc_interrupt;
|
|
retval = hpet_register_irq_handler(cmos_interrupt);
|
|
retval = hpet_register_irq_handler(cmos_interrupt);
|
|
if (retval) {
|
|
if (retval) {
|
|
@@ -824,7 +860,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
|
|
"alarms up to one day",
|
|
"alarms up to one day",
|
|
cmos_rtc.century ? ", y3k" : "",
|
|
cmos_rtc.century ? ", y3k" : "",
|
|
nvmem_cfg.size,
|
|
nvmem_cfg.size,
|
|
- is_hpet_enabled() ? ", hpet irqs" : "");
|
|
|
|
|
|
+ use_hpet_alarm() ? ", hpet irqs" : "");
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
@@ -858,7 +894,8 @@ static void cmos_do_remove(struct device *dev)
|
|
|
|
|
|
if (is_valid_irq(cmos->irq)) {
|
|
if (is_valid_irq(cmos->irq)) {
|
|
free_irq(cmos->irq, cmos->rtc);
|
|
free_irq(cmos->irq, cmos->rtc);
|
|
- hpet_unregister_irq_handler(cmos_interrupt);
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
|
|
+ hpet_unregister_irq_handler(cmos_interrupt);
|
|
}
|
|
}
|
|
|
|
|
|
cmos->rtc = NULL;
|
|
cmos->rtc = NULL;
|
|
@@ -935,13 +972,13 @@ static int cmos_suspend(struct device *dev)
|
|
mask = RTC_IRQMASK;
|
|
mask = RTC_IRQMASK;
|
|
tmp &= ~mask;
|
|
tmp &= ~mask;
|
|
CMOS_WRITE(tmp, RTC_CONTROL);
|
|
CMOS_WRITE(tmp, RTC_CONTROL);
|
|
- hpet_mask_rtc_irq_bit(mask);
|
|
|
|
-
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
|
|
+ hpet_mask_rtc_irq_bit(mask);
|
|
cmos_checkintr(cmos, tmp);
|
|
cmos_checkintr(cmos, tmp);
|
|
}
|
|
}
|
|
spin_unlock_irq(&rtc_lock);
|
|
spin_unlock_irq(&rtc_lock);
|
|
|
|
|
|
- if (tmp & RTC_AIE) {
|
|
|
|
|
|
+ if ((tmp & RTC_AIE) && !use_acpi_alarm) {
|
|
cmos->enabled_wake = 1;
|
|
cmos->enabled_wake = 1;
|
|
if (cmos->wake_on)
|
|
if (cmos->wake_on)
|
|
cmos->wake_on(dev);
|
|
cmos->wake_on(dev);
|
|
@@ -996,7 +1033,7 @@ static int __maybe_unused cmos_resume(struct device *dev)
|
|
struct cmos_rtc *cmos = dev_get_drvdata(dev);
|
|
struct cmos_rtc *cmos = dev_get_drvdata(dev);
|
|
unsigned char tmp;
|
|
unsigned char tmp;
|
|
|
|
|
|
- if (cmos->enabled_wake) {
|
|
|
|
|
|
+ if (cmos->enabled_wake && !use_acpi_alarm) {
|
|
if (cmos->wake_off)
|
|
if (cmos->wake_off)
|
|
cmos->wake_off(dev);
|
|
cmos->wake_off(dev);
|
|
else
|
|
else
|
|
@@ -1014,16 +1051,17 @@ static int __maybe_unused cmos_resume(struct device *dev)
|
|
if (tmp & RTC_IRQMASK) {
|
|
if (tmp & RTC_IRQMASK) {
|
|
unsigned char mask;
|
|
unsigned char mask;
|
|
|
|
|
|
- if (device_may_wakeup(dev))
|
|
|
|
|
|
+ if (device_may_wakeup(dev) && use_hpet_alarm())
|
|
hpet_rtc_timer_init();
|
|
hpet_rtc_timer_init();
|
|
|
|
|
|
do {
|
|
do {
|
|
CMOS_WRITE(tmp, RTC_CONTROL);
|
|
CMOS_WRITE(tmp, RTC_CONTROL);
|
|
- hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK);
|
|
|
|
|
|
+ if (use_hpet_alarm())
|
|
|
|
+ hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK);
|
|
|
|
|
|
mask = CMOS_READ(RTC_INTR_FLAGS);
|
|
mask = CMOS_READ(RTC_INTR_FLAGS);
|
|
mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
|
|
mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
|
|
- if (!is_hpet_enabled() || !is_intr(mask))
|
|
|
|
|
|
+ if (!use_hpet_alarm() || !is_intr(mask))
|
|
break;
|
|
break;
|
|
|
|
|
|
/* force one-shot behavior if HPET blocked
|
|
/* force one-shot behavior if HPET blocked
|
|
@@ -1068,16 +1106,27 @@ static u32 rtc_handler(void *context)
|
|
unsigned char rtc_intr;
|
|
unsigned char rtc_intr;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
|
|
- spin_lock_irqsave(&rtc_lock, flags);
|
|
|
|
- if (cmos_rtc.suspend_ctrl)
|
|
|
|
- rtc_control = CMOS_READ(RTC_CONTROL);
|
|
|
|
- if (rtc_control & RTC_AIE) {
|
|
|
|
- cmos_rtc.suspend_ctrl &= ~RTC_AIE;
|
|
|
|
- CMOS_WRITE(rtc_control, RTC_CONTROL);
|
|
|
|
- rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
|
|
|
|
- rtc_update_irq(cmos->rtc, 1, rtc_intr);
|
|
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Always update rtc irq when ACPI is used as RTC Alarm.
|
|
|
|
+ * Or else, ACPI SCI is enabled during suspend/resume only,
|
|
|
|
+ * update rtc irq in that case.
|
|
|
|
+ */
|
|
|
|
+ if (use_acpi_alarm)
|
|
|
|
+ cmos_interrupt(0, (void *)cmos->rtc);
|
|
|
|
+ else {
|
|
|
|
+ /* Fix me: can we use cmos_interrupt() here as well? */
|
|
|
|
+ spin_lock_irqsave(&rtc_lock, flags);
|
|
|
|
+ if (cmos_rtc.suspend_ctrl)
|
|
|
|
+ rtc_control = CMOS_READ(RTC_CONTROL);
|
|
|
|
+ if (rtc_control & RTC_AIE) {
|
|
|
|
+ cmos_rtc.suspend_ctrl &= ~RTC_AIE;
|
|
|
|
+ CMOS_WRITE(rtc_control, RTC_CONTROL);
|
|
|
|
+ rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
|
|
|
|
+ rtc_update_irq(cmos->rtc, 1, rtc_intr);
|
|
|
|
+ }
|
|
|
|
+ spin_unlock_irqrestore(&rtc_lock, flags);
|
|
}
|
|
}
|
|
- spin_unlock_irqrestore(&rtc_lock, flags);
|
|
|
|
|
|
|
|
pm_wakeup_hard_event(dev);
|
|
pm_wakeup_hard_event(dev);
|
|
acpi_clear_event(ACPI_EVENT_RTC);
|
|
acpi_clear_event(ACPI_EVENT_RTC);
|