linux-pci.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/19] thunderbolt: Power Management improvements
@ 2020-08-19 11:58 Mika Westerberg
  2020-08-19 11:58 ` [PATCH 01/19] thunderbolt: Optimize Force Power logic Mika Westerberg
                   ` (19 more replies)
  0 siblings, 20 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

Hi all,

This series improves power management in the Thunderbolt driver. We already
have a quite complete power management on systems where Firmware based
Connection Manager is used (this is pretty much all non-Apple systems out
there) so this series adds a couple of optimizations to make certain power
transitions slightly faster, hopefully improving user experience.

Rest of the patches improve power management in the Software Connection
manager side of the driver. USB4 spec covers power management for USB4
hosts and devices, and also TBT3 compatible devices so these patches
implement that. We also switch to use device links instead of PCI quirk to
make sure the Thunderbolt/USB4 host controller is resumed before tunneled
PCIe and USB 3.x ports (so that it gets the chance to restore the tunnels
properly before). Tiger Lake systems with Software Connection Manager
enabled describe these relationships using a new ACPI _DSD property that we
parse in the driver and populate device links accordingly.

Mika Westerberg (17):
  thunderbolt: Software CM only should set force power in Tiger Lake
  thunderbolt: Use bit 31 to check if Firmware CM is running in Tiger Lake
  thunderbolt: Do not program NFC buffers for USB4 router protocol adapters
  thunderbolt: No need to log an error if tb_switch_lane_bonding_enable() fails
  thunderbolt: Send reset only to first generation routers
  thunderbolt: Tear down DP tunnels when suspending
  thunderbolt: Initialize TMU again on resume
  thunderbolt: Do not change default USB4 router notification timeout
  thunderbolt: Configure link after lane bonding is enabled
  thunderbolt: Set port configured for both ends of the link
  thunderbolt: Configure port for XDomain
  thunderbolt: Disable lane 1 for XDomain connection
  thunderbolt: Enable wakes from system suspend
  PCI / thunderbolt: Switch to use device links instead of PCI quirk
  ACPI: Export acpi_get_first_physical_node() to modules
  thunderbolt: Create device links from ACPI description
  thunderbolt: Add runtime PM for Software CM

Rajmohan Mani (2):
  thunderbolt: Optimize Force Power logic
  thunderbolt: Optimize NHI LC mailbox command processing

 drivers/acpi/bus.c            |   1 +
 drivers/pci/quirks.c          |  57 --------
 drivers/thunderbolt/Makefile  |   2 +
 drivers/thunderbolt/acpi.c    | 117 ++++++++++++++++
 drivers/thunderbolt/domain.c  |   2 +
 drivers/thunderbolt/icm.c     |   5 +-
 drivers/thunderbolt/lc.c      | 151 ++++++++++++++++----
 drivers/thunderbolt/nhi.c     |  69 ++++++++++
 drivers/thunderbolt/nhi_ops.c |  31 +++--
 drivers/thunderbolt/switch.c  | 209 +++++++++++++++++++++++-----
 drivers/thunderbolt/tb.c      | 189 +++++++++++++++++++++++--
 drivers/thunderbolt/tb.h      |  35 ++++-
 drivers/thunderbolt/tb_regs.h |  16 +++
 drivers/thunderbolt/usb4.c    | 251 ++++++++++++++++++++++++++--------
 14 files changed, 935 insertions(+), 200 deletions(-)
 create mode 100644 drivers/thunderbolt/acpi.c

-- 
2.28.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 01/19] thunderbolt: Optimize Force Power logic
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 02/19] thunderbolt: Optimize NHI LC mailbox command processing Mika Westerberg
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

From: Rajmohan Mani <rajmohan.mani@intel.com>

Currently the "Force Power" logic uses 10 retries, each with a delay of
250 ms. Thunderbolt controllers in Ice Lake and Tiger Lake platforms are
found to complete this in the order of 3 ms or so. Since this delay
is in resume path, surplus delay is effectively affecting runtime PM
resume flows.

Decrease the granularity of the delay to 3 ms and increase the number of
retries so we wait maximum of ~1 s which is the recommended timeout.
This should make runtime resume a bit faster.

