From patchwork Sun Feb 12 16:07: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: 726977 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3vLtvp3PS0z9s2s for ; Mon, 13 Feb 2017 03:11:02 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751247AbdBLQLB (ORCPT ); Sun, 12 Feb 2017 11:11:01 -0500 Received: from mailout2.hostsharing.net ([83.223.90.233]:55749 "EHLO mailout2.hostsharing.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751226AbdBLQLB (ORCPT ); Sun, 12 Feb 2017 11:11:01 -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 E9BEF10189AD2; Sun, 12 Feb 2017 17:10:59 +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 353B3603E044; Sun, 12 Feb 2017 17:10:57 +0100 (CET) X-Mailbox-Line: From 920a91bf915983758d4b19b9dcf28f9fb10c70cb Mon Sep 17 00:00:00 2001 Message-Id: <920a91bf915983758d4b19b9dcf28f9fb10c70cb.1486913733.git.lukas@wunner.de> In-Reply-To: References: From: Lukas Wunner Date: Sun, 12 Feb 2017 17:07:45 +0100 Subject: [PATCH v6 8/8] thunderbolt: Runtime suspend NHI when idle To: Bjorn Helgaas Cc: Andreas Noever , linux-pci@vger.kernel.org Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org 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. Just as the rest of the Thunderbolt driver, runtime PM is an ongoing work in progress. In particular, two issues remain: * When a daisy-chained device is hotplugged and a PCIe port between the end of the chain and the host controller is in D3hot, the pciehp hotplug interrupt cannot be expected to be delivered. The plug event sent via the CIO switch to the NHI serves as a side-band signal and the NHI should runtime resume the PCIe hotplug port on which the plug event occurred. We do not support daisy chaining yet so this is only an issue for PCI tunnels established by the EFI driver. * On Cactus Ridge and newer, directly-attached DisplayPort devices are not wired to the GPU, but rather the DP signal is routed through the Thunderbolt controller. It may thus be necessary to check presence of attached DisplayPort devices when scanning the root switch and acquire a runtime ref for each of them to prevent the controller from powering off while displays are attached. Unfortunately my own machine predates Cactus Ridge and wires the DP signal directly to the GPU, so I cannot test this. 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 88fb2fb8cf4e..319ed81422c5 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -632,6 +632,8 @@ static const struct dev_pm_ops nhi_pm_ops = { * 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 e17edab13e4c..b9dd2a1ab96d 100644 --- a/drivers/thunderbolt/power.c +++ b/drivers/thunderbolt/power.c @@ -323,6 +323,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: @@ -339,6 +345,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 c6f30b1695a9..422fe6e96f09 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 24b6d30c3c86..a3fedf90e545 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); }