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: 9831253 X-Patchwork-Delegate: andy.shevchenko@gmail.com 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 CBAEC60361 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 C1C6C28578 for ; Sat, 8 Jul 2017 00:04:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B650D2858E; 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 B706E28583 for ; Sat, 8 Jul 2017 00:04:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752757AbdGHAEf (ORCPT ); Fri, 7 Jul 2017 20:04:35 -0400 Received: from mail-pf0-f175.google.com ([209.85.192.175]:34753 "EHLO mail-pf0-f175.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751828AbdGHADU (ORCPT ); Fri, 7 Jul 2017 20:03:20 -0400 Received: by mail-pf0-f175.google.com with SMTP id q85so23886275pfq.1 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=DjNLCmF2/w/LP0C2LpeetWyEZd3GPsFJkUDvJABG4dnsUJTESb8/vYDGZXc4126God ZW5CvVKfgb21nJeFI72NQ/TdkvUe9lPb64iexedTkDVOmFiKir92MIhNnPwQ2zmNvSeZ H5oWzFyb+1MpCIBRmYJ4MepVm7SU0SNf25eCP2b1FCi3nP0fJCnxnkwnafurfWVQOJV2 Y6z9lBlcZK+bveufRqRSpqBD3/DnUUt8TwNqF39x0KN/L0eehBHCzZi6mUol0Mvz4FVb zmYwmP9drKwcjeoZgqRuTZrF7vyzqwPC/LwRip9B69KZQOWlTnIaFfIFsu2srruU5R4p jiRg== X-Gm-Message-State: AIVw110mMkqPO30GwLEYthPP1H61kCDdERmPxd6JxF3VqS5RHJRR1Ary WKbpC64pp71hRxvv 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: platform-driver-x86-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: platform-driver-x86@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. *