Reported-by: Dana Alkattan <dana.alkattan@intel.com>
Signed-off-by: Rajmohan Mani <rajmohan.mani@intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/nhi_ops.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/thunderbolt/nhi_ops.c b/drivers/thunderbolt/nhi_ops.c
index 6795851aac95..c0d5ccbb10f5 100644
--- a/drivers/thunderbolt/nhi_ops.c
+++ b/drivers/thunderbolt/nhi_ops.c
@@ -59,7 +59,7 @@ static int icl_nhi_force_power(struct tb_nhi *nhi, bool power)
 	pci_write_config_dword(nhi->pdev, VS_CAP_22, vs_cap);
 
 	if (power) {
-		unsigned int retries = 10;
+		unsigned int retries = 350;
 		u32 val;
 
 		/* Wait until the firmware tells it is up and running */
@@ -67,7 +67,7 @@ static int icl_nhi_force_power(struct tb_nhi *nhi, bool power)
 			pci_read_config_dword(nhi->pdev, VS_CAP_9, &val);
 			if (val & VS_CAP_9_FW_READY)
 				return 0;
-			msleep(250);
+			usleep_range(3000, 3100);
 		} while (--retries);
 
 		return -ETIMEDOUT;
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 02/19] thunderbolt: Optimize NHI LC mailbox command processing
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
  2020-08-19 11:58 ` [PATCH 01/19] thunderbolt: Optimize Force Power logic Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 03/19] thunderbolt: Software CM only should set force power in Tiger Lake Mika Westerberg
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

From: Rajmohan Mani <rajmohan.mani@intel.com>

Currently the Ice Lake and Tiger Lake NHI (host controller) LC (link
controller) mailbox command processing checks for the completion of
command every 100 msecs. These controllers are found to complete this in
the order of 1 ms or so. Since this delay is in suspend path, surplus
delay is effectively affecting runtime PM suspend flows.

Optimize this so that we do the wait for 1 ms after reading the mailbox
register. This should make Ice Lake and Tiger Lake runtime suspend take
less time to complete.

Reported-by: Dana Alkattan <dana.alkattan@intel.com>
Signed-off-by: Rajmohan Mani <rajmohan.mani@intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/nhi_ops.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/thunderbolt/nhi_ops.c b/drivers/thunderbolt/nhi_ops.c
index c0d5ccbb10f5..28583f9faf46 100644
--- a/drivers/thunderbolt/nhi_ops.c
+++ b/drivers/thunderbolt/nhi_ops.c
@@ -97,7 +97,7 @@ static int icl_nhi_lc_mailbox_cmd_complete(struct tb_nhi *nhi, int timeout)
 		pci_read_config_dword(nhi->pdev, VS_CAP_18, &data);
 		if (data & VS_CAP_18_DONE)
 			goto clear;
-		msleep(100);
+		usleep_range(1000, 1100);
 	} while (time_before(jiffies, end));
 
 	return -ETIMEDOUT;
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 03/19] thunderbolt: Software CM only should set force power in Tiger Lake
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
  2020-08-19 11:58 ` [PATCH 01/19] thunderbolt: Optimize Force Power logic Mika Westerberg
  2020-08-19 11:58 ` [PATCH 02/19] thunderbolt: Optimize NHI LC mailbox command processing Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 04/19] thunderbolt: Use bit 31 to check if Firmware CM is running " Mika Westerberg
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

When Software CM is running it should not send any NHI mailbox command
during PM flows. Only force power bit needs to be set and cleared so
change Tiger Lake (well and Ice Lake) nhi_ops to take this into account.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/nhi_ops.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/drivers/thunderbolt/nhi_ops.c b/drivers/thunderbolt/nhi_ops.c
index 28583f9faf46..96da07e88c52 100644
--- a/drivers/thunderbolt/nhi_ops.c
+++ b/drivers/thunderbolt/nhi_ops.c
@@ -121,31 +121,38 @@ static void icl_nhi_set_ltr(struct tb_nhi *nhi)
 
 static int icl_nhi_suspend(struct tb_nhi *nhi)
 {
+	struct tb *tb = pci_get_drvdata(nhi->pdev);
 	int ret;
 
 	if (icl_nhi_is_device_connected(nhi))
 		return 0;
 
-	/*
-	 * If there is no device connected we need to perform both: a
-	 * handshake through LC mailbox and force power down before
-	 * entering D3.
-	 */
-	icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET);
-	ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT);
-	if (ret)
-		return ret;
+	if (tb_switch_is_icm(tb->root_switch)) {
+		/*
+		 * If there is no device connected we need to perform
+		 * both: a handshake through LC mailbox and force power
+		 * down before entering D3.
+		 */
+		icl_nhi_lc_mailbox_cmd(nhi, ICL_LC_PREPARE_FOR_RESET);
+		ret = icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT);
+		if (ret)
+			return ret;
+	}
 
 	return icl_nhi_force_power(nhi, false);
 }
 
 static int icl_nhi_suspend_noirq(struct tb_nhi *nhi, bool wakeup)
 {
+	struct tb *tb = pci_get_drvdata(nhi->pdev);
 	enum icl_lc_mailbox_cmd cmd;
 
 	if (!pm_suspend_via_firmware())
 		return icl_nhi_suspend(nhi);
 
+	if (!tb_switch_is_icm(tb->root_switch))
+		return 0;
+
 	cmd = wakeup ? ICL_LC_GO2SX : ICL_LC_GO2SX_NO_WAKE;
 	icl_nhi_lc_mailbox_cmd(nhi, cmd);
 	return icl_nhi_lc_mailbox_cmd_complete(nhi, ICL_LC_MAILBOX_TIMEOUT);
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 04/19] thunderbolt: Use bit 31 to check if Firmware CM is running in Tiger Lake
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (2 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 03/19] thunderbolt: Software CM only should set force power in Tiger Lake Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 05/19] thunderbolt: Do not program NFC buffers for USB4 router protocol adapters Mika Westerberg
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

In Tiger Lake the Firmware CM is always enabled (so bit 0 is always set)
but it may be in "pass through" mode which means it requires Software CM
instead. This can be determined by checking bit 31 instead.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/icm.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index ffcc8c3459e5..b51fc3f62b1f 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -1635,11 +1635,14 @@ static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr)
 
 static bool icm_tgl_is_supported(struct tb *tb)
 {
+	u32 val;
+
 	/*
 	 * If the firmware is not running use software CM. This platform
 	 * should fully support both.
 	 */
-	return icm_firmware_running(tb->nhi);
+	val = ioread32(tb->nhi->iobase + REG_FW_STS);
+	return !!(val & REG_FW_STS_NVM_AUTH_DONE);
 }
 
 static void icm_handle_notification(struct work_struct *work)
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 05/19] thunderbolt: Do not program NFC buffers for USB4 router protocol adapters
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (3 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 04/19] thunderbolt: Use bit 31 to check if Firmware CM is running " Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 06/19] thunderbolt: No need to log an error if tb_switch_lane_bonding_enable() fails Mika Westerberg
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

USB4 spec says that NFC buffers field is not used for protocol adapters,
only for lane adapters so make tb_port_add_nfc_credits() skip non-lane
adapters in order to follow the spec.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 698c52775eec..72756c8ceead 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -601,6 +601,13 @@ int tb_port_add_nfc_credits(struct tb_port *port, int credits)
 	if (credits == 0 || port->sw->is_unplugged)
 		return 0;
 
+	/*
+	 * USB4 restricts programming NFC buffers to lane adapters only
+	 * so skip other ports.
+	 */
+	if (tb_switch_is_usb4(port->sw) && !tb_port_is_null(port))
+		return 0;
+
 	nfc_credits = port->config.nfc_credits & ADP_CS_4_NFC_BUFFERS_MASK;
 	nfc_credits += credits;
 
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 06/19] thunderbolt: No need to log an error if tb_switch_lane_bonding_enable() fails
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (4 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 05/19] thunderbolt: Do not program NFC buffers for USB4 router protocol adapters Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 07/19] thunderbolt: Send reset only to first generation routers Mika Westerberg
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

The function already logs an error if it fails so get rid of the
duplication.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/tb.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index f507815040eb..98f268a818a0 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -592,8 +592,7 @@ static void tb_scan_port(struct tb_port *port)
 	}
 
 	/* Enable lane bonding if supported */
-	if (tb_switch_lane_bonding_enable(sw))
-		tb_sw_warn(sw, "failed to enable lane bonding\n");
+	tb_switch_lane_bonding_enable(sw);
 
 	if (tb_enable_tmu(sw))
 		tb_sw_warn(sw, "failed to enable TMU\n");
@@ -1245,8 +1244,7 @@ static void tb_restore_children(struct tb_switch *sw)
 		if (!tb_port_has_remote(port))
 			continue;
 
-		if (tb_switch_lane_bonding_enable(port->remote->sw))
-			dev_warn(&sw->dev, "failed to restore lane bonding\n");
+		tb_switch_lane_bonding_enable(port->remote->sw);
 
 		tb_restore_children(port->remote->sw);
 	}
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 07/19] thunderbolt: Send reset only to first generation routers
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (5 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 06/19] thunderbolt: No need to log an error if tb_switch_lane_bonding_enable() fails Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 08/19] thunderbolt: Tear down DP tunnels when suspending Mika Westerberg
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

First generation routers may need the reset command upon resume but it
is not supported by newer generations.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c | 21 +++++++++++----------
 drivers/thunderbolt/tb.c     |  2 +-
 drivers/thunderbolt/tb.h     |  2 +-
 3 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 72756c8ceead..fb30ea1dfc31 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -1234,23 +1234,24 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
 
 /**
  * reset_switch() - reconfigure route, enable and send TB_CFG_PKG_RESET
+ * @sw: Switch to reset
  *
  * Return: Returns 0 on success or an error code on failure.
  */
-int tb_switch_reset(struct tb *tb, u64 route)
+int tb_switch_reset(struct tb_switch *sw)
 {
 	struct tb_cfg_result res;
-	struct tb_regs_switch_header header = {
-		header.route_hi = route >> 32,
-		header.route_lo = route,
-		header.enabled = true,
-	};
-	tb_dbg(tb, "resetting switch at %llx\n", route);
-	res.err = tb_cfg_write(tb->ctl, ((u32 *) &header) + 2, route,
-			0, 2, 2, 2);
+
+	if (sw->generation > 1)
+		return 0;
+
+	tb_sw_dbg(sw, "resetting switch\n");
+
+	res.err = tb_sw_write(sw, ((u32 *) &sw->config) + 2,
+			      TB_CFG_SWITCH, 2, 2);
 	if (res.err)
 		return res.err;
-	res = tb_cfg_reset(tb->ctl, route, TB_CFG_DEFAULT_TIMEOUT);
+	res = tb_cfg_reset(sw->tb->ctl, tb_route(sw), TB_CFG_DEFAULT_TIMEOUT);
 	if (res.err > 0)
 		return -EIO;
 	return res.err;
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 98f268a818a0..a6da2d0567ae 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -1258,7 +1258,7 @@ static int tb_resume_noirq(struct tb *tb)
 	tb_dbg(tb, "resuming...\n");
 
 	/* remove any pci devices the firmware might have setup */
-	tb_switch_reset(tb, 0);
+	tb_switch_reset(tb->root_switch);
 
 	tb_switch_resume(tb->root_switch);
 	tb_free_invalid_tunnels(tb);
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index df08f6d7aaa0..69e78bbed53a 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -634,7 +634,7 @@ int tb_switch_add(struct tb_switch *sw);
 void tb_switch_remove(struct tb_switch *sw);
 void tb_switch_suspend(struct tb_switch *sw);
 int tb_switch_resume(struct tb_switch *sw);
-int tb_switch_reset(struct tb *tb, u64 route);
+int tb_switch_reset(struct tb_switch *sw);
 void tb_sw_set_unplugged(struct tb_switch *sw);
 struct tb_port *tb_switch_find_port(struct tb_switch *sw,
 				    enum tb_port_type type);
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 08/19] thunderbolt: Tear down DP tunnels when suspending
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (6 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 07/19] thunderbolt: Send reset only to first generation routers Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 09/19] thunderbolt: Initialize TMU again on resume Mika Westerberg
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

DP tunnels do not need the same kind of treatment as others because they
are created based on hot-plug events on DP adapter ports, and the
display stack does not need the tunnels to be enabled when resuming from
suspend. Also Tiger Lake Thunderbolt controller sends unplug event on D3
exit so this avoids that as well.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/tb.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index a6da2d0567ae..c35d5fec48f4 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -910,6 +910,29 @@ static void tb_dp_resource_available(struct tb *tb, struct tb_port *port)
 	tb_tunnel_dp(tb);
 }
 
+static void tb_disconnect_and_release_dp(struct tb *tb)
+{
+	struct tb_cm *tcm = tb_priv(tb);
+	struct tb_tunnel *tunnel, *n;
+
+	/*
+	 * Tear down all DP tunnels and release their resources. They
+	 * will be re-established after resume based on plug events.
+	 */
+	list_for_each_entry_safe_reverse(tunnel, n, &tcm->tunnel_list, list) {
+		if (tb_tunnel_is_dp(tunnel))
+			tb_deactivate_and_free_tunnel(tunnel);
+	}
+
+	while (!list_empty(&tcm->dp_resources)) {
+		struct tb_port *port;
+
+		port = list_first_entry(&tcm->dp_resources,
+					struct tb_port, list);
+		list_del_init(&port->list);
+	}
+}
+
 static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
 {
 	struct tb_port *up, *down, *port;
@@ -1226,6 +1249,7 @@ static int tb_suspend_noirq(struct tb *tb)
 	struct tb_cm *tcm = tb_priv(tb);
 
 	tb_dbg(tb, "suspending...\n");
+	tb_disconnect_and_release_dp(tb);
 	tb_switch_suspend(tb->root_switch);
 	tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
 	tb_dbg(tb, "suspend finished\n");
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 09/19] thunderbolt: Initialize TMU again on resume
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (7 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 08/19] thunderbolt: Tear down DP tunnels when suspending Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 10/19] thunderbolt: Do not change default USB4 router notification timeout Mika Westerberg
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

The TMU will be reset after router exits sleep so in order to
re-configure it upon resume make sure the structure is initialized again
based on the current hardware state.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index fb30ea1dfc31..de186d6ed166 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -2534,6 +2534,10 @@ int tb_switch_resume(struct tb_switch *sw)
 	if (err)
 		return err;
 
+	err = tb_switch_tmu_init(sw);
+	if (err)
+		return err;
+
 	/* check for surviving downstream switches */
 	tb_switch_for_each_port(sw, port) {
 		if (!tb_port_has_remote(port) && !port->xdomain)
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 10/19] thunderbolt: Do not change default USB4 router notification timeout
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (8 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 09/19] thunderbolt: Initialize TMU again on resume Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 11/19] thunderbolt: Configure link after lane bonding is enabled Mika Westerberg
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

Some early stage USB4 devices do not like that any of the enumerating
router config space fields (ROUTER_CS_1 - ROUTER_CS_4) are written after
the initial enumeration for example when entering sleep states. The
default timeout by the USB4 spec is 10 ms which should be fine for the
driver to handle.

For this reason do not change the notification timeout from the default
10 ms for USB4 routers.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index de186d6ed166..e1ba8215144b 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -1269,7 +1269,7 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active)
 	u32 data;
 	int res;
 
-	if (tb_switch_is_icm(sw))
+	if (tb_switch_is_icm(sw) || tb_switch_is_usb4(sw))
 		return 0;
 
 	sw->config.plug_events_delay = 0xff;
@@ -1277,10 +1277,6 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active)
 	if (res)
 		return res;
 
-	/* Plug events are always enabled in USB4 */
-	if (tb_switch_is_usb4(sw))
-		return 0;
-
 	res = tb_sw_read(sw, &data, TB_CFG_SWITCH, sw->cap_plug_events + 1, 1);
 	if (res)
 		return res;
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 11/19] thunderbolt: Configure link after lane bonding is enabled
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (9 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 10/19] thunderbolt: Do not change default USB4 router notification timeout Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 12/19] thunderbolt: Set port configured for both ends of the link Mika Westerberg
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

During testing it was noticed that the link is not properly restored
after the domain exits sleep if the link configured bits are set before
lane bonding is enabled. The USB4 spec does not say in which order these
need to be set but setting link configured afterwards makes the link
restoration work so we do that instead.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/lc.c     |  6 ----
 drivers/thunderbolt/switch.c | 55 +++++++++++++++++++++++++++---------
 drivers/thunderbolt/tb.c     |  5 ++++
 drivers/thunderbolt/tb.h     |  2 ++
 drivers/thunderbolt/usb4.c   |  6 ----
 5 files changed, 49 insertions(+), 25 deletions(-)

diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
index 19be627d090f..b2f62ba0421d 100644
--- a/drivers/thunderbolt/lc.c
+++ b/drivers/thunderbolt/lc.c
@@ -94,9 +94,6 @@ int tb_lc_configure_link(struct tb_switch *sw)
 	struct tb_port *up, *down;
 	int ret;
 
-	if (!tb_route(sw) || tb_switch_is_icm(sw))
-		return 0;
-
 	up = tb_upstream_port(sw);
 	down = tb_port_at(tb_route(sw), tb_to_switch(sw->dev.parent));
 
@@ -124,9 +121,6 @@ void tb_lc_unconfigure_link(struct tb_switch *sw)
 {
 	struct tb_port *up, *down;
 
-	if (sw->is_unplugged || !tb_route(sw) || tb_switch_is_icm(sw))
-		return;
-
 	up = tb_upstream_port(sw);
 	down = tb_port_at(tb_route(sw), tb_to_switch(sw->dev.parent));
 
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index e1ba8215144b..ecc47ea81bb6 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -2012,10 +2012,6 @@ int tb_switch_configure(struct tb_switch *sw)
 			return ret;
 
 		ret = usb4_switch_setup(sw);
-		if (ret)
-			return ret;
-
-		ret = usb4_switch_configure_link(sw);
 	} else {
 		if (sw->config.vendor_id != PCI_VENDOR_ID_INTEL)
 			tb_sw_warn(sw, "unknown switch vendor id %#x\n",
@@ -2029,10 +2025,6 @@ int tb_switch_configure(struct tb_switch *sw)
 		/* Enumerate the switch */
 		ret = tb_sw_write(sw, (u32 *)&sw->config + 1, TB_CFG_SWITCH,
 				  ROUTER_CS_1, 3);
-		if (ret)
-			return ret;
-
-		ret = tb_lc_configure_link(sw);
 	}
 	if (ret)
 		return ret;
@@ -2315,6 +2307,48 @@ void tb_switch_lane_bonding_disable(struct tb_switch *sw)
 	tb_sw_dbg(sw, "lane bonding disabled\n");
 }
 
+/**
+ * tb_switch_configure_link() - Set link configured
+ * @sw: Switch whose link is configured
+ *
+ * Sets the link upstream from @sw configured (from both ends) so that
+ * it will not be disconnected when the domain exits sleep. Can be
+ * called for any switch.
+ *
+ * It is recommended that this is called after lane bonding is enabled.
+ *
+ * Returns %0 on success and negative errno in case of error.
+ */
+int tb_switch_configure_link(struct tb_switch *sw)
+{
+	if (!tb_route(sw) || tb_switch_is_icm(sw))
+		return 0;
+
+	if (tb_switch_is_usb4(sw))
+		return usb4_switch_configure_link(sw);
+	return tb_lc_configure_link(sw);
+}
+
+/**
+ * tb_switch_unconfigure_link() - Unconfigure link
+ * @sw: Switch whose link is unconfigured
+ *
+ * Sets the link unconfigured so the @sw will be disconnected if the
+ * domain exists sleep.
+ */
+void tb_switch_unconfigure_link(struct tb_switch *sw)
+{
+	if (sw->is_unplugged)
+		return;
+	if (!tb_route(sw) || tb_switch_is_icm(sw))
+		return;
+
+	if (tb_switch_is_usb4(sw))
+		usb4_switch_unconfigure_link(sw);
+	else
+		tb_lc_unconfigure_link(sw);
+}
+
 /**
  * tb_switch_add() - Add a switch to the domain
  * @sw: Switch to add
@@ -2449,11 +2483,6 @@ void tb_switch_remove(struct tb_switch *sw)
 	if (!sw->is_unplugged)
 		tb_plug_events_active(sw, false);
 
-	if (tb_switch_is_usb4(sw))
-		usb4_switch_unconfigure_link(sw);
-	else
-		tb_lc_unconfigure_link(sw);
-
 	tb_switch_nvm_remove(sw);
 
 	if (tb_route(sw))
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index c35d5fec48f4..54a4daf0b1b4 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -593,6 +593,8 @@ static void tb_scan_port(struct tb_port *port)
 
 	/* Enable lane bonding if supported */
 	tb_switch_lane_bonding_enable(sw);
+	/* Set the link configured */
+	tb_switch_configure_link(sw);
 
 	if (tb_enable_tmu(sw))
 		tb_sw_warn(sw, "failed to enable TMU\n");
@@ -681,6 +683,7 @@ static void tb_free_unplugged_children(struct tb_switch *sw)
 		if (port->remote->sw->is_unplugged) {
 			tb_retimer_remove_all(port);
 			tb_remove_dp_resources(port->remote->sw);
+			tb_switch_unconfigure_link(port->remote->sw);
 			tb_switch_lane_bonding_disable(port->remote->sw);
 			tb_switch_remove(port->remote->sw);
 			port->remote = NULL;
@@ -1076,6 +1079,7 @@ static void tb_handle_hotplug(struct work_struct *work)
 			tb_free_invalid_tunnels(tb);
 			tb_remove_dp_resources(port->remote->sw);
 			tb_switch_tmu_disable(port->remote->sw);
+			tb_switch_unconfigure_link(port->remote->sw);
 			tb_switch_lane_bonding_disable(port->remote->sw);
 			tb_switch_remove(port->remote->sw);
 			port->remote = NULL;
@@ -1269,6 +1273,7 @@ static void tb_restore_children(struct tb_switch *sw)
 			continue;
 
 		tb_switch_lane_bonding_enable(port->remote->sw);
+		tb_switch_configure_link(port->remote->sw);
 
 		tb_restore_children(port->remote->sw);
 	}
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 69e78bbed53a..dcdc886412f5 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -767,6 +767,8 @@ static inline bool tb_switch_is_icm(const struct tb_switch *sw)
 
 int tb_switch_lane_bonding_enable(struct tb_switch *sw);
 void tb_switch_lane_bonding_disable(struct tb_switch *sw);
+int tb_switch_configure_link(struct tb_switch *sw);
+void tb_switch_unconfigure_link(struct tb_switch *sw);
 
 bool tb_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in);
 int tb_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in);
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index 2b8355e6b65f..dd601a6db23c 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -368,9 +368,6 @@ int usb4_switch_configure_link(struct tb_switch *sw)
 {
 	struct tb_port *up;
 
-	if (!tb_route(sw))
-		return 0;
-
 	up = tb_upstream_port(sw);
 	return usb4_set_port_configured(up, true);
 }
@@ -385,9 +382,6 @@ void usb4_switch_unconfigure_link(struct tb_switch *sw)
 {
 	struct tb_port *up;
 
-	if (sw->is_unplugged || !tb_route(sw))
-		return;
-
 	up = tb_upstream_port(sw);
 	usb4_set_port_configured(up, false);
 }
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 12/19] thunderbolt: Set port configured for both ends of the link
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (10 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 11/19] thunderbolt: Configure link after lane bonding is enabled Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:58 ` [PATCH 13/19] thunderbolt: Configure port for XDomain Mika Westerberg
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

