From patchwork Sun Jan 8 08:41:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukas Wunner X-Patchwork-Id: 9503381 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 0D74B60710 for ; Sun, 8 Jan 2017 08:45:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 05F4D27BE5 for ; Sun, 8 Jan 2017 08:45:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EED3228329; Sun, 8 Jan 2017 08:45:01 +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=-6.9 required=2.0 tests=BAYES_00,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 91AA227BE5 for ; Sun, 8 Jan 2017 08:45:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1032720AbdAHIo7 (ORCPT ); Sun, 8 Jan 2017 03:44:59 -0500 Received: from mailout2.hostsharing.net ([83.223.90.233]:48371 "EHLO mailout2.hostsharing.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1032713AbdAHIo6 (ORCPT ); Sun, 8 Jan 2017 03:44:58 -0500 Received: from h08.hostsharing.net (h08.hostsharing.net [83.223.95.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mailout2.hostsharing.net (Postfix) with ESMTPS id B3B1E10189C7F; Sun, 8 Jan 2017 09:44:55 +0100 (CET) Received: from localhost (3-38-90-81.adsl.cmo.de [81.90.38.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA (128/128 bits)) (No client certificate requested) by h08.hostsharing.net (Postfix) with ESMTPSA id 46014608355B; Sun, 8 Jan 2017 09:44:53 +0100 (CET) X-Mailbox-Line: From 2dd9744308f87f15e4e252157b327fcae5633693 Mon Sep 17 00:00:00 2001 Message-Id: <2dd9744308f87f15e4e252157b327fcae5633693.1483806825.git.lukas@wunner.de> In-Reply-To: References: From: Lukas Wunner Date: Sun, 8 Jan 2017 09:41:45 +0100 Subject: [PATCH v4 8/8] thunderbolt: Runtime suspend NHI when idle To: Greg Kroah-Hartman , linux-kernel@vger.kernel.org Cc: Andreas Noever , linux-pci@vger.kernel.org, linux-pm@vger.kernel.org, Chen Yu , Tomas Winkler , Amir Levy 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 Runtime suspend the NHI when no Thunderbolt devices have been plugged in for 10 sec (user-configurable via autosuspend_delay_ms in sysfs). The NHI is not able to detect plug events while suspended, it relies on the GPE handler to resume it on hotplug. After the NHI resumes, it takes about 700 ms until a hotplug event appears on the RX ring. In case autosuspend_delay_ms has been reduced to 0 by the user, we need to wait in tb_resume() to avoid going back to sleep before we had a chance to detect a hotplugged device. A runtime pm ref is held for the duration of tb_handle_hotplug() to keep the NHI awake while the hotplug event is processed. Apart from that we acquire a runtime pm ref for each newly allocated switch (except for the root switch) and drop one when a switch is freed, thereby ensuring the NHI stays active as long as devices are plugged in. This behaviour is identical to the macOS driver. Cc: Andreas Noever Signed-off-by: Lukas Wunner --- drivers/thunderbolt/nhi.c | 2 ++ drivers/thunderbolt/power.c | 9 +++++++++ drivers/thunderbolt/switch.c | 9 +++++++++ drivers/thunderbolt/tb.c | 13 +++++++++++++ 4 files changed, 33 insertions(+) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 88fb2fb..319ed81 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -632,6 +632,8 @@ static void nhi_remove(struct pci_dev *pdev) * pci-tunnels stay alive. */ .restore_noirq = nhi_resume_noirq, + .runtime_suspend = nhi_suspend_noirq, + .runtime_resume = nhi_resume_noirq, }; static struct pci_device_id nhi_ids[] = { diff --git a/drivers/thunderbolt/power.c b/drivers/thunderbolt/power.c index 6e7ef07..06f535c 100644 --- a/drivers/thunderbolt/power.c +++ b/drivers/thunderbolt/power.c @@ -319,6 +319,12 @@ void thunderbolt_power_init(struct tb *tb) tb->power = power; + pm_runtime_allow(nhi_dev); + pm_runtime_set_autosuspend_delay(nhi_dev, 10000); + pm_runtime_use_autosuspend(nhi_dev); + pm_runtime_mark_last_busy(nhi_dev); + pm_runtime_put_autosuspend(nhi_dev); + return; err_free: @@ -335,6 +341,9 @@ void thunderbolt_power_fini(struct tb *tb) if (!power) return; /* thunderbolt_power_init() failed */ + pm_runtime_get(nhi_dev); + pm_runtime_forbid(nhi_dev); + tb->power = NULL; dev_pm_domain_set(upstream_dev, NULL); diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index c6f30b1..422fe6e 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -5,6 +5,7 @@ */ #include +#include #include #include "tb.h" @@ -326,6 +327,11 @@ void tb_switch_free(struct tb_switch *sw) if (!sw->is_unplugged) tb_plug_events_active(sw, false); + if (sw != sw->tb->root_switch) { + pm_runtime_mark_last_busy(&sw->tb->nhi->pdev->dev); + pm_runtime_put_autosuspend(&sw->tb->nhi->pdev->dev); + } + kfree(sw->ports); kfree(sw->drom); kfree(sw); @@ -420,6 +426,9 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) if (tb_plug_events_active(sw, true)) goto err; + if (tb->root_switch) + pm_runtime_get(&tb->nhi->pdev->dev); + return sw; err: kfree(sw->ports); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 24b6d30..a3fedf9 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "tb.h" #include "tb_regs.h" @@ -217,8 +218,11 @@ static void tb_handle_hotplug(struct work_struct *work) { struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work); struct tb *tb = ev->tb; + struct device *dev = &tb->nhi->pdev->dev; struct tb_switch *sw; struct tb_port *port; + + pm_runtime_get(dev); mutex_lock(&tb->lock); if (!tb->hotplug_active) goto out; /* during init, suspend or shutdown */ @@ -274,6 +278,8 @@ static void tb_handle_hotplug(struct work_struct *work) out: mutex_unlock(&tb->lock); kfree(ev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } /** @@ -433,4 +439,11 @@ void thunderbolt_resume(struct tb *tb) tb->hotplug_active = true; mutex_unlock(&tb->lock); tb_info(tb, "resume finished\n"); + + /* + * If runtime resuming due to a hotplug event (rather than resuming + * from system sleep), wait for it to arrive. May take about 700 ms. + */ + if (tb->nhi->pdev->dev.power.runtime_status == RPM_RESUMING) + msleep(1000); }