2023-12-12 14:01:51

by Sanath S

[permalink] [raw]
Subject: [PATCH 0/2] Add support for downstream port reset(DPR)

Tunnels created by boot firmware results in incorrect PCI resource
allocation, which results in failure of extending daisy chain.

This series aligns with windows behaviour of performing a teardown of
tunnels and resetting the downstream ports using DPR during the init
sequence.

Sanath S (2):
thunderbolt: Introduce tb_switch_reset_ports(), tb_port_reset() and
usb4_port_reset()
thunderbolt: Teardown tunnels and reset downstream ports created by
boot firmware

drivers/thunderbolt/switch.c | 31 ++++++++
drivers/thunderbolt/tb.c | 137 ++--------------------------------
drivers/thunderbolt/tb.h | 2 +
drivers/thunderbolt/tb_regs.h | 1 +
drivers/thunderbolt/usb4.c | 39 ++++++++++
5 files changed, 80 insertions(+), 130 deletions(-)

--
2.34.1


2023-12-12 14:02:10

by Sanath S

[permalink] [raw]
Subject: [PATCH 1/2] thunderbolt: Introduce tb_switch_reset_ports(), tb_port_reset() and usb4_port_reset()

Introduce the tb_switch_reset_ports() function that resets the
downstream ports of a given switch. This helps us reset the USB4
links created by boot firmware during the init sequence.

Introduce the tb_port_reset() helper function that resets the
given port.

Introduce the usb4_port_reset() function that performs the DPR
of a given port. This function follows the CM guide specification 7.3

Suggested-by: Mario Limonciello <[email protected]>
Signed-off-by: Sanath S <[email protected]>
---
drivers/thunderbolt/switch.c | 31 ++++++++++++++++++++++++++++
drivers/thunderbolt/tb.h | 2 ++
drivers/thunderbolt/tb_regs.h | 1 +
drivers/thunderbolt/usb4.c | 39 +++++++++++++++++++++++++++++++++++
4 files changed, 73 insertions(+)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 44e9b09de47a..26ad6cc1ee91 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -626,6 +626,19 @@ int tb_port_unlock(struct tb_port *port)
return 0;
}

