From patchwork Sat Jul 8 00:03:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Derek Basehore X-Patchwork-Id: 9831251 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 90DCE60352 for ; Sat, 8 Jul 2017 00:04:48 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8675228578 for ; Sat, 8 Jul 2017 00:04:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 797682858F; Sat, 8 Jul 2017 00:04:48 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6B1DD28578 for ; Sat, 8 Jul 2017 00:04:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752759AbdGHAEg (ORCPT ); Fri, 7 Jul 2017 20:04:36 -0400 Received: from mail-pf0-f173.google.com ([209.85.192.173]:35159 "EHLO mail-pf0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751751AbdGHADU (ORCPT ); Fri, 7 Jul 2017 20:03:20 -0400 Received: by mail-pf0-f173.google.com with SMTP id c73so23902012pfk.2 for ; Fri, 07 Jul 2017 17:03:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=5iIEAYOeviCialuGeiPVXnsYnQkYt0KF4HfwYcbOZNA=; b=GbPcjfmsLS9yZDry/iiCpfiaHDxaPL6W0Zc8uxiWvYt15u7/geNxk8PBWHEEuaV9J+ qJhTt246q6pulzm//Jqy+wZQ1tuRAR5NGXzrvbq4GdCanAo+7zcPSjzGZ2JO4Vvqt8av 9GJix5xIRFinEflIeaK/QqjNRu5W4eQM8Du6o= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=5iIEAYOeviCialuGeiPVXnsYnQkYt0KF4HfwYcbOZNA=; b=K3fxK4K5b4p2rtoh+TWr76aai+R/XHFAVvVfQZB2oc5wrdxwzZFegPVIjsBlB0Opq9 mmTXBHGked10yWJ4rYzwY8wT850rjm/NgHs1xmorT9taTI70XLdw+0Hwp+Rf7ZWdAs4I mWniLu2Rbsq9U7GFpl5/aoR+5CyA/pqJ77oyWGFTWE1v4iFTbva64TNPwlOvaYufh1w3 lZUGVUG7oKQ51FI8KJxKXqFMScq3PeEMA4MvGhLHST0sANvmlSAJMTxWV0PZcnNe3ydi Gqmymclhln2qUORDoX+e4lKmUFnqiYmY1HCtvpNugtER/3bnKCKrC/LbJ4n3pLhGTzqr SHNQ== X-Gm-Message-State: AIVw113mSe8obze2JyHfzu2azW4XJRwLwjhMTeNfUlAhd578FL861oyn MKj4SCZyH1T8WW5b X-Received: by 10.84.135.101 with SMTP id 92mr5637728pli.56.1499472199555; Fri, 07 Jul 2017 17:03:19 -0700 (PDT) Received: from ketosis.mtv.corp.google.com ([172.22.65.104]) by smtp.gmail.com with ESMTPSA id p11sm10026278pfk.128.2017.07.07.17.03.18 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 07 Jul 2017 17:03:18 -0700 (PDT) From: Derek Basehore To: linux-kernel@vger.kernel.org Cc: Thomas Gleixner , Ingo Molnar , Rajneesh Bhardwaj , x86@kernel.org, platform-driver-x86@vger.kernel.org, "Rafael J . Wysocki" , Len Brown , linux-pm@vger.kernel.org, Derek Basehore Subject: [PATCH v5 2/5] tick: Add freeze timer events Date: Fri, 7 Jul 2017 17:03:00 -0700 Message-Id: <20170708000303.21863-2-dbasehore@chromium.org> X-Mailer: git-send-email 2.13.2.725.g09c95d1e9-goog In-Reply-To: <20170708000303.21863-1-dbasehore@chromium.org> References: <20170708000303.21863-1-dbasehore@chromium.org> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Adds a new feature to tick to schedule wakeups on a CPU during freeze. This won't fully wake up the system (devices are not resumed), but allow simple platform functionality to be run during freeze with little power impact. This implementation allows an idle driver to setup a timer event with the clock event device when entering freeze by calling tick_set_freeze_event. Only one caller should exist for the function. tick_freeze_event_expired is used to check if the timer went off when the CPU wakes. The event is cleared by tick_clear_freeze_event. Signed-off-by: Derek Basehore --- include/linux/clockchips.h | 9 +++++ include/linux/suspend.h | 2 + kernel/time/tick-common.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index a116926598fd..6a3f30008020 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -66,12 +66,20 @@ enum clock_event_state { */ # define CLOCK_EVT_FEAT_HRTIMER 0x000080 +/* + * Clockevent device may run during freeze + */ +# define CLOCK_EVT_FEAT_FREEZE_NONSTOP 0x000100 + /** * struct clock_event_device - clock event device descriptor * @event_handler: Assigned by the framework to be called by the low * level handler of the event source * @set_next_event: set next event function using a clocksource delta * @set_next_ktime: set next event function using a direct ktime value + * @event_expired: check if the programmed event is expired. Used for + * freeze events when timekeeping is suspended and + * irqs are disabled. * @next_event: local storage for the next event in oneshot mode * @max_delta_ns: maximum delta value in ns * @min_delta_ns: minimum delta value in ns @@ -100,6 +108,7 @@ struct clock_event_device { void (*event_handler)(struct clock_event_device *); int (*set_next_event)(unsigned long evt, struct clock_event_device *); int (*set_next_ktime)(ktime_t expires, struct clock_event_device *); + int (*event_expired)(struct clock_event_device *); ktime_t next_event; u64 max_delta_ns; u64 min_delta_ns; diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 0b1cf32edfd7..1d56269a7b31 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -248,6 +248,8 @@ static inline bool idle_should_freeze(void) } extern void __init pm_states_init(void); +int tick_set_freeze_event(int cpu, ktime_t expires); +int tick_clear_freeze_event(int cpu); extern void freeze_set_ops(const struct platform_freeze_ops *ops); extern void freeze_wake(void); diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 49edc1c4f3e6..688d1c0cad10 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -498,6 +498,97 @@ void tick_freeze(void) raw_spin_unlock(&tick_freeze_lock); } +/** + * tick_set_freeze_event - Set timer to wake up the CPU from freeze. + * + * @cpu: CPU to set the clock event for + * @delta: time to wait before waking the CPU + * + * Returns 0 on success and -EERROR on failure. + */ +int tick_set_freeze_event(int cpu, ktime_t delta) +{ + struct clock_event_device *dev = per_cpu(tick_cpu_device, cpu).evtdev; + u64 delta_ns; + int ret; + + if (!dev->set_next_event || + !(dev->features & CLOCK_EVT_FEAT_FREEZE_NONSTOP)) { + printk_deferred(KERN_WARNING + "[%s] unsupported by clock event device\n", + __func__); + return -EPERM; + } + + if (!clockevent_state_shutdown(dev)) { + printk_deferred(KERN_WARNING + "[%s] clock event device in use\n", + __func__); + return -EBUSY; + } + + delta_ns = ktime_to_ns(delta); + if (delta_ns > dev->max_delta_ns || delta_ns < dev->min_delta_ns) { + printk_deferred(KERN_WARNING + "[%s] %lluns outside range: [%lluns, %lluns]\n", + __func__, delta_ns, dev->min_delta_ns, + dev->max_delta_ns); + return -ERANGE; + } + + clockevents_tick_resume(dev); + clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT); + ret = dev->set_next_event((delta_ns * dev->mult) >> dev->shift, dev); + if (ret < 0) { + printk_deferred(KERN_WARNING + "Failed to program freeze event\n"); + clockevents_shutdown(dev); + } + + return ret; +} +EXPORT_SYMBOL_GPL(tick_set_freeze_event); + +/** + * tick_freeze_event_expired - Check if the programmed freeze event expired + * + * @cpu: CPU to check the clock event device for an expired event + * + * Returns 1 if the event expired and 0 otherwise. + */ +int tick_freeze_event_expired(int cpu) +{ + struct clock_event_device *dev = per_cpu(tick_cpu_device, cpu).evtdev; + + if (!(dev && dev->event_expired)) + return 0; + + return dev->event_expired(dev); +} + +/** + * tick_clear_freeze_event - Shuts down the clock device after programming a + * freeze event. + * + * @cpu: CPU to shutdown the clock device for + * + * Returns 0 on success and -EERROR otherwise. + */ +int tick_clear_freeze_event(int cpu) +{ + struct clock_event_device *dev = per_cpu(tick_cpu_device, cpu).evtdev; + + if (!dev) + return -ENODEV; + + if (!clockevent_state_oneshot(dev)) + return -EBUSY; + + clockevents_shutdown(dev); + return 0; +} +EXPORT_SYMBOL_GPL(tick_clear_freeze_event); + /** * tick_unfreeze - Resume the local tick and (possibly) timekeeping. *