Both ends of the link needs to have this set. Otherwise the link is not
re-established properly after sleep. Now since it is possible to have
mixed USB4 and Thunderbolt 1, 2 and 3 devices we need to split the link
configuration functionality to happen per port so we can pick the
correct implementation.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/lc.c     | 48 +++++--------------
 drivers/thunderbolt/switch.c | 33 ++++++++++---
 drivers/thunderbolt/tb.h     |  8 ++--
 drivers/thunderbolt/usb4.c   | 92 +++++++++++++++++-------------------
 4 files changed, 87 insertions(+), 94 deletions(-)

diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
index b2f62ba0421d..5c209a570360 100644
--- a/drivers/thunderbolt/lc.c
+++ b/drivers/thunderbolt/lc.c
@@ -45,7 +45,7 @@ static int find_port_lc_cap(struct tb_port *port)
 	return sw->cap_lc + start + phys * size;
 }
 
-static int tb_lc_configure_lane(struct tb_port *port, bool configure)
+static int tb_lc_set_port_configured(struct tb_port *port, bool configured)
 {
 	bool upstream = tb_is_upstream_port(port);
 	struct tb_switch *sw = port->sw;
@@ -69,7 +69,7 @@ static int tb_lc_configure_lane(struct tb_port *port, bool configure)
 	else
 		lane = TB_LC_SX_CTRL_L2C;
 
-	if (configure) {
+	if (configured) {
 		ctrl |= lane;
 		if (upstream)
 			ctrl |= TB_LC_SX_CTRL_UPSTREAM;
@@ -83,49 +83,25 @@ static int tb_lc_configure_lane(struct tb_port *port, bool configure)
 }
 
 /**
- * tb_lc_configure_link() - Let LC know about configured link
- * @sw: Switch that is being added
+ * tb_lc_configure_port() - Let LC know about configured port
+ * @port: Port that is set as configured
  *
- * Informs LC of both parent switch and @sw that there is established
- * link between the two.
+ * Sets the port configured for power management purposes.
  */
-int tb_lc_configure_link(struct tb_switch *sw)
+int tb_lc_configure_port(struct tb_port *port)
 {
-	struct tb_port *up, *down;
-	int ret;
-
-	up = tb_upstream_port(sw);
-	down = tb_port_at(tb_route(sw), tb_to_switch(sw->dev.parent));
-
-	/* Configure parent link toward this switch */
-	ret = tb_lc_configure_lane(down, true);
-	if (ret)
-		return ret;
-
-	/* Configure upstream link from this switch to the parent */
-	ret = tb_lc_configure_lane(up, true);
-	if (ret)
-		tb_lc_configure_lane(down, false);
-
-	return ret;
+	return tb_lc_set_port_configured(port, true);
 }
 
 /**
- * tb_lc_unconfigure_link() - Let LC know about unconfigured link
- * @sw: Switch to unconfigure
+ * tb_lc_unconfigure_port() - Let LC know about unconfigured port
+ * @port: Port that is set as configured
  *
- * Informs LC of both parent switch and @sw that the link between the
- * two does not exist anymore.
+ * Sets the port unconfigured for power management purposes.
  */
-void tb_lc_unconfigure_link(struct tb_switch *sw)
+void tb_lc_unconfigure_port(struct tb_port *port)
 {
-	struct tb_port *up, *down;
-
-	up = tb_upstream_port(sw);
-	down = tb_port_at(tb_route(sw), tb_to_switch(sw->dev.parent));
-
-	tb_lc_configure_lane(up, false);
-	tb_lc_configure_lane(down, false);
+	tb_lc_set_port_configured(port, false);
 }
 
 /**
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index ecc47ea81bb6..1c45f8baf487 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -2321,12 +2321,24 @@ void tb_switch_lane_bonding_disable(struct tb_switch *sw)
  */
 int tb_switch_configure_link(struct tb_switch *sw)
 {
+	struct tb_port *up, *down;
+	int ret;
+
 	if (!tb_route(sw) || tb_switch_is_icm(sw))
 		return 0;
 
-	if (tb_switch_is_usb4(sw))
-		return usb4_switch_configure_link(sw);
-	return tb_lc_configure_link(sw);
+	up = tb_upstream_port(sw);
+	if (tb_switch_is_usb4(up->sw))
+		ret = usb4_port_configure(up);
+	else
+		ret = tb_lc_configure_port(up);
+	if (ret)
+		return ret;
+
+	down = up->remote;
+	if (tb_switch_is_usb4(down->sw))
+		return usb4_port_configure(down);
+	return tb_lc_configure_port(down);
 }
 
 /**
@@ -2338,15 +2350,24 @@ int tb_switch_configure_link(struct tb_switch *sw)
  */
 void tb_switch_unconfigure_link(struct tb_switch *sw)
 {
+	struct tb_port *up, *down;
+
 	if (sw->is_unplugged)
 		return;
 	if (!tb_route(sw) || tb_switch_is_icm(sw))
 		return;
 
-	if (tb_switch_is_usb4(sw))
-		usb4_switch_unconfigure_link(sw);
+	up = tb_upstream_port(sw);
+	if (tb_switch_is_usb4(up->sw))
+		usb4_port_unconfigure(up);
+	else
+		tb_lc_unconfigure_port(up);
+
+	down = up->remote;
+	if (tb_switch_is_usb4(down->sw))
+		usb4_port_unconfigure(down);
 	else
-		tb_lc_unconfigure_link(sw);
+		tb_lc_unconfigure_port(down);
 }
 
 /**
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index dcdc886412f5..082ae9da4cbc 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -846,8 +846,8 @@ int tb_drom_read(struct tb_switch *sw);
 int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid);
 
 int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid);
-int tb_lc_configure_link(struct tb_switch *sw);
-void tb_lc_unconfigure_link(struct tb_switch *sw);
+int tb_lc_configure_port(struct tb_port *port);
+void tb_lc_unconfigure_port(struct tb_port *port);
 int tb_lc_set_sleep(struct tb_switch *sw);
 bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
 bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in);
@@ -902,8 +902,6 @@ int usb4_switch_setup(struct tb_switch *sw);
 int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid);
 int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf,
 			  size_t size);
-int usb4_switch_configure_link(struct tb_switch *sw);
-void usb4_switch_unconfigure_link(struct tb_switch *sw);
 bool usb4_switch_lane_bonding_possible(struct tb_switch *sw);
 int usb4_switch_set_sleep(struct tb_switch *sw);
 int usb4_switch_nvm_sector_size(struct tb_switch *sw);
@@ -921,6 +919,8 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
 					  const struct tb_port *port);
 
 int usb4_port_unlock(struct tb_port *port);
+int usb4_port_configure(struct tb_port *port);
+void usb4_port_unconfigure(struct tb_port *port);
 int usb4_port_enumerate_retimers(struct tb_port *port);
 
 int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index dd601a6db23c..b2677427789f 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -338,54 +338,6 @@ int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf,
 				 usb4_switch_drom_read_block, sw);
 }
 
-static int usb4_set_port_configured(struct tb_port *port, bool configured)
-{
-	int ret;
-	u32 val;
-
-	ret = tb_port_read(port, &val, TB_CFG_PORT,
-			   port->cap_usb4 + PORT_CS_19, 1);
-	if (ret)
-		return ret;
-
-	if (configured)
-		val |= PORT_CS_19_PC;
-	else
-		val &= ~PORT_CS_19_PC;
-
-	return tb_port_write(port, &val, TB_CFG_PORT,
-			     port->cap_usb4 + PORT_CS_19, 1);
-}
-
-/**
- * usb4_switch_configure_link() - Set upstream USB4 link configured
- * @sw: USB4 router
- *
- * Sets the upstream USB4 link to be configured for power management
- * purposes.
- */
-int usb4_switch_configure_link(struct tb_switch *sw)
-{
-	struct tb_port *up;
-
-	up = tb_upstream_port(sw);
-	return usb4_set_port_configured(up, true);
-}
-
-/**
- * usb4_switch_unconfigure_link() - Un-set upstream USB4 link configuration
- * @sw: USB4 router
- *
- * Reverse of usb4_switch_configure_link().
- */
-void usb4_switch_unconfigure_link(struct tb_switch *sw)
-{
-	struct tb_port *up;
-
-	up = tb_upstream_port(sw);
-	usb4_set_port_configured(up, false);
-}
-
 /**
  * usb4_switch_lane_bonding_possible() - Are conditions met for lane bonding
  * @sw: USB4 router
@@ -789,6 +741,50 @@ int usb4_port_unlock(struct tb_port *port)
 	return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_4, 1);
 }
 
+static int usb4_port_set_configured(struct tb_port *port, bool configured)
+{
+	int ret;
+	u32 val;
+
+	if (!port->cap_usb4)
+		return -EINVAL;
+
+	ret = tb_port_read(port, &val, TB_CFG_PORT,
+			   port->cap_usb4 + PORT_CS_19, 1);
+	if (ret)
+		return ret;
+
+	if (configured)
+		val |= PORT_CS_19_PC;
+	else
+		val &= ~PORT_CS_19_PC;
+
+	return tb_port_write(port, &val, TB_CFG_PORT,
+			     port->cap_usb4 + PORT_CS_19, 1);
+}
+
+/**
+ * usb4_port_configure() - Set USB4 port configured
+ * @port: USB4 router
+ *
+ * Sets the USB4 link to be configured for power management purposes.
+ */
+int usb4_port_configure(struct tb_port *port)
+{
+	return usb4_port_set_configured(port, true);
+}
+
+/**
+ * usb4_port_unconfigure() - Set USB4 port unconfigured
+ * @port: USB4 router
+ *
+ * Sets the USB4 link to be unconfigured for power management purposes.
+ */
+void usb4_port_unconfigure(struct tb_port *port)
+{
+	usb4_port_set_configured(port, false);
+}
+
 static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit,
 				  u32 value, int timeout_msec)
 {
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 13/19] thunderbolt: Configure port for XDomain
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (11 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 12/19] thunderbolt: Set port configured for both ends of the link Mika Westerberg
@ 2020-08-19 11:58 ` Mika Westerberg
  2020-08-19 11:59 ` [PATCH 14/19] thunderbolt: Disable lane 1 for XDomain connection Mika Westerberg
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:58 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

When the port is connected to another host it should be marked as such
in the USB4 port capability. This information is used by the router
during sleep and wakeup.

Also do the same for legacy switches via link controller vendor specific
registers.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/lc.c      | 54 +++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.c      | 32 ++++++++++++++++++---
 drivers/thunderbolt/tb.h      |  4 +++
 drivers/thunderbolt/tb_regs.h |  3 ++
 drivers/thunderbolt/usb4.c    | 45 +++++++++++++++++++++++++++++
 5 files changed, 134 insertions(+), 4 deletions(-)

diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
index 5c209a570360..44647fa1ec1c 100644
--- a/drivers/thunderbolt/lc.c
+++ b/drivers/thunderbolt/lc.c
@@ -104,6 +104,60 @@ void tb_lc_unconfigure_port(struct tb_port *port)
 	tb_lc_set_port_configured(port, false);
 }
 