+/**
+ * tb_port_reset() - Reset downstream port
+ * @port: Port to reset
+ *
+ * Helps to reconfigure the USB4 link by resetting the downstream port.
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+static int tb_port_reset(struct tb_port *port)
+{
+ return usb4_port_reset(port);
+}
+
static int __tb_port_enable(struct tb_port *port, bool enable)
{
int ret;
@@ -1547,6 +1560,24 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
regs->__unknown1, regs->__unknown4);
}

+/**
+ * tb_switch_reset_ports() - Reset downstream ports of switch.
+ * @sw: Switch whose ports need to be reset.
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+int tb_switch_reset_ports(struct tb_switch *sw)
+{
+ struct tb_port *port;
+ int ret = -EINVAL;
+
+ tb_switch_for_each_port(sw, port) {
+ if (tb_port_is_null(port) && port->cap_usb4)
+ ret = tb_port_reset(port);
+ }
+ return ret;
+}
+
/**
* tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET
* @sw: Switch to reset
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index e299e53473ae..f2687ec4ac53 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -797,6 +797,7 @@ void tb_switch_remove(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);
+int tb_switch_reset_ports(struct tb_switch *sw);
int tb_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
u32 value, int timeout_msec);
void tb_sw_set_unplugged(struct tb_switch *sw);
@@ -1281,6 +1282,7 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
int usb4_switch_add_ports(struct tb_switch *sw);
void usb4_switch_remove_ports(struct tb_switch *sw);

+int usb4_port_reset(struct tb_port *port);
int usb4_port_unlock(struct tb_port *port);
int usb4_port_hotplug_enable(struct tb_port *port);
int usb4_port_configure(struct tb_port *port);
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index 87e4795275fe..d49530bc0d53 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -389,6 +389,7 @@ struct tb_regs_port_header {
#define PORT_CS_18_CSA BIT(22)
#define PORT_CS_18_TIP BIT(24)
#define PORT_CS_19 0x13
+#define PORT_CS_19_DPR BIT(0)
#define PORT_CS_19_PC BIT(3)
#define PORT_CS_19_PID BIT(4)
#define PORT_CS_19_WOC BIT(16)
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index 4277733d0021..55f7c163bf84 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -1073,6 +1073,45 @@ void usb4_switch_remove_ports(struct tb_switch *sw)
}
}

+/**
+ * usb4_port_reset() - Reset USB4 downsteam port
+ * @port: USB4 port to reset.
+ *
+ * Helps to reconfigure USB4 link by resetting downstream port.
+ *
+ * Return: Returns 0 on success or an error code on failure.
+ */
+int usb4_port_reset(struct tb_port *port)
+{
+ int ret;
+ u32 val = 0;
+
+ ret = tb_port_read(port, &val, TB_CFG_PORT,
+ port->cap_usb4 + PORT_CS_19, 1);
+ if (ret)
+ return ret;
+
+ val = val | PORT_CS_19_DPR;
+ ret = tb_port_write(port, &val, TB_CFG_PORT,
+ port->cap_usb4 + PORT_CS_19, 1);
+ if (ret)
+ return ret;
+
+ /* Wait for 10ms after requesting downstream port reset */
+ msleep(10);
+
+ ret = tb_port_read(port, &val, TB_CFG_PORT,
+ port->cap_usb4 + PORT_CS_19, 1);
+ if (ret)
+ return ret;
+
+ val &= ~PORT_CS_19_DPR;
+ ret = tb_port_write(port, &val, TB_CFG_PORT,
+ port->cap_usb4 + PORT_CS_19, 1);
+
+ return ret;
+}
+
/**
* usb4_port_unlock() - Unlock USB4 downstream port
* @port: USB4 port to unlock
--
2.34.1

2023-12-12 14:02:30

by Sanath S

[permalink] [raw]
Subject: [PATCH 2/2] thunderbolt: Teardown tunnels and reset downstream ports created by boot firmware

Boot firmware might have created tunnels of its own. Since we cannot
be sure they are usable for us. Tear them down and reset the ports
to handle it as a new hotplug.

Since we teardown the tunnels, Discovering of tunnels is not needed.
Removing helper functions tb_discover_tunnels(),
tb_discover_dp_resources(), tb_create_usb3_tunnels(),
tb_add_dp_resources() and device_for_each_child().

Suggested-by: Mario Limonciello <[email protected]>
Signed-off-by: Sanath S <[email protected]>
---
drivers/thunderbolt/tb.c | 137 ++-------------------------------------
1 file changed, 7 insertions(+), 130 deletions(-)

diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index fd49f86e0353..bfad14846514 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -151,24 +151,6 @@ tb_attach_bandwidth_group(struct tb_cm *tcm, struct tb_port *in,
return group;
}

-static void tb_discover_bandwidth_group(struct tb_cm *tcm, struct tb_port *in,
- struct tb_port *out)
-{
- if (usb4_dp_port_bandwidth_mode_enabled(in)) {
- int index, i;
-
- index = usb4_dp_port_group_id(in);
- for (i = 0; i < ARRAY_SIZE(tcm->groups); i++) {
- if (tcm->groups[i].index == index) {
- tb_bandwidth_group_attach_port(&tcm->groups[i], in);
- return;
- }
- }
- }
-
- tb_attach_bandwidth_group(tcm, in, out);
-}
-
static void tb_detach_bandwidth_group(struct tb_port *in)
{
struct tb_bandwidth_group *group = in->group;
@@ -247,32 +229,6 @@ static void tb_remove_dp_resources(struct tb_switch *sw)
}
}

-static void tb_discover_dp_resource(struct tb *tb, struct tb_port *port)
-{
- struct tb_cm *tcm = tb_priv(tb);
- struct tb_port *p;
-
- list_for_each_entry(p, &tcm->dp_resources, list) {
- if (p == port)
- return;
- }
-
- tb_port_dbg(port, "DP %s resource available discovered\n",
- tb_port_is_dpin(port) ? "IN" : "OUT");
- list_add_tail(&port->list, &tcm->dp_resources);
-}
-
-static void tb_discover_dp_resources(struct tb *tb)
-{
- struct tb_cm *tcm = tb_priv(tb);
- struct tb_tunnel *tunnel;
-
- list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
- if (tb_tunnel_is_dp(tunnel))
- tb_discover_dp_resource(tb, tunnel->dst_port);
- }
-}
-
/* Enables CL states up to host router */
static int tb_enable_clx(struct tb_switch *sw)
{
@@ -472,34 +428,6 @@ static void tb_switch_discover_tunnels(struct tb_switch *sw,
}
}

