All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] X86: check X86_VENDOR_ANY correctly
@ 2018-01-29  2:48 Zhang Rui
  2018-01-29  2:48 ` [PATCH 2/4] rtc: cmos: allow using ACPI for RTC alarm instead of HPET Zhang Rui
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Zhang Rui @ 2018-01-29  2:48 UTC (permalink / raw)
  To: alexandre.belloni, linux-rtc; +Cc: gabriele.mzt, rui.zhang, x86, tglx

static const struct x86_cpu_id foo[] = {
	{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ANY },
	{}
};

x86_match_cpu(foo) returns NULL with Intel processors.

This is wrong because,
unlike X86_FAMILY_ANY/X86_MODEL_ANY/X86_FEATURE_ANY, X86_VENDOR_ANY is
0xffff, rather than 0.

Fix x86_match_cpu() by checking X86_VENDOR_ANY explicitly.

cc: x86@kernel.org
cc: tglx@linutronix.de
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 arch/x86/kernel/cpu/match.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kernel/cpu/match.c b/arch/x86/kernel/cpu/match.c
index 3fed388..72f363e 100644
--- a/arch/x86/kernel/cpu/match.c
+++ b/arch/x86/kernel/cpu/match.c
@@ -34,7 +34,7 @@ const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match)
 	const struct x86_cpu_id *m;
 	struct cpuinfo_x86 *c = &boot_cpu_data;
 
-	for (m = match; m->vendor | m->family | m->model | m->feature; m++) {
+	for (m = match; (m->vendor != X86_VENDOR_ANY) | m->family | m->model | m->feature; m++) {
 		if (m->vendor != X86_VENDOR_ANY && c->x86_vendor != m->vendor)
 			continue;
 		if (m->family != X86_FAMILY_ANY && c->x86 != m->family)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/4] rtc: cmos: allow using ACPI for RTC alarm instead of HPET
  2018-01-29  2:48 [PATCH 1/4] X86: check X86_VENDOR_ANY correctly Zhang Rui
@ 2018-01-29  2:48 ` Zhang Rui
  2018-01-29  2:48 ` [PATCH 3/4] rtc: cmos: acknowledge ACPI driven wake alarms upon resume Zhang Rui
  2018-01-29  2:48 ` [PATCH 4/4] rtc: cmos: introduce quirks to enable use_acpi_alarm mode Zhang Rui
  2 siblings, 0 replies; 4+ messages in thread
From: Zhang Rui @ 2018-01-29  2:48 UTC (permalink / raw)
  To: alexandre.belloni, linux-rtc; +Cc: gabriele.mzt, rui.zhang

It's found that the HPET timer prevents the platform from entering
Low Power S0 on some new Intel platforms.

This means that
1. users can still use RTC wake Alarm for suspend-to-idle, but the system
   never enters Low Power S0, which is a waste of power.
or
2. if users want to put the system into Low Power S0, they can not use
   RTC as the wakeup source.

To fix this, we need to stop using the HPET timer for wake alarm.
But disabling CONFIG_HPET_EMULATE_RTC is not an option because HPET
emulates PIT at the same time, and this is needed on some of these
platforms.

Thus, introduce a new mode (use_acpi_alarm) to the rtc_cmos driver,
so that, even with CONFIG_HPET_EMULATE_RTC enabled, it's still possible to
use ACPI SCI for RTC Alarm, including UIE/AIE/wkalrm, instead of HPET.

Only necessary changes are made for the new "use_acpi_alarm" mode, including
1. drop all the calls to HPET emulation code, including the HPET irq
   handler for rtc interrupt.
2. enabling/disabling ACPI RTC Fixed event upon RTC UIE/AIE request.
3. acknowledge the RTC Alarm in ACPI RTC Fixed event handler.

There is no functional change made in this patch if the new mode is not
enabled.

Note: this "use_acpi_alarm" mode is made based on the assumption that
ACPI RTC Fixed event is reliable both at runtime and during system wakeup.
And this has been verified on a couple of platforms I have, including
a MS Surface Pro 4 (SKL), a Lenovo Yoga 900 (SKL), and a HP 9360 (KBL).

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/rtc/rtc-cmos.c | 111 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 80 insertions(+), 31 deletions(-)

diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 9dca53d..9665a72 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -48,6 +48,17 @@
 /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
 #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 rtc_device	*rtc;
 	struct device		*dev;
@@ -153,6 +164,12 @@ static inline int hpet_unregister_irq_handler(irq_handler_t handler)
 
 #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
@@ -298,7 +315,7 @@ static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control)
 	 */
 	rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
 
-	if (is_hpet_enabled())
+	if (use_hpet_alarm())
 		return;
 
 	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;
 	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);
 }
@@ -330,7 +353,13 @@ static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
 	rtc_control = CMOS_READ(RTC_CONTROL);
 	rtc_control &= ~mask;
 	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);
 }
@@ -448,10 +477,14 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
 			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)
 		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",
 		   (rtc_control & RTC_PIE) ? "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_DM_BINARY) ? "no" : "yes",
 		   (rtc_control & RTC_DST_EN) ? "yes" : "no",