+static int tb_lc_set_xdomain_configured(struct tb_port *port, bool configure)
+{
+	struct tb_switch *sw = port->sw;
+	u32 ctrl, lane;
+	int cap, ret;
+
+	if (sw->generation < 2)
+		return 0;
+
+	cap = find_port_lc_cap(port);
+	if (cap < 0)
+		return cap;
+
+	ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
+	if (ret)
+		return ret;
+
+	/* Resolve correct lane */
+	if (port->port % 2)
+		lane = TB_LC_SX_CTRL_L1D;
+	else
+		lane = TB_LC_SX_CTRL_L2D;
+
+	if (configure)
+		ctrl |= lane;
+	else
+		ctrl &= ~lane;
+
+	return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
+}
+
+/**
+ * tb_lc_configure_xdomain() - Inform LC that the link is XDomain
+ * @port: Switch downstream port connected to another host
+ *
+ * Sets the lane configured for XDomain accordingly so that the LC knows
+ * about this. Returns %0 in success and negative errno in failure.
+ */
+int tb_lc_configure_xdomain(struct tb_port *port)
+{
+	return tb_lc_set_xdomain_configured(port, true);
+}
+
+/**
+ * tb_lc_unconfigure_xdomain() - Unconfigure XDomain from port
+ * @port: Switch downstream port that was connected to another host
+ *
+ * Unsets the lane XDomain configuration.
+ */
+void tb_lc_unconfigure_xdomain(struct tb_port *port)
+{
+	tb_lc_set_xdomain_configured(port, false);
+}
+
 /**
  * tb_lc_set_sleep() - Inform LC that the switch is going to sleep
  * @sw: Switch to set sleep
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 54a4daf0b1b4..602e00e0b45e 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -140,6 +140,21 @@ static void tb_discover_tunnels(struct tb_switch *sw)
 	}
 }
 
+static int tb_port_configure_xdomain(struct tb_port *port)
+{
+	if (tb_switch_is_usb4(port->sw))
+		return usb4_port_configure_xdomain(port);
+	return tb_lc_configure_xdomain(port);
+}
+
+static void tb_port_unconfigure_xdomain(struct tb_port *port)
+{
+	if (tb_switch_is_usb4(port->sw))
+		usb4_port_unconfigure_xdomain(port);
+	else
+		tb_lc_unconfigure_xdomain(port);
+}
+
 static void tb_scan_xdomain(struct tb_port *port)
 {
 	struct tb_switch *sw = port->sw;
@@ -158,6 +173,7 @@ static void tb_scan_xdomain(struct tb_port *port)
 			      NULL);
 	if (xd) {
 		tb_port_at(route, sw)->xdomain = xd;
+		tb_port_configure_xdomain(port);
 		tb_xdomain_add(xd);
 	}
 }
@@ -566,6 +582,7 @@ static void tb_scan_port(struct tb_port *port)
 	 */
 	if (port->xdomain) {
 		tb_xdomain_remove(port->xdomain);
+		tb_port_unconfigure_xdomain(port);
 		port->xdomain = NULL;
 	}
 
@@ -1047,6 +1064,7 @@ static void tb_handle_hotplug(struct work_struct *work)
 	struct tb_cm *tcm = tb_priv(tb);
 	struct tb_switch *sw;
 	struct tb_port *port;
+
 	mutex_lock(&tb->lock);
 	if (!tcm->hotplug_active)
 		goto out; /* during init, suspend or shutdown */
@@ -1103,6 +1121,7 @@ static void tb_handle_hotplug(struct work_struct *work)
 			port->xdomain = NULL;
 			__tb_disconnect_xdomain_paths(tb, xd);
 			tb_xdomain_put(xd);
+			tb_port_unconfigure_xdomain(port);
 		} else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) {
 			tb_dp_resource_unavailable(tb, port);
 		} else {
@@ -1269,13 +1288,17 @@ static void tb_restore_children(struct tb_switch *sw)
 		tb_sw_warn(sw, "failed to restore TMU configuration\n");
 
 	tb_switch_for_each_port(sw, port) {
-		if (!tb_port_has_remote(port))
+		if (!tb_port_has_remote(port) && !port->xdomain)
 			continue;
 
-		tb_switch_lane_bonding_enable(port->remote->sw);
-		tb_switch_configure_link(port->remote->sw);
+		if (port->remote) {
+			tb_switch_lane_bonding_enable(port->remote->sw);
+			tb_switch_configure_link(port->remote->sw);
 
-		tb_restore_children(port->remote->sw);
+			tb_restore_children(port->remote->sw);
+		} else if (port->xdomain) {
+			tb_port_configure_xdomain(port);
+		}
 	}
 }
 
@@ -1321,6 +1344,7 @@ static int tb_free_unplugged_xdomains(struct tb_switch *sw)
 		if (port->xdomain && port->xdomain->is_unplugged) {
 			tb_retimer_remove_all(port);
 			tb_xdomain_remove(port->xdomain);
+			tb_port_unconfigure_xdomain(port);
 			port->xdomain = NULL;
 			ret++;
 		} else if (port->remote) {
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 082ae9da4cbc..dbe332c3e95e 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -848,6 +848,8 @@ int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid);
 int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid);
 int tb_lc_configure_port(struct tb_port *port);
 void tb_lc_unconfigure_port(struct tb_port *port);
+int tb_lc_configure_xdomain(struct tb_port *port);
+void tb_lc_unconfigure_xdomain(struct tb_port *port);
 int tb_lc_set_sleep(struct tb_switch *sw);
 bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
 bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in);
@@ -921,6 +923,8 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
 int usb4_port_unlock(struct tb_port *port);
 int usb4_port_configure(struct tb_port *port);
 void usb4_port_unconfigure(struct tb_port *port);
+int usb4_port_configure_xdomain(struct tb_port *port);
+void usb4_port_unconfigure_xdomain(struct tb_port *port);
 int usb4_port_enumerate_retimers(struct tb_port *port);
 
 int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index fd4fc144d17f..a553be24f1c0 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -303,6 +303,7 @@ struct tb_regs_port_header {
 #define PORT_CS_18_TCM				BIT(9)
 #define PORT_CS_19				0x13
 #define PORT_CS_19_PC				BIT(3)
+#define PORT_CS_19_PID				BIT(4)
 
 /* Display Port adapter registers */
 #define ADP_DP_CS_0				0x00
@@ -417,7 +418,9 @@ struct tb_regs_hop {
 
 #define TB_LC_SX_CTRL			0x96
 #define TB_LC_SX_CTRL_L1C		BIT(16)
+#define TB_LC_SX_CTRL_L1D		BIT(17)
 #define TB_LC_SX_CTRL_L2C		BIT(20)
+#define TB_LC_SX_CTRL_L2D		BIT(21)
 #define TB_LC_SX_CTRL_UPSTREAM		BIT(30)
 #define TB_LC_SX_CTRL_SLP		BIT(31)
 
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index b2677427789f..59b8b51d1fa4 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -785,6 +785,51 @@ void usb4_port_unconfigure(struct tb_port *port)
 	usb4_port_set_configured(port, false);
 }
 
+static int usb4_set_xdomain_configured(struct tb_port *port, bool configured)
+{
+	int ret;
+	u32 val;
+
+	if (!port->cap_usb4)
+		return -EINVAL;
+
+	ret = tb_port_read(port, &val, TB_CFG_PORT,
+			   port->cap_usb4 + PORT_CS_19, 1);
+	if (ret)
+		return ret;
+
+	if (configured)
+		val |= PORT_CS_19_PID;
+	else
+		val &= ~PORT_CS_19_PID;
+
+	return tb_port_write(port, &val, TB_CFG_PORT,
+			     port->cap_usb4 + PORT_CS_19, 1);
+}
+
+/**
+ * usb4_port_configure_xdomain() - Configure port for XDomain
+ * @port: USB4 port connected to another host
+ *
+ * Marks the USB4 port as being connected to another host. Returns %0 in
+ * success and negative errno in failure.
+ */
+int usb4_port_configure_xdomain(struct tb_port *port)
+{
+	return usb4_set_xdomain_configured(port, true);
+}
+
+/**
+ * usb4_port_unconfigure_xdomain() - Unconfigure port for XDomain
+ * @port: USB4 port that was connected to another host
+ *
+ * Clears USB4 port from being marked as XDomain.
+ */
+void usb4_port_unconfigure_xdomain(struct tb_port *port)
+{
+	usb4_set_xdomain_configured(port, false);
+}
+
 static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit,
 				  u32 value, int timeout_msec)
 {
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 14/19] thunderbolt: Disable lane 1 for XDomain connection
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (12 preceding siblings ...)
  2020-08-19 11:58 ` [PATCH 13/19] thunderbolt: Configure port for XDomain Mika Westerberg
@ 2020-08-19 11:59 ` Mika Westerberg
  2020-08-19 11:59 ` [PATCH 15/19] thunderbolt: Enable wakes from system suspend Mika Westerberg
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:59 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

USB4 spec mandates that the lane 1 should be disabled if lanes are not
bonded. For host-to-host connections (XDomain) we don't support lane
bonding so in order to be compatible with the spec, disable lane 1 when
another host is connected.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c  | 44 +++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.c      |  8 +++++++
 drivers/thunderbolt/tb.h      |  2 ++
 drivers/thunderbolt/tb_regs.h |  1 +
 4 files changed, 55 insertions(+)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 1c45f8baf487..a8df25dae57b 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -673,6 +673,50 @@ int tb_port_unlock(struct tb_port *port)
 	return 0;
 }
 
+static int __tb_port_enable(struct tb_port *port, bool enable)
+{
+	int ret;
+	u32 phy;
+
+	if (!tb_port_is_null(port))
+		return -EINVAL;
+
+	ret = tb_port_read(port, &phy, TB_CFG_PORT,
+			   port->cap_phy + LANE_ADP_CS_1, 1);
+	if (ret)
+		return ret;
+
+	if (enable)
+		phy &= ~LANE_ADP_CS_1_LD;
+	else
+		phy |= LANE_ADP_CS_1_LD;
+
+	return tb_port_write(port, &phy, TB_CFG_PORT,
+			     port->cap_phy + LANE_ADP_CS_1, 1);
+}
+
+/**
+ * tb_port_enable() - Enable lane adapter
+ * @port: Port to enable (can be %NULL)
+ *
+ * This is used for lane 0 and 1 adapters to enable it.
+ */
+int tb_port_enable(struct tb_port *port)
+{
+	return __tb_port_enable(port, true);
+}
+
+/**
+ * tb_port_disable() - Disable lane adapter
+ * @port: Port to disable (can be %NULL)
+ *
+ * This is used for lane 0 and 1 adapters to disable it.
+ */
+int tb_port_disable(struct tb_port *port)
+{
+	return __tb_port_enable(port, false);
+}
+
 /**
  * tb_init_port() - initialize a port
  *
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 602e00e0b45e..214e47656be6 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -142,6 +142,12 @@ static void tb_discover_tunnels(struct tb_switch *sw)
 
 static int tb_port_configure_xdomain(struct tb_port *port)
 {
+	/*
+	 * XDomain paths currently only support single lane so we must
+	 * disable the other lane according to USB4 spec.
+	 */
+	tb_port_disable(port->dual_link_port);
+
 	if (tb_switch_is_usb4(port->sw))
 		return usb4_port_configure_xdomain(port);
 	return tb_lc_configure_xdomain(port);
@@ -153,6 +159,8 @@ static void tb_port_unconfigure_xdomain(struct tb_port *port)
 		usb4_port_unconfigure_xdomain(port);
 	else
 		tb_lc_unconfigure_xdomain(port);
+
+	tb_port_enable(port->dual_link_port);
 }
 
 static void tb_scan_xdomain(struct tb_port *port)
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index dbe332c3e95e..f58cd3fa98ea 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -790,6 +790,8 @@ int tb_port_add_nfc_credits(struct tb_port *port, int credits);
 int tb_port_set_initial_credits(struct tb_port *port, u32 credits);
 int tb_port_clear_counter(struct tb_port *port, int counter);
 int tb_port_unlock(struct tb_port *port);
+int tb_port_enable(struct tb_port *port);
+int tb_port_disable(struct tb_port *port);
 int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_in_hopid(struct tb_port *port, int hopid);
 int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index a553be24f1c0..d1a40baa63d2 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -279,6 +279,7 @@ struct tb_regs_port_header {
 #define LANE_ADP_CS_1_TARGET_WIDTH_SHIFT	4
 #define LANE_ADP_CS_1_TARGET_WIDTH_SINGLE	0x1
 #define LANE_ADP_CS_1_TARGET_WIDTH_DUAL		0x3
+#define LANE_ADP_CS_1_LD			BIT(14)
 #define LANE_ADP_CS_1_LB			BIT(15)
 #define LANE_ADP_CS_1_CURRENT_SPEED_MASK	GENMASK(19, 16)
 #define LANE_ADP_CS_1_CURRENT_SPEED_SHIFT	16
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 15/19] thunderbolt: Enable wakes from system suspend
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (13 preceding siblings ...)
  2020-08-19 11:59 ` [PATCH 14/19] thunderbolt: Disable lane 1 for XDomain connection Mika Westerberg
@ 2020-08-19 11:59 ` Mika Westerberg
  2020-08-19 11:59 ` [PATCH 16/19] PCI / thunderbolt: Switch to use device links instead of PCI quirk Mika Westerberg
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:59 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

In order for the router and the whole domain to wake up from system
suspend states we need to enable wakes for the connected routers. For
device routers we enable wakes from PCIe and USB 3.x. This allows
devices such as keyboards connected to USB 3.x hub that is tunneled to
wake the system up as expected. For all routers we enabled wake on USB4
for each connected ports. This is used to propagate the wake from router
to another.

Do the same for legacy routers through link controller vendor specific
registers as documented in USB4 spec chapter 13.

While there correct kernel-doc of usb4_switch_set_sleep() -- it does not
enable wakes instead there is a separate function (usb4_switch_set_wake())
that does.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/domain.c  |   2 +
 drivers/thunderbolt/lc.c      |  67 ++++++++++++++++++++
 drivers/thunderbolt/nhi.c     |   2 +
 drivers/thunderbolt/switch.c  |  30 ++++++++-
 drivers/thunderbolt/tb.h      |   9 +++
 drivers/thunderbolt/tb_regs.h |  12 ++++
 drivers/thunderbolt/usb4.c    | 112 +++++++++++++++++++++++++++++++++-
 7 files changed, 231 insertions(+), 3 deletions(-)

diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c
index bba4cbfa9759..7ca6a2b34ddc 100644
--- a/drivers/thunderbolt/domain.c
+++ b/drivers/thunderbolt/domain.c
@@ -455,6 +455,8 @@ int tb_domain_add(struct tb *tb)
 	/* This starts event processing */
 	mutex_unlock(&tb->lock);
 
+	device_init_wakeup(&tb->dev, true);
+
 	pm_runtime_no_callbacks(&tb->dev);
 	pm_runtime_set_active(&tb->dev);
 	pm_runtime_enable(&tb->dev);
diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
index 44647fa1ec1c..41e6c738f6c8 100644
--- a/drivers/thunderbolt/lc.c
+++ b/drivers/thunderbolt/lc.c
@@ -158,6 +158,73 @@ void tb_lc_unconfigure_xdomain(struct tb_port *port)
 	tb_lc_set_xdomain_configured(port, false);
 }
 
+static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
+			      unsigned int flags)
+{
+	u32 ctrl;
+	int ret;
+
+	/*
+	 * Enable wake on PCIe and USB4 (wake coming from another
+	 * router).
+	 */
+	ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH,
+			 offset + TB_LC_SX_CTRL, 1);
+	if (ret)
+		return ret;
+
+	ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WOP |
+		  TB_LC_SX_CTRL_WOU4);
+
+	if (flags & TB_WAKE_ON_CONNECT)
+		ctrl |= TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD;
+	if (flags & TB_WAKE_ON_USB4)
+		ctrl |= TB_LC_SX_CTRL_WOU4;
+	if (flags & TB_WAKE_ON_PCIE)
+		ctrl |= TB_LC_SX_CTRL_WOP;
+
+	return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, offset + TB_LC_SX_CTRL, 1);
+}
+
+/**
+ * tb_lc_set_wake() - Enable/disable wake
+ * @sw: Switch whose wakes to configure
+ * @flags: Wakeup flags (%0 to disable)
+ *
+ * For each LC sets wake bits accordingly.
+ */
+int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags)
+{
+	int start, size, nlc, ret, i;
+	u32 desc;
+
+	if (sw->generation < 2)
+		return 0;
+
+	if (!tb_route(sw))
+		return 0;
+
+	ret = read_lc_desc(sw, &desc);
+	if (ret)
+		return ret;
+
+	/* Figure out number of link controllers */
+	nlc = desc & TB_LC_DESC_NLC_MASK;
+	start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT;
+	size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT;
+
+	/* For each link controller set sleep bit */
+	for (i = 0; i < nlc; i++) {
+		unsigned int offset = sw->cap_lc + start + i * size;
+
+		ret = tb_lc_set_wake_one(sw, offset, flags);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 /**
  * tb_lc_set_sleep() - Inform LC that the switch is going to sleep
  * @sw: Switch to set sleep
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 5f7489fa1327..24d2b7eff59b 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -1157,6 +1157,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	}
 	pci_set_drvdata(pdev, tb);
 
+	device_wakeup_enable(&pdev->dev);
+
 	pm_runtime_allow(&pdev->dev);
 	pm_runtime_set_autosuspend_delay(&pdev->dev, TB_AUTOSUSPEND_DELAY);
 	pm_runtime_use_autosuspend(&pdev->dev);
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index a8df25dae57b..a2ebf51ac389 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -2036,7 +2036,7 @@ int tb_switch_configure(struct tb_switch *sw)
 	route = tb_route(sw);
 
 	tb_dbg(tb, "%s Switch at %#llx (depth: %d, up port: %d)\n",
-	       sw->config.enabled ? "restoring " : "initializing", route,
+	       sw->config.enabled ? "restoring" : "initializing", route,
 	       tb_route_length(route), sw->config.upstream_port_number);
 
 	sw->config.enabled = 1;
@@ -2502,6 +2502,13 @@ int tb_switch_add(struct tb_switch *sw)
 		return ret;
 	}
 
+	/*
+	 * Thunderbolt routers do not generate wakeups themselves but
+	 * they forward wakeups from tunneled protocols, so enable it
+	 * here.
+	 */
+	device_init_wakeup(&sw->dev, true);
+
 	pm_runtime_set_active(&sw->dev);
 	if (sw->rpm) {
 		pm_runtime_set_autosuspend_delay(&sw->dev, TB_AUTOSUSPEND_DELAY);
@@ -2579,6 +2586,18 @@ void tb_sw_set_unplugged(struct tb_switch *sw)
 	}
 }
 
+static int tb_switch_set_wake(struct tb_switch *sw, unsigned int flags)
+{
+	if (flags)
+		tb_sw_dbg(sw, "enabling wakeup: %#x\n", flags);
+	else
+		tb_sw_dbg(sw, "disabling wakeup\n");
+
+	if (tb_switch_is_usb4(sw))
+		return usb4_switch_set_wake(sw, flags);
+	return tb_lc_set_wake(sw, flags);
+}
+
 int tb_switch_resume(struct tb_switch *sw)
 {
 	struct tb_port *port;
@@ -2624,6 +2643,9 @@ int tb_switch_resume(struct tb_switch *sw)
 	if (err)
 		return err;
 
+	/* Disable wakes */
+	tb_switch_set_wake(sw, 0);
+
 	err = tb_switch_tmu_init(sw);
 	if (err)
 		return err;
@@ -2659,6 +2681,7 @@ int tb_switch_resume(struct tb_switch *sw)
 
 void tb_switch_suspend(struct tb_switch *sw)
 {
+	unsigned int flags = 0;
 	struct tb_port *port;
 	int err;
 
@@ -2671,6 +2694,11 @@ void tb_switch_suspend(struct tb_switch *sw)
 			tb_switch_suspend(port->remote->sw);
 	}
 
+	if (device_may_wakeup(&sw->dev))
+		flags = TB_WAKE_ON_USB4 | TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE;
+
+	tb_switch_set_wake(sw, flags);
+
 	if (tb_switch_is_usb4(sw))
 		usb4_switch_set_sleep(sw);
 	else
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index f58cd3fa98ea..847accd91bfa 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -333,6 +333,13 @@ struct tb_path {
  */
 #define TB_PATH_MAX_HOPS	(7 * 2)
 
+/* Possible wake types */
+#define TB_WAKE_ON_CONNECT	BIT(0)
+#define TB_WAKE_ON_DISCONNECT	BIT(1)
+#define TB_WAKE_ON_USB4		BIT(2)
+#define TB_WAKE_ON_USB3		BIT(3)
+#define TB_WAKE_ON_PCIE		BIT(4)
+
 /**
  * struct tb_cm_ops - Connection manager specific operations vector
  * @driver_ready: Called right after control channel is started. Used by
@@ -852,6 +859,7 @@ int tb_lc_configure_port(struct tb_port *port);
 void tb_lc_unconfigure_port(struct tb_port *port);
 int tb_lc_configure_xdomain(struct tb_port *port);
 void tb_lc_unconfigure_xdomain(struct tb_port *port);
+int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags);
 int tb_lc_set_sleep(struct tb_switch *sw);
 bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
 bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in);
@@ -907,6 +915,7 @@ int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid);
 int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf,
 			  size_t size);
 bool usb4_switch_lane_bonding_possible(struct tb_switch *sw);
+int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags);
 int usb4_switch_set_sleep(struct tb_switch *sw);
 int usb4_switch_nvm_sector_size(struct tb_switch *sw);
 int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index d1a40baa63d2..0431e415e3bc 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -178,6 +178,8 @@ struct tb_regs_switch_header {
 #define ROUTER_CS_4				0x04
 #define ROUTER_CS_5				0x05
 #define ROUTER_CS_5_SLP				BIT(0)
+#define ROUTER_CS_5_WOP				BIT(1)
+#define ROUTER_CS_5_WOU				BIT(2)
 #define ROUTER_CS_5_C3S				BIT(23)
 #define ROUTER_CS_5_PTO				BIT(24)
 #define ROUTER_CS_5_UTO				BIT(25)
@@ -186,6 +188,8 @@ struct tb_regs_switch_header {
 #define ROUTER_CS_6				0x06
 #define ROUTER_CS_6_SLPR			BIT(0)
 #define ROUTER_CS_6_TNS				BIT(1)
+#define ROUTER_CS_6_WOPS			BIT(2)
+#define ROUTER_CS_6_WOUS			BIT(3)
 #define ROUTER_CS_6_HCI				BIT(18)
 #define ROUTER_CS_6_CR				BIT(25)
 #define ROUTER_CS_7				0x07
@@ -302,9 +306,13 @@ struct tb_regs_port_header {
 #define PORT_CS_18				0x12
 #define PORT_CS_18_BE				BIT(8)
 #define PORT_CS_18_TCM				BIT(9)
+#define PORT_CS_18_WOU4S			BIT(18)
 #define PORT_CS_19				0x13
 #define PORT_CS_19_PC				BIT(3)
 #define PORT_CS_19_PID				BIT(4)
+#define PORT_CS_19_WOC				BIT(16)
+#define PORT_CS_19_WOD				BIT(17)
+#define PORT_CS_19_WOU4				BIT(18)
 
 /* Display Port adapter registers */
 #define ADP_DP_CS_0				0x00
@@ -418,6 +426,10 @@ struct tb_regs_hop {
 #define TB_LC_PORT_ATTR_BE		BIT(12)
 
 #define TB_LC_SX_CTRL			0x96
+#define TB_LC_SX_CTRL_WOC		BIT(1)
+#define TB_LC_SX_CTRL_WOD		BIT(2)
+#define TB_LC_SX_CTRL_WOU4		BIT(5)
+#define TB_LC_SX_CTRL_WOP		BIT(6)
 #define TB_LC_SX_CTRL_L1C		BIT(16)
 #define TB_LC_SX_CTRL_L1D		BIT(17)
 #define TB_LC_SX_CTRL_L2C		BIT(20)
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index 59b8b51d1fa4..40f13579a3fe 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -196,6 +196,46 @@ static int usb4_switch_op(struct tb_switch *sw, u16 opcode, u8 *status)
 	return 0;
 }
 
+static void usb4_switch_check_wakes(struct tb_switch *sw)
+{
+	struct tb_port *port;
+	bool wakeup = false;
+	u32 val;
+
+	if (!device_may_wakeup(&sw->dev))
+		return;
+
+	if (tb_route(sw)) {
+		if (tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_6, 1))
+			return;
+
+		tb_sw_dbg(sw, "PCIe wake: %s, USB3 wake: %s\n",
+			  (val & ROUTER_CS_6_WOPS) ? "yes" : "no",
+			  (val & ROUTER_CS_6_WOUS) ? "yes" : "no");
+
+		wakeup = val & (ROUTER_CS_6_WOPS | ROUTER_CS_6_WOUS);
+	}
+
+	/* Check for any connected downstream ports for USB4 wake */
+	tb_switch_for_each_port(sw, port) {
+		if (!tb_port_has_remote(port))
+			continue;
+
+		if (tb_port_read(port, &val, TB_CFG_PORT,
+				 port->cap_usb4 + PORT_CS_18, 1))
+			break;
+
+		tb_port_dbg(port, "USB4 wake: %s\n",
+			    (val & PORT_CS_18_WOU4S) ? "yes" : "no");
+
+		if (val & PORT_CS_18_WOU4S)
+			wakeup = true;
+	}
+
+	if (wakeup)
+		pm_wakeup_event(&sw->dev, 0);
+}
+
 static bool link_is_usb4(struct tb_port *port)
 {
 	u32 val;
@@ -229,6 +269,8 @@ int usb4_switch_setup(struct tb_switch *sw)
 	u32 val = 0;
 	int ret;
 
+	usb4_switch_check_wakes(sw);
+
 	if (!tb_route(sw))
 		return 0;
 
@@ -359,12 +401,78 @@ bool usb4_switch_lane_bonding_possible(struct tb_switch *sw)
 	return !!(val & PORT_CS_18_BE);
 }
 
+/**
+ * usb4_switch_set_wake() - Enabled/disable wake
+ * @sw: USB4 router
+ * @flags: Wakeup flags (%0 to disable)
+ *
+ * Enables/disables router to wake up from sleep.
+ */
+int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
+{
+	struct tb_port *port;
+	u64 route = tb_route(sw);
+	u32 val;
+	int ret;
+
+	/*
+	 * Enable wakes coming from all USB4 downstream ports (from
+	 * child routers). For device routers do this also for the
+	 * upstream USB4 port.
+	 */
+	tb_switch_for_each_port(sw, port) {
+		if (!route && tb_is_upstream_port(port))
+			continue;
+
+		ret = tb_port_read(port, &val, TB_CFG_PORT,
+				   port->cap_usb4 + PORT_CS_19, 1);
+		if (ret)
+			return ret;
+
+		val &= ~(PORT_CS_19_WOC | PORT_CS_19_WOD | PORT_CS_19_WOU4);
+
+		if (flags & TB_WAKE_ON_CONNECT)
+			val |= PORT_CS_19_WOC;
+		if (flags & TB_WAKE_ON_DISCONNECT)
+			val |= PORT_CS_19_WOD;
+		if (flags & TB_WAKE_ON_USB4)
+			val |= PORT_CS_19_WOU4;
+
+		ret = tb_port_write(port, &val, TB_CFG_PORT,
+				    port->cap_usb4 + PORT_CS_19, 1);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * Enable wakes from PCIe and USB 3.x on this router. Only
+	 * needed for device routers.
+	 */
+	if (route) {
+		ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1);
+		if (ret)
+			return ret;
+
+		val &= ~(ROUTER_CS_5_WOP | ROUTER_CS_5_WOU);
+		if (flags & TB_WAKE_ON_USB3)
+			val |= ROUTER_CS_5_WOU;
+		if (flags & TB_WAKE_ON_PCIE)
+			val |= ROUTER_CS_5_WOP;
+
+		ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 /**
  * usb4_switch_set_sleep() - Prepare the router to enter sleep
  * @sw: USB4 router
  *
- * Enables wakes and sets sleep bit for the router. Returns when the
- * router sleep ready bit has been asserted.
+ * Sets sleep bit for the router. Returns when the router sleep ready
+ * bit has been asserted.
  */
 int usb4_switch_set_sleep(struct tb_switch *sw)
 {
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 16/19] PCI / thunderbolt: Switch to use device links instead of PCI quirk
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (14 preceding siblings ...)
  2020-08-19 11:59 ` [PATCH 15/19] thunderbolt: Enable wakes from system suspend Mika Westerberg
@ 2020-08-19 11:59 ` Mika Westerberg
  2020-08-20 17:24   ` Bjorn Helgaas
  2020-08-19 11:59 ` [PATCH 17/19] ACPI: Export acpi_get_first_physical_node() to modules Mika Westerberg
                   ` (3 subsequent siblings)
  19 siblings, 1 reply; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:59 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

On older Apple systems there is currently a PCI quirk in place to block
resume of tunneled PCIe ports until NHI (Thunderbolt controller) is
resumed. This makes sure the PCIe tunnels are re-established before PCI
core notices it.

With device links the same thing can be done without quirks. The driver
core will make sure the supplier (NHI) is resumed before consumers (PCIe
downstream ports).

For this reason switch the Thunderbolt driver to use device links and
remove the PCI quirk.

Cc: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/pci/quirks.c      | 57 ---------------------------------
 drivers/thunderbolt/nhi.c | 66 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 66 insertions(+), 57 deletions(-)

diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index bdf9b52567e0..a25471436523 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3673,63 +3673,6 @@ static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev)
 DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL,
 			       PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
 			       quirk_apple_poweroff_thunderbolt);
-
-/*
- * Apple: Wait for the Thunderbolt controller to reestablish PCI tunnels
- *
- * During suspend the Thunderbolt controller is reset and all PCI
- * tunnels are lost. The NHI driver will try to reestablish all tunnels
- * during resume. We have to manually wait for the NHI since there is
- * no parent child relationship between the NHI and the tunneled
- * bridges.
- */
-static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev)
-{
-	struct pci_dev *sibling = NULL;
-	struct pci_dev *nhi = NULL;
-
-	if (!x86_apple_machine)
-		return;
-	if (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)
-		return;
-
-	/*
-	 * Find the NHI and confirm that we are a bridge on the Thunderbolt
-	 * host controller and not on a Thunderbolt endpoint.
-	 */
-	sibling = pci_get_slot(dev->bus, 0x0);
-	if (sibling == dev)
-		goto out; /* we are the downstream bridge to the NHI */
-	if (!sibling || !sibling->subordinate)
-		goto out;
-	nhi = pci_get_slot(sibling->subordinate, 0x0);
-	if (!nhi)
-		goto out;
-	if (nhi->vendor != PCI_VENDOR_ID_INTEL
-		    || (nhi->device != PCI_DEVICE_ID_INTEL_LIGHT_RIDGE &&
-			nhi->device != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C &&
-			nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI &&
-			nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI)
-		    || nhi->class != PCI_CLASS_SYSTEM_OTHER << 8)
-		goto out;
-	pci_info(dev, "quirk: waiting for Thunderbolt to reestablish PCI tunnels...\n");
-	device_pm_wait_for_dev(&dev->dev, &nhi->dev);
-out:
-	pci_dev_put(nhi);
-	pci_dev_put(sibling);
-}
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
-			       PCI_DEVICE_ID_INTEL_LIGHT_RIDGE,
-			       quirk_apple_wait_for_thunderbolt);
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
-			       PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
-			       quirk_apple_wait_for_thunderbolt);
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
-			       PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE,
-			       quirk_apple_wait_for_thunderbolt);
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
-			       PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE,
-			       quirk_apple_wait_for_thunderbolt);
 #endif
 
 /*
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 24d2b7eff59b..e499fe78756b 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/property.h>
+#include <linux/platform_data/x86/apple.h>
 
 #include "nhi.h"
 #include "nhi_regs.h"
@@ -1069,6 +1070,69 @@ static bool nhi_imr_valid(struct pci_dev *pdev)
 	return true;
 }
 
+/*
+ * During suspend the Thunderbolt controller is reset and all PCIe
+ * tunnels are lost. The NHI driver will try to reestablish all tunnels
+ * during resume. This adds device links between the tunneled PCIe
+ * downstream ports and the NHI so that the device core will make sure
+ * NHI is resumed first before the rest.
+ */
+static void tb_apple_add_links(struct tb_nhi *nhi)
+{
+	struct pci_dev *upstream, *pdev;
+
+	if (!x86_apple_machine)
+		return;
+
+	switch (nhi->pdev->device) {
+	case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
+	case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
+	case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
+	case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
+		break;
+	default:
+		return;
+	}
+
+	upstream = pci_upstream_bridge(nhi->pdev);
+	while (upstream) {
+		if (!pci_is_pcie(upstream))
+			return;
+		if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
+			break;
+		upstream = pci_upstream_bridge(upstream);
+	}
+
+	if (!upstream)
+		return;
+
+	/*
+	 * For each hotplug downstream port, create add device link
+	 * back to NHI so that PCIe tunnels can be re-established after
+	 * sleep.
+	 */
+	for_each_pci_bridge(pdev, upstream->subordinate) {
+		const struct device_link *link;
+
+		if (!pci_is_pcie(pdev))
+			continue;
+		if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
+		    !pdev->is_hotplug_bridge)
+			continue;
+
+		link = device_link_add(&pdev->dev, &nhi->pdev->dev,
+				       DL_FLAG_AUTOREMOVE_SUPPLIER |
+				       DL_FLAG_PM_RUNTIME);
+		if (link) {
+			dev_dbg(&nhi->pdev->dev, "created link from %s\n",
+				dev_name(&pdev->dev));
+		} else {
+			dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
+				 dev_name(&pdev->dev));
+		}
+	}
+}
+
 static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct tb_nhi *nhi;
@@ -1134,6 +1198,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 			return res;
 	}
 
+	tb_apple_add_links(nhi);
+
 	tb = icm_probe(nhi);
 	if (!tb)
 		tb = tb_probe(nhi);
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 17/19] ACPI: Export acpi_get_first_physical_node() to modules
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (15 preceding siblings ...)
  2020-08-19 11:59 ` [PATCH 16/19] PCI / thunderbolt: Switch to use device links instead of PCI quirk Mika Westerberg
@ 2020-08-19 11:59 ` Mika Westerberg
  2020-08-19 11:59 ` [PATCH 18/19] thunderbolt: Create device links from ACPI description Mika Westerberg
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:59 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

This function will be needed by the Thunderbolt driver when it parses
ACPI description for linking tunneled ports to the Thunderbolt
controller device.

Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/acpi/bus.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 54002670cb7a..53cc1e0cef21 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -551,6 +551,7 @@ struct device *acpi_get_first_physical_node(struct acpi_device *adev)
 	mutex_unlock(physical_node_lock);
 	return phys_dev;
 }
+EXPORT_SYMBOL_GPL(acpi_get_first_physical_node);
 
 static struct acpi_device *acpi_primary_dev_companion(struct acpi_device *adev,
 						      const struct device *dev)
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 18/19] thunderbolt: Create device links from ACPI description
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (16 preceding siblings ...)
  2020-08-19 11:59 ` [PATCH 17/19] ACPI: Export acpi_get_first_physical_node() to modules Mika Westerberg
@ 2020-08-19 11:59 ` Mika Westerberg
  2020-08-19 11:59 ` [PATCH 19/19] thunderbolt: Add runtime PM for Software CM Mika Westerberg
  2020-09-03  9:15 ` [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:59 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

The new way to describe relationship between tunneled ports and USB4 NHI
(Native Host Interface) is with ACPI _DSD looking like below for a PCIe
downstream port:

    Scope (\_SB.PCI0)
    {
        Device (NHI0) { } // Thunderbolt NHI

        Device (DSB0) // Hotplug downstream port
        {
            Name (_DSD, Package () {
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package () {
                    Package () {"usb4-host-interface", \_SB.PCI0.NHI0},
                    ...
                }
            })
        }
    }

This is "documented" in these [1] USB-IF slides and being used on
systems that ship with Windows.

The _DSD can be added to tunneled USB3 and PCIe ports, and is needed to
make sure the USB4 NHI is resumed before any of the tunneled ports so
the protocol tunnels get established properly before the actual port
itself is resumed. Othwerwise the USB/PCI core find the link may not be
established and starts tearing down the device stack.

This parses the ACPI description each time NHI is probed and tries to
find devices that has the property and it references the NHI in
question. For each matching device a device link from that device to the
NHI is created.

Since USB3 ports themselves do not get runtime suspended with the parent
device (hub) we do not add the link from the USB3 port to USB4 NHI but
instead we add the link from the xHCI device. This makes the device link
usable for runtime PM as well.

[1] https://www.usb.org/sites/default/files/D1T2-2%20-%20USB4%20on%20Windows.pdf

Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/Makefile |   2 +
 drivers/thunderbolt/acpi.c   | 117 +++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/nhi.c    |   1 +
 drivers/thunderbolt/tb.h     |   6 ++
 4 files changed, 126 insertions(+)
 create mode 100644 drivers/thunderbolt/acpi.c

diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index 4ab5bfad7bfd..754a529aa132 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -4,4 +4,6 @@ thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o ee
 thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o
 thunderbolt-objs += nvm.o retimer.o quirks.o
 
+thunderbolt-${CONFIG_ACPI} += acpi.o
+
 obj-${CONFIG_USB4_KUNIT_TEST} += test.o
diff --git a/drivers/thunderbolt/acpi.c b/drivers/thunderbolt/acpi.c
new file mode 100644
index 000000000000..a5f988a9f948
--- /dev/null
+++ b/drivers/thunderbolt/acpi.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ACPI support
+ *
+ * Copyright (C) 2020, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include <linux/acpi.h>
+
+#include "tb.h"
+
+static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
+				    void **return_value)
+{
+	struct fwnode_reference_args args;
+	struct fwnode_handle *fwnode;
+	struct tb_nhi *nhi = data;
+	struct acpi_device *adev;
+	struct pci_dev *pdev;
+	struct device *dev;
+	int ret;
+
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+
+	fwnode = acpi_fwnode_handle(adev);
+	ret = fwnode_property_get_reference_args(fwnode, "usb4-host-interface",
+						 NULL, 0, 0, &args);
+	if (ret)
+		return AE_OK;
+
+	/* It needs to reference this NHI */
+	if (nhi->pdev->dev.fwnode != args.fwnode)
+		goto out_put;
+
+	/*
+	 * Try to find physical device walking upwards to the hierarcy.
+	 * We need to do this because the xHCI driver might not yet be
+	 * bound so the USB3 SuperSpeed ports are not yet created.
+	 */
+	dev = acpi_get_first_physical_node(adev);
+	while (!dev) {
+		adev = adev->parent;
+		if (!adev)
+			break;
+		dev = acpi_get_first_physical_node(adev);
+	}
+
+	if (!dev)
+		goto out_put;
+
+	/*
+	 * Check that the device is PCIe. This is because USB3
+	 * SuperSpeed ports have this property and they are not power
+	 * managed with the xHCI and the SuperSpeed hub so we create the
+	 * link from xHCI instead.
+	 */
+	while (!dev_is_pci(dev))
+		dev = dev->parent;
+
+	if (!dev)
+		goto out_put;
+
+	/*
+	 * Check that this actually matches the type of device we
+	 * expect. It should either be xHCI or PCIe root/downstream
+	 * port.
+	 */
+	pdev = to_pci_dev(dev);
+	if (pdev->class == PCI_CLASS_SERIAL_USB_XHCI ||
+	    (pci_is_pcie(pdev) &&
+		(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+		 pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM))) {
+		const struct device_link *link;
+
+		link = device_link_add(&pdev->dev, &nhi->pdev->dev,
+				       DL_FLAG_AUTOREMOVE_SUPPLIER |
+				       DL_FLAG_PM_RUNTIME);
+		if (link) {
+			dev_dbg(&nhi->pdev->dev, "created link from %s\n",
+				dev_name(&pdev->dev));
+		} else {
+			dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
+				 dev_name(&pdev->dev));
+		}
+	}
+
+out_put:
+	fwnode_handle_put(args.fwnode);
+	return AE_OK;
+}
+
+/**
+ * tb_acpi_add_links() - Add device links based on ACPI description
+ * @nhi: Pointer to NHI
+ *
+ * Goes over ACPI namespace finding tunneled ports that reference to
+ * @nhi ACPI node. For each reference a device link is added. The link
+ * is automatically removed by the driver core.
+ */
+void tb_acpi_add_links(struct tb_nhi *nhi)
+{
+	acpi_status status;
+
+	if (!has_acpi_companion(&nhi->pdev->dev))
+		return;
+
+	/*
+	 * Find all devices that have usb4-host-controller interface
+	 * property that references to this NHI.
+	 */
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 32,
+				     tb_acpi_add_link, NULL, nhi, NULL);
+	if (ACPI_FAILURE(status))
+		dev_warn(&nhi->pdev->dev, "failed to enumerate tunneled ports\n");
+}
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index e499fe78756b..bd24e8254336 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -1199,6 +1199,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	}
 
 	tb_apple_add_links(nhi);