-static void tb_discover_tunnels(struct tb *tb)
-{
- struct tb_cm *tcm = tb_priv(tb);
- struct tb_tunnel *tunnel;
-
- tb_switch_discover_tunnels(tb->root_switch, &tcm->tunnel_list, true);
-
- list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
- if (tb_tunnel_is_pci(tunnel)) {
- struct tb_switch *parent = tunnel->dst_port->sw;
-
- while (parent != tunnel->src_port->sw) {
- parent->boot = true;
- parent = tb_switch_parent(parent);
- }
- } else if (tb_tunnel_is_dp(tunnel)) {
- struct tb_port *in = tunnel->src_port;
- struct tb_port *out = tunnel->dst_port;
-
- /* Keep the domain from powering down */
- pm_runtime_get_sync(&in->sw->dev);
- pm_runtime_get_sync(&out->sw->dev);
-
- tb_discover_bandwidth_group(tcm, in, out);
- }
- }
-}
-
static int tb_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd)
{
if (tb_switch_is_usb4(port->sw))
@@ -1043,31 +971,6 @@ static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw)
return ret;
}

-static int tb_create_usb3_tunnels(struct tb_switch *sw)
-{
- struct tb_port *port;
- int ret;
-
- if (!tb_acpi_may_tunnel_usb3())
- return 0;
-
- if (tb_route(sw)) {
- ret = tb_tunnel_usb3(sw->tb, sw);
- if (ret)
- return ret;
- }
-
- tb_switch_for_each_port(sw, port) {
- if (!tb_port_has_remote(port))
- continue;
- ret = tb_create_usb3_tunnels(port->remote->sw);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
/**
* tb_configure_asym() - Transition links to asymmetric if needed
* @tb: Domain structure
@@ -2534,27 +2437,6 @@ static void tb_stop(struct tb *tb)
tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
}

-static int tb_scan_finalize_switch(struct device *dev, void *data)
-{
- if (tb_is_switch(dev)) {
- struct tb_switch *sw = tb_to_switch(dev);
-
- /*
- * If we found that the switch was already setup by the
- * boot firmware, mark it as authorized now before we
- * send uevent to userspace.
- */
- if (sw->boot)
- sw->authorized = 1;
-
- dev_set_uevent_suppress(dev, false);
- kobject_uevent(&dev->kobj, KOBJ_ADD);
- device_for_each_child(dev, NULL, tb_scan_finalize_switch);
- }
-
- return 0;
-}
-
static int tb_start(struct tb *tb)
{
struct tb_cm *tcm = tb_priv(tb);
@@ -2598,20 +2480,15 @@ static int tb_start(struct tb *tb)
tb_switch_tmu_enable(tb->root_switch);
/* Full scan to discover devices added before the driver was loaded. */
tb_scan_switch(tb->root_switch);
- /* Find out tunnels created by the boot firmware */
- tb_discover_tunnels(tb);
- /* Add DP resources from the DP tunnels created by the boot firmware */
- tb_discover_dp_resources(tb);
/*
- * If the boot firmware did not create USB 3.x tunnels create them
- * now for the whole topology.
+ * Boot firmware might have created tunnels of its own. Since we cannot
+ * be sure they are usable for us, Tear them down and reset the ports
+ * to handle it as new hotplug.
*/
- tb_create_usb3_tunnels(tb->root_switch);
- /* Add DP IN resources for the root switch */
- tb_add_dp_resources(tb->root_switch);
- /* Make the discovered switches available to the userspace */
- device_for_each_child(&tb->root_switch->dev, NULL,
- tb_scan_finalize_switch);
+ tb_switch_discover_tunnels(tb->root_switch, &tcm->tunnel_list, false);
+ ret = tb_switch_reset_ports(tb->root_switch);
+ if (ret)
+ return ret;

/* Allow tb_handle_hotplug to progress events */
tcm->hotplug_active = true;
--
2.34.1

2023-12-12 15:11:20

by Mika Westerberg

[permalink] [raw]
Subject: Re: [PATCH 2/2] thunderbolt: Teardown tunnels and reset downstream ports created by boot firmware

On Tue, Dec 12, 2023 at 07:30:47PM +0530, Sanath S wrote:
> Boot firmware might have created tunnels of its own. Since we cannot
> be sure they are usable for us. Tear them down and reset the ports
> to handle it as a new hotplug.
>
> Since we teardown the tunnels, Discovering of tunnels is not needed.

Let's leave this for non-USB4 (That's TBT1-3) as we agreed.