@@ -629,7 +662,7 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
 	 */
 	irqstat = CMOS_READ(RTC_INTR_FLAGS);
 	rtc_control = CMOS_READ(RTC_CONTROL);
-	if (is_hpet_enabled())
+	if (use_hpet_alarm())
 		irqstat = (unsigned long)irq & 0xF0;
 
 	/* If we were suspended, RTC_CONTROL may not be accurate since the
@@ -648,7 +681,8 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
 		cmos_rtc.suspend_ctrl &= ~RTC_AIE;
 		rtc_control &= ~RTC_AIE;
 		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);
 	}
 	spin_unlock(&rtc_lock);
@@ -770,7 +804,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
 		 * need to do something about other clock frequencies.
 		 */
 		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);
 	}
 
@@ -788,12 +823,13 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
 		goto cleanup1;
 	}
 
-	hpet_rtc_timer_init();
+	if (use_hpet_alarm())
+		hpet_rtc_timer_init();
 
 	if (is_valid_irq(rtc_irq)) {
 		irq_handler_t rtc_cmos_int_handler;
 
-		if (is_hpet_enabled()) {
+		if (use_hpet_alarm()) {
 			rtc_cmos_int_handler = hpet_rtc_interrupt;
 			retval = hpet_register_irq_handler(cmos_interrupt);
 			if (retval) {
@@ -829,7 +865,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
 			"alarms up to one day",
 		cmos_rtc.century ? ", y3k" : "",
 		nvram.size,
-		is_hpet_enabled() ? ", hpet irqs" : "");
+		use_hpet_alarm() ? ", hpet irqs" : "");
 
 	return 0;
 
@@ -866,7 +902,8 @@ static void cmos_do_remove(struct device *dev)
 
 	if (is_valid_irq(cmos->irq)) {
 		free_irq(cmos->irq, cmos->rtc);
-		hpet_unregister_irq_handler(cmos_interrupt);
+		if (use_hpet_alarm())
+			hpet_unregister_irq_handler(cmos_interrupt);
 	}
 
 	rtc_device_unregister(cmos->rtc);
@@ -944,13 +981,13 @@ static int cmos_suspend(struct device *dev)
 			mask = RTC_IRQMASK;
 		tmp &= ~mask;
 		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);
 	}
 	spin_unlock_irq(&rtc_lock);
 