+	tb_acpi_add_links(nhi);
 
 	tb = icm_probe(nhi);
 	if (!tb)
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 847accd91bfa..dbcfa24caaf1 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -967,4 +967,10 @@ int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw,
 
 void tb_check_quirks(struct tb_switch *sw);
 
+#ifdef CONFIG_ACPI
+void tb_acpi_add_links(struct tb_nhi *nhi);
+#else
+static inline void tb_acpi_add_links(struct tb_nhi *nhi) { }
+#endif
+
 #endif
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 19/19] thunderbolt: Add runtime PM for Software CM
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (17 preceding siblings ...)
  2020-08-19 11:59 ` [PATCH 18/19] thunderbolt: Create device links from ACPI description Mika Westerberg
@ 2020-08-19 11:59 ` Mika Westerberg
  2020-09-03  9:15 ` [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-08-19 11:59 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Mika Westerberg, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

This adds runtime PM support for the Software Connection Manager parts
of the driver. This allows to save power when either there is no device
attached at all or there is a device attached and all following
conditions are true:

  - Tunneled PCIe root/downstream ports are runtime suspended
  - Tunneled USB3 ports are runtime suspended
  - No active DisplayPort stream
  - No active XDomain connection

For the first two we take advantage of device links that were added in
previous patch. Difference for the system sleep case is that we also
enable wakes when something is geting plugged in/out of the Thunderbolt
ports.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c |  25 ++++++--
 drivers/thunderbolt/tb.c     | 116 ++++++++++++++++++++++++++++++++++-
 drivers/thunderbolt/tb.h     |   2 +-
 3 files changed, 136 insertions(+), 7 deletions(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index a2ebf51ac389..db63b5eb9467 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -2679,23 +2679,40 @@ int tb_switch_resume(struct tb_switch *sw)
 	return 0;
 }
 
-void tb_switch_suspend(struct tb_switch *sw)
+/**
+ * tb_switch_suspend() - Put a switch to sleep
+ * @sw: Switch to suspend
+ * @runtime: Is this runtime suspend or system sleep
+ *
+ * Suspends router and all its children. Enables wakes according to
+ * value of @runtime and then sets sleep bit for the router. If @sw is
+ * host router the domain is ready to go to sleep once this function
+ * returns.
+ */
+void tb_switch_suspend(struct tb_switch *sw, bool runtime)
 {
 	unsigned int flags = 0;
 	struct tb_port *port;
 	int err;
 
+	tb_sw_dbg(sw, "suspending switch\n");
+
 	err = tb_plug_events_active(sw, false);
 	if (err)
 		return;
 
 	tb_switch_for_each_port(sw, port) {
 		if (tb_port_has_remote(port))
-			tb_switch_suspend(port->remote->sw);
+			tb_switch_suspend(port->remote->sw, runtime);
 	}
 
-	if (device_may_wakeup(&sw->dev))
-		flags = TB_WAKE_ON_USB4 | TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE;
+	if (runtime) {
+		/* Trigger wake when something is plugged in/out */
+		flags |= TB_WAKE_ON_CONNECT | TB_WAKE_ON_DISCONNECT;
+		flags |= TB_WAKE_ON_USB4 | TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE;
+	} else if (device_may_wakeup(&sw->dev)) {
+		flags |= TB_WAKE_ON_USB4 | TB_WAKE_ON_USB3 | TB_WAKE_ON_PCIE;
+	}
 
 	tb_switch_set_wake(sw, flags);
 
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 214e47656be6..170d1d846557 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -9,6 +9,7 @@
 #include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 
 #include "tb.h"
 #include "tb_regs.h"
@@ -22,13 +23,21 @@
  *		    events and exit if this is not set (it needs to
  *		    acquire the lock one more time). Used to drain wq
  *		    after cfg has been paused.
+ * @remove_work: Work used to remove any unplugged routers after
+ *		 runtime resume
  */
 struct tb_cm {
 	struct list_head tunnel_list;
 	struct list_head dp_resources;
 	bool hotplug_active;
+	struct delayed_work remove_work;
 };
 
+static inline struct tb *tcm_to_tb(struct tb_cm *tcm)
+{
+	return ((void *)tcm - sizeof(struct tb));
+}
+
 struct tb_hotplug_event {
 	struct work_struct work;
 	struct tb *tb;
@@ -526,8 +535,13 @@ static void tb_scan_switch(struct tb_switch *sw)
 {
 	struct tb_port *port;
 
+	pm_runtime_get_sync(&sw->dev);
+
 	tb_switch_for_each_port(sw, port)
 		tb_scan_port(port);
+
+	pm_runtime_mark_last_busy(&sw->dev);
+	pm_runtime_put_autosuspend(&sw->dev);
 }
 
 /**
@@ -602,6 +616,12 @@ static void tb_scan_port(struct tb_port *port)
 	if (!tcm->hotplug_active)
 		dev_set_uevent_suppress(&sw->dev, true);
 
+	/*
+	 * At the moment Thunderbolt 2 and beyond (devices with LC) we
+	 * can support runtime PM.
+	 */
+	sw->rpm = sw->generation > 1;
+
 	if (tb_switch_add(sw)) {
 		tb_switch_put(sw);
 		return;
@@ -662,6 +682,11 @@ static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)
 		 * deallocated properly.
 		 */
 		tb_switch_dealloc_dp_resource(src_port->sw, src_port);
+		/* Now we can allow the domain to runtime suspend again */
+		pm_runtime_mark_last_busy(&dst_port->sw->dev);
+		pm_runtime_put_autosuspend(&dst_port->sw->dev);
+		pm_runtime_mark_last_busy(&src_port->sw->dev);
+		pm_runtime_put_autosuspend(&src_port->sw->dev);
 		fallthrough;
 
 	case TB_TUNNEL_USB3:
@@ -848,9 +873,20 @@ static void tb_tunnel_dp(struct tb *tb)
 		return;
 	}
 
+	/*
+	 * DP stream needs the domain to be active so runtime resume
+	 * both ends of the tunnel.
+	 *
+	 * This should bring the routers in the middle active as well
+	 * and keeps the domain from runtime suspending while the DP
+	 * tunnel is active.
+	 */
+	pm_runtime_get_sync(&in->sw->dev);
+	pm_runtime_get_sync(&out->sw->dev);
+
 	if (tb_switch_alloc_dp_resource(in->sw, in)) {
 		tb_port_dbg(in, "no resource available for DP IN, not tunneling\n");
-		return;
+		goto err_rpm_put;
 	}
 
 	/* Make all unused USB3 bandwidth available for the new DP tunnel */
@@ -889,6 +925,11 @@ static void tb_tunnel_dp(struct tb *tb)
 	tb_reclaim_usb3_bandwidth(tb, in, out);
 err_dealloc_dp:
 	tb_switch_dealloc_dp_resource(in->sw, in);
+err_rpm_put:
+	pm_runtime_mark_last_busy(&out->sw->dev);
+	pm_runtime_put_autosuspend(&out->sw->dev);
+	pm_runtime_mark_last_busy(&in->sw->dev);
+	pm_runtime_put_autosuspend(&in->sw->dev);
 }
 
 static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port)
@@ -1073,6 +1114,9 @@ static void tb_handle_hotplug(struct work_struct *work)
 	struct tb_switch *sw;
 	struct tb_port *port;
 
+	/* Bring the domain back from sleep if it was suspended */
+	pm_runtime_get_sync(&tb->dev);
+
 	mutex_lock(&tb->lock);
 	if (!tcm->hotplug_active)
 		goto out; /* during init, suspend or shutdown */
@@ -1096,6 +1140,9 @@ static void tb_handle_hotplug(struct work_struct *work)
 		       ev->route, ev->port, ev->unplug);
 		goto put_sw;
 	}
+
+	pm_runtime_get_sync(&sw->dev);
+
 	if (ev->unplug) {
 		tb_retimer_remove_all(port);
 
@@ -1149,10 +1196,17 @@ static void tb_handle_hotplug(struct work_struct *work)
 		}
 	}
 
+	pm_runtime_mark_last_busy(&sw->dev);
+	pm_runtime_put_autosuspend(&sw->dev);
+
 put_sw:
 	tb_switch_put(sw);
 out:
 	mutex_unlock(&tb->lock);
+
+	pm_runtime_mark_last_busy(&tb->dev);
+	pm_runtime_put_autosuspend(&tb->dev);
+
 	kfree(ev);
 }
 
@@ -1188,6 +1242,7 @@ static void tb_stop(struct tb *tb)
 	struct tb_tunnel *tunnel;
 	struct tb_tunnel *n;
 
+	cancel_delayed_work(&tcm->remove_work);
 	/* tunnels are only present after everything has been initialized */
 	list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
 		/*
@@ -1239,6 +1294,8 @@ static int tb_start(struct tb *tb)
 	 * root switch.
 	 */
 	tb->root_switch->no_nvm_upgrade = true;
+	/* All USB4 routers support runtime PM */
+	tb->root_switch->rpm = tb_switch_is_usb4(tb->root_switch);
 
 	ret = tb_switch_configure(tb->root_switch);
 	if (ret) {
@@ -1281,7 +1338,7 @@ static int tb_suspend_noirq(struct tb *tb)
 
 	tb_dbg(tb, "suspending...\n");
 	tb_disconnect_and_release_dp(tb);
-	tb_switch_suspend(tb->root_switch);
+	tb_switch_suspend(tb->root_switch, false);
 	tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
 	tb_dbg(tb, "suspend finished\n");
 
@@ -1292,6 +1349,10 @@ static void tb_restore_children(struct tb_switch *sw)
 {
 	struct tb_port *port;
 
+	/* No need to restore if the router is already unplugged */
+	if (sw->is_unplugged)
+		return;
+
 	if (tb_enable_tmu(sw))
 		tb_sw_warn(sw, "failed to restore TMU configuration\n");
 
@@ -1376,12 +1437,62 @@ static void tb_complete(struct tb *tb)
 	mutex_unlock(&tb->lock);
 }
 
+static int tb_runtime_suspend(struct tb *tb)
+{
+	struct tb_cm *tcm = tb_priv(tb);
+
+	mutex_lock(&tb->lock);
+	tb_switch_suspend(tb->root_switch, true);
+	tcm->hotplug_active = false;
+	mutex_unlock(&tb->lock);
+
+	return 0;
+}
+
+static void tb_remove_work(struct work_struct *work)
+{
+	struct tb_cm *tcm = container_of(work, struct tb_cm, remove_work.work);
+	struct tb *tb = tcm_to_tb(tcm);
+
+	mutex_lock(&tb->lock);
+	if (tb->root_switch) {
+		tb_free_unplugged_children(tb->root_switch);
+		tb_free_unplugged_xdomains(tb->root_switch);
+	}
+	mutex_unlock(&tb->lock);
+}
+
+static int tb_runtime_resume(struct tb *tb)
+{
+	struct tb_cm *tcm = tb_priv(tb);
+	struct tb_tunnel *tunnel, *n;
+
+	mutex_lock(&tb->lock);
+	tb_switch_resume(tb->root_switch);
+	tb_free_invalid_tunnels(tb);
+	tb_restore_children(tb->root_switch);
+	list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
+		tb_tunnel_restart(tunnel);
+	tcm->hotplug_active = true;
+	mutex_unlock(&tb->lock);
+
+	/*
+	 * Schedule cleanup of any unplugged devices. Run this in a
+	 * separate thread to avoid possible deadlock if the device
+	 * removal runtime resumes the unplugged device.
+	 */
+	queue_delayed_work(tb->wq, &tcm->remove_work, msecs_to_jiffies(50));
+	return 0;
+}
+
 static const struct tb_cm_ops tb_cm_ops = {
 	.start = tb_start,
 	.stop = tb_stop,
 	.suspend_noirq = tb_suspend_noirq,
 	.resume_noirq = tb_resume_noirq,
 	.complete = tb_complete,
+	.runtime_suspend = tb_runtime_suspend,
+	.runtime_resume = tb_runtime_resume,
 	.handle_event = tb_handle_event,
 	.approve_switch = tb_tunnel_pci,
 	.approve_xdomain_paths = tb_approve_xdomain_paths,
@@ -1403,6 +1514,7 @@ struct tb *tb_probe(struct tb_nhi *nhi)
 	tcm = tb_priv(tb);
 	INIT_LIST_HEAD(&tcm->tunnel_list);
 	INIT_LIST_HEAD(&tcm->dp_resources);
+	INIT_DELAYED_WORK(&tcm->remove_work, tb_remove_work);
 
 	return tb;
 }
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index dbcfa24caaf1..7c8f505e6818 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -639,7 +639,7 @@ struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb,
 int tb_switch_configure(struct tb_switch *sw);
 int tb_switch_add(struct tb_switch *sw);
 void tb_switch_remove(struct tb_switch *sw);
-void tb_switch_suspend(struct tb_switch *sw);
+void tb_switch_suspend(struct tb_switch *sw, bool runtime);
 int tb_switch_resume(struct tb_switch *sw);
 int tb_switch_reset(struct tb_switch *sw);
 void tb_sw_set_unplugged(struct tb_switch *sw);
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH 16/19] PCI / thunderbolt: Switch to use device links instead of PCI quirk
  2020-08-19 11:59 ` [PATCH 16/19] PCI / thunderbolt: Switch to use device links instead of PCI quirk Mika Westerberg