2023-12-12 15:27:24

by Mika Westerberg

[permalink] [raw]
Subject: Re: [PATCH 1/2] thunderbolt: Introduce tb_switch_reset_ports(), tb_port_reset() and usb4_port_reset()

On Tue, Dec 12, 2023 at 07:30:46PM +0530, Sanath S wrote:
> Introduce the tb_switch_reset_ports() function that resets the
> downstream ports of a given switch. This helps us reset the USB4
> links created by boot firmware during the init sequence.
>
> Introduce the tb_port_reset() helper function that resets the
> given port.
>
> Introduce the usb4_port_reset() function that performs the DPR
> of a given port. This function follows the CM guide specification 7.3
>
> Suggested-by: Mario Limonciello <[email protected]>
> Signed-off-by: Sanath S <[email protected]>
> ---
> drivers/thunderbolt/switch.c | 31 ++++++++++++++++++++++++++++
> drivers/thunderbolt/tb.h | 2 ++
> drivers/thunderbolt/tb_regs.h | 1 +
> drivers/thunderbolt/usb4.c | 39 +++++++++++++++++++++++++++++++++++
> 4 files changed, 73 insertions(+)
>
> diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
> index 44e9b09de47a..26ad6cc1ee91 100644
> --- a/drivers/thunderbolt/switch.c
> +++ b/drivers/thunderbolt/switch.c
> @@ -626,6 +626,19 @@ int tb_port_unlock(struct tb_port *port)
> return 0;
> }
>
> +/**
> + * tb_port_reset() - Reset downstream port
> + * @port: Port to reset
> + *
> + * Helps to reconfigure the USB4 link by resetting the downstream port.
> + *
> + * Return: Returns 0 on success or an error code on failure.
> + */
> +static int tb_port_reset(struct tb_port *port)
> +{
> + return usb4_port_reset(port);
> +}
> +
> static int __tb_port_enable(struct tb_port *port, bool enable)
> {
> int ret;
> @@ -1547,6 +1560,24 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
> regs->__unknown1, regs->__unknown4);
> }
>
> +/**
> + * tb_switch_reset_ports() - Reset downstream ports of switch.
> + * @sw: Switch whose ports need to be reset.
> + *
> + * Return: Returns 0 on success or an error code on failure.
> + */
> +int tb_switch_reset_ports(struct tb_switch *sw)
> +{
> + struct tb_port *port;
> + int ret = -EINVAL;

Why it returns -EINVAL? What if this is run for non-USB4 router?

> +
> + tb_switch_for_each_port(sw, port) {
> + if (tb_port_is_null(port) && port->cap_usb4)
> + ret = tb_port_reset(port);

Should it stop here and return ret?

> + }
> + return ret;
> +}
> +
> /**
> * tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET
> * @sw: Switch to reset
> diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
> index e299e53473ae..f2687ec4ac53 100644
> --- a/drivers/thunderbolt/tb.h
> +++ b/drivers/thunderbolt/tb.h
> @@ -797,6 +797,7 @@ void tb_switch_remove(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);
> +int tb_switch_reset_ports(struct tb_switch *sw);
> int tb_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
> u32 value, int timeout_msec);
> void tb_sw_set_unplugged(struct tb_switch *sw);
> @@ -1281,6 +1282,7 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
> int usb4_switch_add_ports(struct tb_switch *sw);
> void usb4_switch_remove_ports(struct tb_switch *sw);
>
> +int usb4_port_reset(struct tb_port *port);
> int usb4_port_unlock(struct tb_port *port);
> int usb4_port_hotplug_enable(struct tb_port *port);
> int usb4_port_configure(struct tb_port *port);
> diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
> index 87e4795275fe..d49530bc0d53 100644
> --- a/drivers/thunderbolt/tb_regs.h
> +++ b/drivers/thunderbolt/tb_regs.h
> @@ -389,6 +389,7 @@ struct tb_regs_port_header {
> #define PORT_CS_18_CSA BIT(22)
> #define PORT_CS_18_TIP BIT(24)
> #define PORT_CS_19 0x13
> +#define PORT_CS_19_DPR BIT(0)
> #define PORT_CS_19_PC BIT(3)
> #define PORT_CS_19_PID BIT(4)
> #define PORT_CS_19_WOC BIT(16)
> diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
> index 4277733d0021..55f7c163bf84 100644
> --- a/drivers/thunderbolt/usb4.c
> +++ b/drivers/thunderbolt/usb4.c
> @@ -1073,6 +1073,45 @@ void usb4_switch_remove_ports(struct tb_switch *sw)
> }
> }
>
> +/**
> + * usb4_port_reset() - Reset USB4 downsteam port
> + * @port: USB4 port to reset.
> + *
> + * Helps to reconfigure USB4 link by resetting downstream port.
> + *
> + * Return: Returns 0 on success or an error code on failure.
> + */
> +int usb4_port_reset(struct tb_port *port)
> +{
> + int ret;
> + u32 val = 0;

Reverse christmas tree please:

u32 val = 0;
int ret;

> +
> + ret = tb_port_read(port, &val, TB_CFG_PORT,
> + port->cap_usb4 + PORT_CS_19, 1);
> + if (ret)
> + return ret;
> +
> + val = val | PORT_CS_19_DPR;
> + ret = tb_port_write(port, &val, TB_CFG_PORT,
> + port->cap_usb4 + PORT_CS_19, 1);
> + if (ret)
> + return ret;
> +
> + /* Wait for 10ms after requesting downstream port reset */
> + msleep(10);

Probably good to add a couple of more ms just in case. Also
usleep_range()? (or fsleep()).

> +
> + ret = tb_port_read(port, &val, TB_CFG_PORT,
> + port->cap_usb4 + PORT_CS_19, 1);
> + if (ret)
> + return ret;
> +
> + val &= ~PORT_CS_19_DPR;
> + ret = tb_port_write(port, &val, TB_CFG_PORT,
> + port->cap_usb4 + PORT_CS_19, 1);
> +
> + return ret;
> +}
> +
> /**
> * usb4_port_unlock() - Unlock USB4 downstream port
> * @port: USB4 port to unlock
> --
> 2.34.1

2023-12-12 15:32:26

by Mario Limonciello

[permalink] [raw]
Subject: Re: [PATCH 1/2] thunderbolt: Introduce tb_switch_reset_ports(), tb_port_reset() and usb4_port_reset()

On 12/12/2023 09:27, Mika Westerberg wrote:
> On Tue, Dec 12, 2023 at 07:30:46PM +0530, Sanath S wrote:
>> Introduce the tb_switch_reset_ports() function that resets the
>> downstream ports of a given switch. This helps us reset the USB4
>> links created by boot firmware during the init sequence.
>>
>> Introduce the tb_port_reset() helper function that resets the
>> given port.
>>
>> Introduce the usb4_port_reset() function that performs the DPR
>> of a given port. This function follows the CM guide specification 7.3
>>
>> Suggested-by: Mario Limonciello <[email protected]>
>> Signed-off-by: Sanath S <[email protected]>
>> ---
>> drivers/thunderbolt/switch.c | 31 ++++++++++++++++++++++++++++
>> drivers/thunderbolt/tb.h | 2 ++
>> drivers/thunderbolt/tb_regs.h | 1 +
>> drivers/thunderbolt/usb4.c | 39 +++++++++++++++++++++++++++++++++++
>> 4 files changed, 73 insertions(+)
>>
>> diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
>> index 44e9b09de47a..26ad6cc1ee91 100644
>> --- a/drivers/thunderbolt/switch.c
>> +++ b/drivers/thunderbolt/switch.c
>> @@ -626,6 +626,19 @@ int tb_port_unlock(struct tb_port *port)
>> return 0;
>> }
>>
>> +/**
>> + * tb_port_reset() - Reset downstream port
>> + * @port: Port to reset
>> + *
>> + * Helps to reconfigure the USB4 link by resetting the downstream port.
>> + *
>> + * Return: Returns 0 on success or an error code on failure.
>> + */
>> +static int tb_port_reset(struct tb_port *port)
>> +{
>> + return usb4_port_reset(port);
>> +}
>> +
>> static int __tb_port_enable(struct tb_port *port, bool enable)
>> {
>> int ret;
>> @@ -1547,6 +1560,24 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
>> regs->__unknown1, regs->__unknown4);
>> }
>>
>> +/**
>> + * tb_switch_reset_ports() - Reset downstream ports of switch.
>> + * @sw: Switch whose ports need to be reset.
>> + *
>> + * Return: Returns 0 on success or an error code on failure.
>> + */
>> +int tb_switch_reset_ports(struct tb_switch *sw)
>> +{
>> + struct tb_port *port;
>> + int ret = -EINVAL;
>
> Why it returns -EINVAL? What if this is run for non-USB4 router?

This is a good point, but in the non USB4 case (default return) maybe
it's better to be -ENODEV and in patch 2 be careful about the caller.

>
>> +
>> + tb_switch_for_each_port(sw, port) {
>> + if (tb_port_is_null(port) && port->cap_usb4)
>> + ret = tb_port_reset(port);
>
> Should it stop here and return ret?

+1

>
>> + }
>> + return ret;
>> +}
>> +
>> /**
>> * tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET
>> * @sw: Switch to reset
>> diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
>> index e299e53473ae..f2687ec4ac53 100644
>> --- a/drivers/thunderbolt/tb.h
>> +++ b/drivers/thunderbolt/tb.h
>> @@ -797,6 +797,7 @@ void tb_switch_remove(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);
>> +int tb_switch_reset_ports(struct tb_switch *sw);
>> int tb_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
>> u32 value, int timeout_msec);
>> void tb_sw_set_unplugged(struct tb_switch *sw);
>> @@ -1281,6 +1282,7 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
>> int usb4_switch_add_ports(struct tb_switch *sw);
>> void usb4_switch_remove_ports(struct tb_switch *sw);
>>
>> +int usb4_port_reset(struct tb_port *port);
>> int usb4_port_unlock(struct tb_port *port);
>> int usb4_port_hotplug_enable(struct tb_port *port);
>> int usb4_port_configure(struct tb_port *port);
>> diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
>> index 87e4795275fe..d49530bc0d53 100644
>> --- a/drivers/thunderbolt/tb_regs.h
>> +++ b/drivers/thunderbolt/tb_regs.h
>> @@ -389,6 +389,7 @@ struct tb_regs_port_header {
>> #define PORT_CS_18_CSA BIT(22)
>> #define PORT_CS_18_TIP BIT(24)
>> #define PORT_CS_19 0x13
>> +#define PORT_CS_19_DPR BIT(0)
>> #define PORT_CS_19_PC BIT(3)
>> #define PORT_CS_19_PID BIT(4)
>> #define PORT_CS_19_WOC BIT(16)
>> diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
>> index 4277733d0021..55f7c163bf84 100644
>> --- a/drivers/thunderbolt/usb4.c
>> +++ b/drivers/thunderbolt/usb4.c
>> @@ -1073,6 +1073,45 @@ void usb4_switch_remove_ports(struct tb_switch *sw)
>> }
>> }
>>
>> +/**
>> + * usb4_port_reset() - Reset USB4 downsteam port
>> + * @port: USB4 port to reset.
>> + *
>> + * Helps to reconfigure USB4 link by resetting downstream port.
>> + *
>> + * Return: Returns 0 on success or an error code on failure.
>> + */
>> +int usb4_port_reset(struct tb_port *port)
>> +{
>> + int ret;
>> + u32 val = 0;
>
> Reverse christmas tree please:
>
> u32 val = 0;
> int ret;
>
>> +
>> + ret = tb_port_read(port, &val, TB_CFG_PORT,
>> + port->cap_usb4 + PORT_CS_19, 1);
>> + if (ret)
>> + return ret;
>> +
>> + val = val | PORT_CS_19_DPR;
>> + ret = tb_port_write(port, &val, TB_CFG_PORT,
>> + port->cap_usb4 + PORT_CS_19, 1);
>> + if (ret)
>> + return ret;
>> +
>> + /* Wait for 10ms after requesting downstream port reset */
>> + msleep(10);
>
> Probably good to add a couple of more ms just in case. Also
> usleep_range()? (or fsleep()).

Sanath had it at 20 but I had suggested to align to spec.
For the wiggle room maybe usleep_range(10000, 15000)?

>
>> +
>> + ret = tb_port_read(port, &val, TB_CFG_PORT,
>> + port->cap_usb4 + PORT_CS_19, 1);
>> + if (ret)
>> + return ret;
>> +
>> + val &= ~PORT_CS_19_DPR;
>> + ret = tb_port_write(port, &val, TB_CFG_PORT,
>> + port->cap_usb4 + PORT_CS_19, 1);
>> +
>> + return ret;
>> +}
>> +
>> /**
>> * usb4_port_unlock() - Unlock USB4 downstream port
>> * @port: USB4 port to unlock
>> --
>> 2.34.1

2023-12-12 15:41:27

by Mika Westerberg

[permalink] [raw]
Subject: Re: [PATCH 1/2] thunderbolt: Introduce tb_switch_reset_ports(), tb_port_reset() and usb4_port_reset()

On Tue, Dec 12, 2023 at 09:32:03AM -0600, Mario Limonciello wrote:
> On 12/12/2023 09:27, Mika Westerberg wrote:
> > On Tue, Dec 12, 2023 at 07:30:46PM +0530, Sanath S wrote:
> > > Introduce the tb_switch_reset_ports() function that resets the
> > > downstream ports of a given switch. This helps us reset the USB4
> > > links created by boot firmware during the init sequence.
> > >
> > > Introduce the tb_port_reset() helper function that resets the
> > > given port.
> > >
> > > Introduce the usb4_port_reset() function that performs the DPR
> > > of a given port. This function follows the CM guide specification 7.3
> > >
> > > Suggested-by: Mario Limonciello <[email protected]>
> > > Signed-off-by: Sanath S <[email protected]>
> > > ---
> > > drivers/thunderbolt/switch.c | 31 ++++++++++++++++++++++++++++
> > > drivers/thunderbolt/tb.h | 2 ++
> > > drivers/thunderbolt/tb_regs.h | 1 +
> > > drivers/thunderbolt/usb4.c | 39 +++++++++++++++++++++++++++++++++++
> > > 4 files changed, 73 insertions(+)
> > >
> > > diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
> > > index 44e9b09de47a..26ad6cc1ee91 100644
> > > --- a/drivers/thunderbolt/switch.c
> > > +++ b/drivers/thunderbolt/switch.c
> > > @@ -626,6 +626,19 @@ int tb_port_unlock(struct tb_port *port)
> > > return 0;
> > > }
> > > +/**
> > > + * tb_port_reset() - Reset downstream port
> > > + * @port: Port to reset
> > > + *
> > > + * Helps to reconfigure the USB4 link by resetting the downstream port.
> > > + *
> > > + * Return: Returns 0 on success or an error code on failure.
> > > + */
> > > +static int tb_port_reset(struct tb_port *port)
> > > +{
> > > + return usb4_port_reset(port);
> > > +}
> > > +
> > > static int __tb_port_enable(struct tb_port *port, bool enable)
> > > {
> > > int ret;
> > > @@ -1547,6 +1560,24 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
> > > regs->__unknown1, regs->__unknown4);
> > > }
> > > +/**
> > > + * tb_switch_reset_ports() - Reset downstream ports of switch.
> > > + * @sw: Switch whose ports need to be reset.
> > > + *
> > > + * Return: Returns 0 on success or an error code on failure.
> > > + */
> > > +int tb_switch_reset_ports(struct tb_switch *sw)
> > > +{
> > > + struct tb_port *port;
> > > + int ret = -EINVAL;
> >
> > Why it returns -EINVAL? What if this is run for non-USB4 router?
>
> This is a good point, but in the non USB4 case (default return) maybe it's
> better to be -ENODEV and in patch 2 be careful about the caller.

Or -EOPNOTSUPP (to be consistent with the rest of the driver). Add this
to the kernel-doc too so that the caller needs to make sure
tb_switch_is_usb4() is called before this one or so.

>
> >
> > > +
> > > + tb_switch_for_each_port(sw, port) {
> > > + if (tb_port_is_null(port) && port->cap_usb4)
> > > + ret = tb_port_reset(port);
> >
> > Should it stop here and return ret?
>
> +1
>
> >
> > > + }
> > > + return ret;
> > > +}
> > > +
> > > /**
> > > * tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET
> > > * @sw: Switch to reset
> > > diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
> > > index e299e53473ae..f2687ec4ac53 100644
> > > --- a/drivers/thunderbolt/tb.h
> > > +++ b/drivers/thunderbolt/tb.h
> > > @@ -797,6 +797,7 @@ void tb_switch_remove(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);
> > > +int tb_switch_reset_ports(struct tb_switch *sw);
> > > int tb_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
> > > u32 value, int timeout_msec);
> > > void tb_sw_set_unplugged(struct tb_switch *sw);
> > > @@ -1281,6 +1282,7 @@ struct tb_port *usb4_switch_map_usb3_down(struct tb_switch *sw,
> > > int usb4_switch_add_ports(struct tb_switch *sw);
> > > void usb4_switch_remove_ports(struct tb_switch *sw);
> > > +int usb4_port_reset(struct tb_port *port);
> > > int usb4_port_unlock(struct tb_port *port);
> > > int usb4_port_hotplug_enable(struct tb_port *port);
> > > int usb4_port_configure(struct tb_port *port);
> > > diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
> > > index 87e4795275fe..d49530bc0d53 100644
> > > --- a/drivers/thunderbolt/tb_regs.h
> > > +++ b/drivers/thunderbolt/tb_regs.h
> > > @@ -389,6 +389,7 @@ struct tb_regs_port_header {
> > > #define PORT_CS_18_CSA BIT(22)
> > > #define PORT_CS_18_TIP BIT(24)
> > > #define PORT_CS_19 0x13
> > > +#define PORT_CS_19_DPR BIT(0)
> > > #define PORT_CS_19_PC BIT(3)
> > > #define PORT_CS_19_PID BIT(4)
> > > #define PORT_CS_19_WOC BIT(16)
> > > diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
> > > index 4277733d0021..55f7c163bf84 100644
> > > --- a/drivers/thunderbolt/usb4.c
> > > +++ b/drivers/thunderbolt/usb4.c
> > > @@ -1073,6 +1073,45 @@ void usb4_switch_remove_ports(struct tb_switch *sw)
> > > }
> > > }
> > > +/**
> > > + * usb4_port_reset() - Reset USB4 downsteam port
> > > + * @port: USB4 port to reset.
> > > + *
> > > + * Helps to reconfigure USB4 link by resetting downstream port.
> > > + *
> > > + * Return: Returns 0 on success or an error code on failure.
> > > + */
> > > +int usb4_port_reset(struct tb_port *port)
> > > +{
> > > + int ret;
> > > + u32 val = 0;
> >
> > Reverse christmas tree please:
> >
> > u32 val = 0;
> > int ret;
> >
> > > +
> > > + ret = tb_port_read(port, &val, TB_CFG_PORT,
> > > + port->cap_usb4 + PORT_CS_19, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + val = val | PORT_CS_19_DPR;
> > > + ret = tb_port_write(port, &val, TB_CFG_PORT,
> > > + port->cap_usb4 + PORT_CS_19, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + /* Wait for 10ms after requesting downstream port reset */
> > > + msleep(10);
> >
> > Probably good to add a couple of more ms just in case. Also
> > usleep_range()? (or fsleep()).
>
> Sanath had it at 20 but I had suggested to align to spec.
> For the wiggle room maybe usleep_range(10000, 15000)?

Works for me.

>
> >
> > > +
> > > + ret = tb_port_read(port, &val, TB_CFG_PORT,
> > > + port->cap_usb4 + PORT_CS_19, 1);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + val &= ~PORT_CS_19_DPR;
> > > + ret = tb_port_write(port, &val, TB_CFG_PORT,
> > > + port->cap_usb4 + PORT_CS_19, 1);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > /**
> > > * usb4_port_unlock() - Unlock USB4 downstream port
> > > * @port: USB4 port to unlock
> > > --
> > > 2.34.1