From patchwork Thu Jan 4 08:58:58 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Zhang, Rui" X-Patchwork-Id: 855501 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-rtc-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3zC1v65R4zz9s71 for ; Thu, 4 Jan 2018 19:59:14 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751394AbeADI7N (ORCPT ); Thu, 4 Jan 2018 03:59:13 -0500 Received: from mga01.intel.com ([192.55.52.88]:44368 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751378AbeADI7N (ORCPT ); Thu, 4 Jan 2018 03:59:13 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 04 Jan 2018 00:59:00 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.45,506,1508828400"; d="scan'208";a="7312035" Received: from yinfendu-mobl2.ccr.corp.intel.com (HELO rzhang1-surface.ccr.corp.intel.com) ([10.255.26.68]) by fmsmga008.fm.intel.com with ESMTP; 04 Jan 2018 00:58:59 -0800 From: Zhang Rui To: alexandre.belloni@free-electrons.com, linux-rtc@vger.kernel.org Cc: gabriele.mzt@gmail.com, rui.zhang@intel.com Subject: [RFC 1/3] rtc: cmos: allow using ACPI for RTC alarm instead of HPET Date: Thu, 4 Jan 2018 16:58:58 +0800 Message-Id: <1515056338-14658-1-git-send-email-rui.zhang@intel.com> X-Mailer: git-send-email 2.7.4 Sender: linux-rtc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-rtc@vger.kernel.org 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 "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 --- 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 +/* + * 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); From patchwork Thu Jan 4 08:59:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Zhang, Rui" X-Patchwork-Id: 855502 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-rtc-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3zC1vD0F4Mz9s71 for ; Thu, 4 Jan 2018 19:59:20 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751394AbeADI7T (ORCPT ); Thu, 4 Jan 2018 03:59:19 -0500 Received: from mga09.intel.com ([134.134.136.24]:5650 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751378AbeADI7S (ORCPT ); Thu, 4 Jan 2018 03:59:18 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 04 Jan 2018 00:59:18 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.45,506,1508828400"; d="scan'208";a="24196810" Received: from yinfendu-mobl2.ccr.corp.intel.com (HELO rzhang1-surface.ccr.corp.intel.com) ([10.255.26.68]) by orsmga002.jf.intel.com with ESMTP; 04 Jan 2018 00:59:16 -0800 From: Zhang Rui To: alexandre.belloni@free-electrons.com, linux-rtc@vger.kernel.org Cc: gabriele.mzt@gmail.com, rui.zhang@intel.com Subject: [RFC 2/3] rtc: cmos: acknowledge ACPI driven wake alarms upon resume Date: Thu, 4 Jan 2018 16:59:16 +0800 Message-Id: <1515056356-14757-1-git-send-email-rui.zhang@intel.com> X-Mailer: git-send-email 2.7.4 Sender: linux-rtc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-rtc@vger.kernel.org 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 --- 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, ¤t_alarm); t_current_expires = rtc_tm_to_time64(¤t_alarm.time); From patchwork Thu Jan 4 08:59:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Zhang, Rui" X-Patchwork-Id: 855503 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-rtc-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3zC1vP5Brfz9s71 for ; Thu, 4 Jan 2018 19:59:29 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751378AbeADI73 (ORCPT ); Thu, 4 Jan 2018 03:59:29 -0500 Received: from mga03.intel.com ([134.134.136.65]:43009 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751294AbeADI73 (ORCPT ); Thu, 4 Jan 2018 03:59:29 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 04 Jan 2018 00:59:28 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.45,506,1508828400"; d="scan'208";a="16684141" Received: from yinfendu-mobl2.ccr.corp.intel.com (HELO rzhang1-surface.ccr.corp.intel.com) ([10.255.26.68]) by FMSMGA003.fm.intel.com with ESMTP; 04 Jan 2018 00:59:26 -0800 From: Zhang Rui To: alexandre.belloni@free-electrons.com, linux-rtc@vger.kernel.org Cc: gabriele.mzt@gmail.com, rui.zhang@intel.com Subject: [RFC 3/3] rtc: cmos: introduce quirks to enable use_acpi_alarm mode Date: Thu, 4 Jan 2018 16:59:26 +0800 Message-Id: <1515056366-14850-1-git-send-email-rui.zhang@intel.com> X-Mailer: git-send-email 2.7.4 Sender: linux-rtc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-rtc@vger.kernel.org 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 --- 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 #ifdef CONFIG_X86 #include +#include +#include #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;