@ 2020-08-20 17:24   ` Bjorn Helgaas
  0 siblings, 0 replies; 22+ messages in thread
From: Bjorn Helgaas @ 2020-08-20 17:24 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-usb, Michael Jamet, Yehezkel Bernat, Andreas Noever,
	Rajmohan Mani, Dana Alkattan, Lukas Wunner, Rafael J . Wysocki,
	Bjorn Helgaas, Len Brown, Greg Kroah-Hartman, linux-acpi,
	linux-pci

On Wed, Aug 19, 2020 at 02:59:02PM +0300, Mika Westerberg wrote:
> On older Apple systems there is currently a PCI quirk in place to block
> resume of tunneled PCIe ports until NHI (Thunderbolt controller) is
> resumed. This makes sure the PCIe tunnels are re-established before PCI
> core notices it.
> 
> With device links the same thing can be done without quirks. The driver
> core will make sure the supplier (NHI) is resumed before consumers (PCIe
> downstream ports).
> 
> For this reason switch the Thunderbolt driver to use device links and
> remove the PCI quirk.
> 
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>

Acked-by: Bjorn Helgaas <bhelgaas@google.com>

> ---
>  drivers/pci/quirks.c      | 57 ---------------------------------
>  drivers/thunderbolt/nhi.c | 66 +++++++++++++++++++++++++++++++++++++++
>  2 files changed, 66 insertions(+), 57 deletions(-)
> 
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index bdf9b52567e0..a25471436523 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
> @@ -3673,63 +3673,6 @@ static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev)
>  DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL,
>  			       PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
>  			       quirk_apple_poweroff_thunderbolt);
> -
> -/*
> - * Apple: Wait for the Thunderbolt controller to reestablish PCI tunnels
> - *
> - * During suspend the Thunderbolt controller is reset and all PCI
> - * tunnels are lost. The NHI driver will try to reestablish all tunnels
> - * during resume. We have to manually wait for the NHI since there is
> - * no parent child relationship between the NHI and the tunneled
> - * bridges.
> - */
> -static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev)
> -{
> -	struct pci_dev *sibling = NULL;
> -	struct pci_dev *nhi = NULL;
> -
> -	if (!x86_apple_machine)
> -		return;
> -	if (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)
> -		return;
> -
> -	/*
> -	 * Find the NHI and confirm that we are a bridge on the Thunderbolt
> -	 * host controller and not on a Thunderbolt endpoint.
> -	 */
> -	sibling = pci_get_slot(dev->bus, 0x0);
> -	if (sibling == dev)
> -		goto out; /* we are the downstream bridge to the NHI */
> -	if (!sibling || !sibling->subordinate)
> -		goto out;
> -	nhi = pci_get_slot(sibling->subordinate, 0x0);
> -	if (!nhi)
> -		goto out;
> -	if (nhi->vendor != PCI_VENDOR_ID_INTEL
> -		    || (nhi->device != PCI_DEVICE_ID_INTEL_LIGHT_RIDGE &&
> -			nhi->device != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C &&
> -			nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI &&
> -			nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI)
> -		    || nhi->class != PCI_CLASS_SYSTEM_OTHER << 8)
> -		goto out;
> -	pci_info(dev, "quirk: waiting for Thunderbolt to reestablish PCI tunnels...\n");
> -	device_pm_wait_for_dev(&dev->dev, &nhi->dev);
> -out:
> -	pci_dev_put(nhi);
> -	pci_dev_put(sibling);
> -}
> -DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
> -			       PCI_DEVICE_ID_INTEL_LIGHT_RIDGE,
> -			       quirk_apple_wait_for_thunderbolt);
> -DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
> -			       PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
> -			       quirk_apple_wait_for_thunderbolt);
> -DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
> -			       PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE,
> -			       quirk_apple_wait_for_thunderbolt);
> -DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
> -			       PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE,
> -			       quirk_apple_wait_for_thunderbolt);
>  #endif
>  
>  /*
> diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
> index 24d2b7eff59b..e499fe78756b 100644
> --- a/drivers/thunderbolt/nhi.c
> +++ b/drivers/thunderbolt/nhi.c
> @@ -17,6 +17,7 @@
>  #include <linux/module.h>
>  #include <linux/delay.h>
>  #include <linux/property.h>
> +#include <linux/platform_data/x86/apple.h>
>  
>  #include "nhi.h"
>  #include "nhi_regs.h"
> @@ -1069,6 +1070,69 @@ static bool nhi_imr_valid(struct pci_dev *pdev)
>  	return true;
>  }
>  
> +/*
> + * During suspend the Thunderbolt controller is reset and all PCIe
> + * tunnels are lost. The NHI driver will try to reestablish all tunnels
> + * during resume. This adds device links between the tunneled PCIe
> + * downstream ports and the NHI so that the device core will make sure
> + * NHI is resumed first before the rest.
> + */
> +static void tb_apple_add_links(struct tb_nhi *nhi)
> +{
> +	struct pci_dev *upstream, *pdev;
> +
> +	if (!x86_apple_machine)
> +		return;
> +
> +	switch (nhi->pdev->device) {
> +	case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
> +	case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
> +	case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
> +	case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	upstream = pci_upstream_bridge(nhi->pdev);
> +	while (upstream) {
> +		if (!pci_is_pcie(upstream))
> +			return;
> +		if (pci_pcie_type(upstream) == PCI_EXP_TYPE_UPSTREAM)
> +			break;
> +		upstream = pci_upstream_bridge(upstream);
> +	}
> +
> +	if (!upstream)
> +		return;
> +
> +	/*
> +	 * For each hotplug downstream port, create add device link
> +	 * back to NHI so that PCIe tunnels can be re-established after
> +	 * sleep.
> +	 */
> +	for_each_pci_bridge(pdev, upstream->subordinate) {
> +		const struct device_link *link;
> +
> +		if (!pci_is_pcie(pdev))
> +			continue;
> +		if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM ||
> +		    !pdev->is_hotplug_bridge)
> +			continue;
> +
> +		link = device_link_add(&pdev->dev, &nhi->pdev->dev,
> +				       DL_FLAG_AUTOREMOVE_SUPPLIER |
> +				       DL_FLAG_PM_RUNTIME);
> +		if (link) {
> +			dev_dbg(&nhi->pdev->dev, "created link from %s\n",
> +				dev_name(&pdev->dev));
> +		} else {
> +			dev_warn(&nhi->pdev->dev, "device link creation from %s failed\n",
> +				 dev_name(&pdev->dev));
> +		}
> +	}
> +}
> +
>  static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  {
>  	struct tb_nhi *nhi;
> @@ -1134,6 +1198,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  			return res;
>  	}
>  
> +	tb_apple_add_links(nhi);
> +
>  	tb = icm_probe(nhi);
>  	if (!tb)
>  		tb = tb_probe(nhi);
> -- 
> 2.28.0
> 

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 00/19] thunderbolt: Power Management improvements
  2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
                   ` (18 preceding siblings ...)
  2020-08-19 11:59 ` [PATCH 19/19] thunderbolt: Add runtime PM for Software CM Mika Westerberg
@ 2020-09-03  9:15 ` Mika Westerberg
  19 siblings, 0 replies; 22+ messages in thread