-	if (tmp & RTC_AIE) {
+	if ((tmp & RTC_AIE) && !use_acpi_alarm) {
 		cmos->enabled_wake = 1;
 		if (cmos->wake_on)
 			cmos->wake_on(dev);
@@ -1005,7 +1042,7 @@ static int __maybe_unused cmos_resume(struct device *dev)
 	struct cmos_rtc	*cmos = dev_get_drvdata(dev);
 	unsigned char tmp;
 
-	if (cmos->enabled_wake) {
+	if (cmos->enabled_wake && !use_acpi_alarm) {
 		if (cmos->wake_off)
 			cmos->wake_off(dev);
 		else
@@ -1023,16 +1060,17 @@ static int __maybe_unused cmos_resume(struct device *dev)
 	if (tmp & RTC_IRQMASK) {
 		unsigned char	mask;
 
-		if (device_may_wakeup(dev))
+		if (device_may_wakeup(dev) && use_hpet_alarm())
 			hpet_rtc_timer_init();
 
 		do {
 			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 &= (tmp & RTC_IRQMASK) | RTC_IRQF;
-			if (!is_hpet_enabled() || !is_intr(mask))
+			if (!use_hpet_alarm() || !is_intr(mask))
 				break;
 
 			/* force one-shot behavior if HPET blocked
@@ -1077,16 +1115,27 @@ static u32 rtc_handler(void *context)
 	unsigned char rtc_intr;
 	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);
 	acpi_clear_event(ACPI_EVENT_RTC);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/4] rtc: cmos: acknowledge ACPI driven wake alarms upon resume
  2018-01-29  2:48 [PATCH 1/4] X86: check X86_VENDOR_ANY correctly Zhang Rui
  2018-01-29  2:48 ` [PATCH 2/4] rtc: cmos: allow using ACPI for RTC alarm instead of HPET Zhang Rui
@ 2018-01-29  2:48 ` Zhang Rui
  2018-01-29  2:48 ` [PATCH 4/4] rtc: cmos: introduce quirks to enable use_acpi_alarm mode Zhang Rui
  2 siblings, 0 replies; 4+ messages in thread
From: Zhang Rui @ 2018-01-29  2:48 UTC (permalink / raw)
  To: alexandre.belloni, linux-rtc; +Cc: gabriele.mzt, rui.zhang

Previously, the RTC alarm is acknowledged either by the cmos rtc irq
handler, or by the hpet rtc irq handler.

When using ACPI RTC Fixed event as the RTC alarm, the RTC alarm is
acknowledged by the ACPI RTC event handler, as addressed in the previous
patch.
But, when resume from suspend-to-ram (ACPI S3), the ACPI SCI is cleared
right after resume, thus the ACPI RTC event handler is not invoked at all,
results in the RTC Alarm unacknowledged.

Handle this by comparing the current time and the RTC Alarm time in the
rtc_cmos driver .resume() callback
1. Assume the wakeup event has already been fired if the RTC Alarm time
   is earlier than/equal to the current time, and ACK the RTC Alarm.
2. Assume the wakeup event has not been fired if the RTC Alarm time
   is later than current time, and re-arm it if needed.

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/rtc/rtc-cmos.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 9665a72..bcf86e1 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -1022,8 +1022,26 @@ static void cmos_check_wkalrm(struct device *dev)
 {
 	struct cmos_rtc *cmos = dev_get_drvdata(dev);
 	struct rtc_wkalrm current_alarm;
+	time64_t t_now;
 	time64_t t_current_expires;
 	time64_t t_saved_expires;
+	struct rtc_time now;
+
+	/* Check if we have RTC Alarm armed */
+	if (!(cmos->suspend_ctrl & RTC_AIE))
+		return;
+
+	cmos_read_time(dev, &now);
+	t_now = rtc_tm_to_time64(&now);
+
+	/*
+	 * ACPI RTC wake event is cleared after resume from STR,
+	 * ACK the rtc irq here
+	 */
+	if (t_now >= cmos->alarm_expires && use_acpi_alarm) {
+		cmos_interrupt(0, (void *)cmos->rtc);
+		return;
+	}
 
 	cmos_read_alarm(dev, &current_alarm);
 	t_current_expires = rtc_tm_to_time64(&current_alarm.time);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 4/4] rtc: cmos: introduce quirks to enable use_acpi_alarm mode
  2018-01-29  2:48 [PATCH 1/4] X86: check X86_VENDOR_ANY correctly Zhang Rui
  2018-01-29  2:48 ` [PATCH 2/4] rtc: cmos: allow using ACPI for RTC alarm instead of HPET Zhang Rui
  2018-01-29  2:48 ` [PATCH 3/4] rtc: cmos: acknowledge ACPI driven wake alarms upon resume Zhang Rui
@ 2018-01-29  2:48 ` Zhang Rui
  2 siblings, 0 replies; 4+ messages in thread
From: Zhang Rui @ 2018-01-29  2:48 UTC (permalink / raw)
  To: alexandre.belloni, linux-rtc; +Cc: gabriele.mzt, rui.zhang

Use ACPI for RTC Alarm only for Intel platforms
1. with Low Power S0 support
2. with HPET RTC emulation enabled
3. no earlier than 2015

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/rtc/rtc-cmos.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index bcf86e1..414b450 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -43,6 +43,8 @@
 #include <linux/of_platform.h>
 #ifdef CONFIG_X86
 #include <asm/i8259.h>
+#include <asm/cpu_device_id.h>
+#include <linux/dmi.h>
 #endif
 
 /* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
@@ -1183,6 +1185,33 @@ static void rtc_wake_off(struct device *dev)
 	acpi_disable_event(ACPI_EVENT_RTC, 0);
 }
 
+#ifdef CONFIG_X86
+static const struct x86_cpu_id use_acpi_alarm_ids[] = {
+	{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ANY },
+	{}
+};
+
+/* Enable use_acpi_alarm mode for Intel platforms no earlier than 2015 */
+static void use_acpi_alarm_quirks(void)
+{
+	int year;
+
+	if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
+		return;
+
+	if (!is_hpet_enabled())
+		return;
+
+	if (!x86_match_cpu(use_acpi_alarm_ids))
+		return;
+
+	if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year >= 2015)
+		use_acpi_alarm = true;
+}
+#else
+static inline void use_acpi_alarm_quirks(void) { }
+#endif
+
 /* Every ACPI platform has a mc146818 compatible "cmos rtc".  Here we find
  * its device node and pass extra config data.  This helps its driver use
  * capabilities that the now-obsolete mc146818 didn't have, and informs it
@@ -1195,6 +1224,8 @@ static void cmos_wake_setup(struct device *dev)
 	if (acpi_disabled)
 		return;
 
+	use_acpi_alarm_quirks();
+
 	rtc_wake_setup(dev);
 	acpi_rtc_info.wake_on = rtc_wake_on;
 	acpi_rtc_info.wake_off = rtc_wake_off;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2018-01-29  2:49 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-29  2:48 [PATCH 1/4] X86: check X86_VENDOR_ANY correctly Zhang Rui
2018-01-29  2:48 ` [PATCH 2/4] rtc: cmos: allow using ACPI for RTC alarm instead of HPET Zhang Rui
2018-01-29  2:48 ` [PATCH 3/4] rtc: cmos: acknowledge ACPI driven wake alarms upon resume Zhang Rui
2018-01-29  2:48 ` [PATCH 4/4] rtc: cmos: introduce quirks to enable use_acpi_alarm mode Zhang Rui

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.