summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2018-11-26 12:47:46 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-12-17 09:24:36 +0100
commit5dda1e7d7ce4afe233d2c2e647634d2f0fd99f9e (patch)
treee456bf15658877b8220e26c0536270f73ebaf7d2 /drivers/thunderbolt
parent9ca88f3c4cb5eb6983aaf7492f78e467c0e123e1 (diff)
thunderbolt: Prevent root port runtime suspend during NVM upgrade
[ Upstream commit 1830b6eeda1fed42d85f2388f79c926331a9b2d0 ] During NVM upgrade process the host router is hot-removed for a short while. During this time it is possible that the root port is moved into D3cold which would be fine if the root port could trigger PME on itself. However, many systems actually do not implement it so what happens is that the root port goes into D3cold and never wakes up unless userspace does PCI config space access, such as running 'lscpi'. For this reason we explicitly prevent the root port from runtime suspending during NVM upgrade. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r--drivers/thunderbolt/switch.c40
1 files changed, 38 insertions, 2 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 7442bc4c6433..dd9ae6f5d19c 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -864,6 +864,30 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR(key, 0600, key_show, key_store);
+static void nvm_authenticate_start(struct tb_switch *sw)
+{
+ struct pci_dev *root_port;
+
+ /*
+ * During host router NVM upgrade we should not allow root port to
+ * go into D3cold because some root ports cannot trigger PME
+ * itself. To be on the safe side keep the root port in D0 during
+ * the whole upgrade process.
+ */
+ root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev);
+ if (root_port)
+ pm_runtime_get_noresume(&root_port->dev);
+}
+
+static void nvm_authenticate_complete(struct tb_switch *sw)
+{
+ struct pci_dev *root_port;
+
+ root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev);
+ if (root_port)
+ pm_runtime_put(&root_port->dev);
+}
+
static ssize_t nvm_authenticate_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -913,10 +937,18 @@ static ssize_t nvm_authenticate_store(struct device *dev,
sw->nvm->authenticating = true;
- if (!tb_route(sw))
+ if (!tb_route(sw)) {
+ /*
+ * Keep root port from suspending as long as the
+ * NVM upgrade process is running.
+ */
+ nvm_authenticate_start(sw);
ret = nvm_authenticate_host(sw);
- else
+ if (ret)
+ nvm_authenticate_complete(sw);
+ } else {
ret = nvm_authenticate_device(sw);
+ }
pm_runtime_mark_last_busy(&sw->dev);
pm_runtime_put_autosuspend(&sw->dev);
}
@@ -1336,6 +1368,10 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
if (ret <= 0)
return ret;
+ /* Now we can allow root port to suspend again */
+ if (!tb_route(sw))
+ nvm_authenticate_complete(sw);
+
if (status) {
tb_sw_info(sw, "switch flash authentication failed\n");
tb_switch_set_uuid(sw);