From: Mika Westerberg @ 2020-09-03  9:15 UTC (permalink / raw)
  To: linux-usb
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Rajmohan Mani,
	Dana Alkattan, Lukas Wunner, Rafael J . Wysocki, Bjorn Helgaas,
	Len Brown, Greg Kroah-Hartman, linux-acpi, linux-pci

On Wed, Aug 19, 2020 at 02:58:46PM +0300, Mika Westerberg wrote:
> Hi all,
> 
> This series improves power management in the Thunderbolt driver. We already
> have a quite complete power management on systems where Firmware based
> Connection Manager is used (this is pretty much all non-Apple systems out
> there) so this series adds a couple of optimizations to make certain power
> transitions slightly faster, hopefully improving user experience.
> 
> Rest of the patches improve power management in the Software Connection
> manager side of the driver. USB4 spec covers power management for USB4
> hosts and devices, and also TBT3 compatible devices so these patches
> implement that. We also switch to use device links instead of PCI quirk to
> make sure the Thunderbolt/USB4 host controller is resumed before tunneled
> PCIe and USB 3.x ports (so that it gets the chance to restore the tunnels
> properly before). Tiger Lake systems with Software Connection Manager
> enabled describe these relationships using a new ACPI _DSD property that we
> parse in the driver and populate device links accordingly.
> 
> Mika Westerberg (17):
>   thunderbolt: Software CM only should set force power in Tiger Lake
>   thunderbolt: Use bit 31 to check if Firmware CM is running in Tiger Lake
>   thunderbolt: Do not program NFC buffers for USB4 router protocol adapters
>   thunderbolt: No need to log an error if tb_switch_lane_bonding_enable() fails
>   thunderbolt: Send reset only to first generation routers
>   thunderbolt: Tear down DP tunnels when suspending
>   thunderbolt: Initialize TMU again on resume
>   thunderbolt: Do not change default USB4 router notification timeout
>   thunderbolt: Configure link after lane bonding is enabled
>   thunderbolt: Set port configured for both ends of the link
>   thunderbolt: Configure port for XDomain
>   thunderbolt: Disable lane 1 for XDomain connection
>   thunderbolt: Enable wakes from system suspend
>   PCI / thunderbolt: Switch to use device links instead of PCI quirk
>   ACPI: Export acpi_get_first_physical_node() to modules
>   thunderbolt: Create device links from ACPI description
>   thunderbolt: Add runtime PM for Software CM
> 
> Rajmohan Mani (2):
>   thunderbolt: Optimize Force Power logic
>   thunderbolt: Optimize NHI LC mailbox command processing

All applied to thunderbolt.git/next.

@Rafael, I added your Acked-by to patches 17 and 18 as we discussed
offline.

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2020-09-03  9:15 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-19 11:58 [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg
2020-08-19 11:58 ` [PATCH 01/19] thunderbolt: Optimize Force Power logic Mika Westerberg
2020-08-19 11:58 ` [PATCH 02/19] thunderbolt: Optimize NHI LC mailbox command processing Mika Westerberg
2020-08-19 11:58 ` [PATCH 03/19] thunderbolt: Software CM only should set force power in Tiger Lake Mika Westerberg
2020-08-19 11:58 ` [PATCH 04/19] thunderbolt: Use bit 31 to check if Firmware CM is running " Mika Westerberg
2020-08-19 11:58 ` [PATCH 05/19] thunderbolt: Do not program NFC buffers for USB4 router protocol adapters Mika Westerberg
2020-08-19 11:58 ` [PATCH 06/19] thunderbolt: No need to log an error if tb_switch_lane_bonding_enable() fails Mika Westerberg
2020-08-19 11:58 ` [PATCH 07/19] thunderbolt: Send reset only to first generation routers Mika Westerberg
2020-08-19 11:58 ` [PATCH 08/19] thunderbolt: Tear down DP tunnels when suspending Mika Westerberg
2020-08-19 11:58 ` [PATCH 09/19] thunderbolt: Initialize TMU again on resume Mika Westerberg
2020-08-19 11:58 ` [PATCH 10/19] thunderbolt: Do not change default USB4 router notification timeout Mika Westerberg
2020-08-19 11:58 ` [PATCH 11/19] thunderbolt: Configure link after lane bonding is enabled Mika Westerberg
2020-08-19 11:58 ` [PATCH 12/19] thunderbolt: Set port configured for both ends of the link Mika Westerberg
2020-08-19 11:58 ` [PATCH 13/19] thunderbolt: Configure port for XDomain Mika Westerberg
2020-08-19 11:59 ` [PATCH 14/19] thunderbolt: Disable lane 1 for XDomain connection Mika Westerberg
2020-08-19 11:59 ` [PATCH 15/19] thunderbolt: Enable wakes from system suspend Mika Westerberg
2020-08-19 11:59 ` [PATCH 16/19] PCI / thunderbolt: Switch to use device links instead of PCI quirk Mika Westerberg
2020-08-20 17:24   ` Bjorn Helgaas
2020-08-19 11:59 ` [PATCH 17/19] ACPI: Export acpi_get_first_physical_node() to modules Mika Westerberg
2020-08-19 11:59 ` [PATCH 18/19] thunderbolt: Create device links from ACPI description Mika Westerberg
2020-08-19 11:59 ` [PATCH 19/19] thunderbolt: Add runtime PM for Software CM Mika Westerberg
2020-09-03  9:15 ` [PATCH 00/19] thunderbolt: Power Management improvements Mika Westerberg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).