* [PATCH -next v3] thunderbolt: Add quirk to reset host interface on DMA path teardown for AMD USB4 routers
@ 2026-08-06 12:59 Basavaraj Natikar
2026-08-07 6:16 ` Mika Westerberg
0 siblings, 1 reply; 3+ messages in thread
From: Basavaraj Natikar @ 2026-08-06 12:59 UTC (permalink / raw)
To: andreas.noever, westeri, YehezkelShB, linux-usb
Cc: Mario.Limonciello, Basavaraj Natikar, Sanath S
Some AMD USB4 host routers have a bug in the Host Interface where
DMA path setup and teardown cycles may cause the Tx ring to hang.
Fix this by issuing a Host Interface Reset on every DMA path teardown
for affected routers. The Host Interface Reset brings the registers in
the memory BAR to their default state and clears the End-to-End Flow
Control state, preventing the hang condition.
Co-developed-by: Sanath S <Sanath.S@amd.com>
Signed-off-by: Sanath S <Sanath.S@amd.com>
Signed-off-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
---
v3:
- nhi_reset_interface() takes struct tb_nhi.
- Rename the quirk to QUIRK_RESET_DMA_ON_TEARDOWN.
- Give the AMD NHI PCI device IDs symbolic names in nhi.h.
- Reset the host interface through nhi->ops->reset_interface.
v2: https://lore.kernel.org/all/20260805111811.517996-1-Basavaraj.Natikar@amd.com/
v1: https://lore.kernel.org/all/20260804122638.1623429-1-Basavaraj.Natikar@amd.com/
drivers/thunderbolt/domain.c | 28 +++++++++++++++++++++++++++-
drivers/thunderbolt/nhi.c | 26 ++++++++++++++++++++++++++
drivers/thunderbolt/nhi.h | 16 ++++++++++++++++
drivers/thunderbolt/nhi_regs.h | 4 ++++
drivers/thunderbolt/pci.c | 23 +++++++++++++++++++++++
5 files changed, 96 insertions(+), 1 deletion(-)
diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c
index 24611f05b3cd..62896930c93e 100644
--- a/drivers/thunderbolt/domain.c
+++ b/drivers/thunderbolt/domain.c
@@ -788,6 +788,23 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
transmit_ring, receive_path, receive_ring);
}
+static void tb_domain_reset_interface(struct tb *tb)
+{
+ struct tb_nhi *nhi = tb->nhi;
+
+ if (!nhi->ops->reset_interface)
+ return;
+
+ mutex_lock(&tb->lock);
+
+ /* The reset clears the ring state so stop the control channel */
+ tb_ctl_stop(tb->ctl);
+ nhi->ops->reset_interface(nhi);
+ tb_ctl_start(tb->ctl);
+
+ mutex_unlock(&tb->lock);
+}
+
/**
* tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain
* @tb: Domain disabling the DMA paths
@@ -810,11 +827,20 @@ int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
int transmit_path, int transmit_ring,
int receive_path, int receive_ring)
{
+ int ret;
+
if (!tb->cm_ops->disconnect_xdomain_paths)
return -ENOTSUPP;
- return tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path,
+ ret = tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path,
transmit_ring, receive_path, receive_ring);
+ if (ret)
+ return ret;
+
+ if (tb->nhi->quirks & QUIRK_RESET_DMA_ON_TEARDOWN)
+ tb_domain_reset_interface(tb);
+
+ return 0;
}
static int disconnect_xdomain(struct device *dev, void *data)
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 383a36212f70..76fb14221438 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -1160,6 +1160,32 @@ static void nhi_reset(struct tb_nhi *nhi)
dev_warn(nhi->dev, "timeout resetting host router\n");
}
+/**
+ * nhi_reset_interface() - Reset the host interface
+ * @nhi: Host interface to reset
+ *
+ * Brings the registers in the memory BAR back to their default state and
+ * clears the End-to-End Flow Control state. The caller is responsible for
+ * stopping the control channel over the reset because it clears the ring
+ * state as well.
+ */
+void nhi_reset_interface(struct tb_nhi *nhi)
+{
+ u32 val;
+
+ val = ioread32(nhi->iobase + REG_CAPS);
+ /* Only v1 host interfaces implement the reset */
+ if (FIELD_GET(REG_CAPS_VERSION_MASK, val) >= REG_CAPS_VERSION_2)
+ return;
+
+ dev_dbg(nhi->dev, "issuing host interface reset\n");
+
+ iowrite32(REG_HOST_INTERFACE_RESET_RST,
+ nhi->iobase + REG_HOST_INTERFACE_RESET);
+ /* Wait for tHIReset (10 ms) to complete */
+ usleep_range(10000, 20000);
+}
+
static struct tb *nhi_select_cm(struct tb_nhi *nhi)
{
struct tb *tb;
diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h
index d488eadadfce..15bff76c66bc 100644
--- a/drivers/thunderbolt/nhi.h
+++ b/drivers/thunderbolt/nhi.h
@@ -36,6 +36,7 @@ irqreturn_t nhi_msi(int irq, void *data);
irqreturn_t ring_msix(int irq, void *data);
int nhi_probe(struct tb_nhi *nhi);
void nhi_shutdown(struct tb_nhi *nhi);
+void nhi_reset_interface(struct tb_nhi *nhi);
extern const struct dev_pm_ops nhi_pm_ops;
/**
@@ -52,6 +53,7 @@ extern const struct dev_pm_ops nhi_pm_ops;
* @release_ring_irq: NHI specific interrupt release hook
* @is_present: Whether the device is currently present on the parent bus
* @init_interrupts: NHI specific interrupt initialization hook
+ * @reset_interface: Resets the host interface
*/
struct tb_nhi_ops {
int (*init)(struct tb_nhi *nhi);
@@ -66,6 +68,7 @@ struct tb_nhi_ops {
void (*release_ring_irq)(struct tb_ring *ring);
bool (*is_present)(struct tb_nhi *nhi);
int (*init_interrupts)(struct tb_nhi *nhi);
+ void (*reset_interface)(struct tb_nhi *nhi);
};
/*
@@ -116,11 +119,24 @@ struct tb_nhi_ops {
#define PCI_DEVICE_ID_INTEL_PTL_P_NHI0 0xe433
#define PCI_DEVICE_ID_INTEL_PTL_P_NHI1 0xe434
+#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI0 0x1120
+#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI1 0x1121
+#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI0 0x113b
+#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI1 0x113c
+#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI0 0x1155
+#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI1 0x1158
+#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI2 0x1159
+#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI0 0x151c
+#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI1 0x151d
+#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI0 0x158d
+#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI1 0x158e
+
#define PCI_CLASS_SERIAL_USB_USB4 0x0c0340
/* Host interface quirks */
#define QUIRK_AUTO_CLEAR_INT BIT(0)
#define QUIRK_E2E BIT(1)
+#define QUIRK_RESET_DMA_ON_TEARDOWN BIT(2)
/*
* Minimal number of vectors when we use MSI-X. Two for control channel
diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h
index d6a197fabc74..99df60b6db36 100644
--- a/drivers/thunderbolt/nhi_regs.h
+++ b/drivers/thunderbolt/nhi_regs.h
@@ -115,6 +115,10 @@ struct ring_desc {
#define REG_CAPS_VERSION_MASK GENMASK(23, 16)
#define REG_CAPS_VERSION_2 0x40
+/* Host Interface Reset - resets TX/RX rings and E2E flow control counters */
+#define REG_HOST_INTERFACE_RESET 0x39858
+#define REG_HOST_INTERFACE_RESET_RST BIT(0)
+
#define REG_DMA_MISC 0x39864
#define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2)
#define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17)
diff --git a/drivers/thunderbolt/pci.c b/drivers/thunderbolt/pci.c
index 8462ccb59b7e..99333729f3c2 100644
--- a/drivers/thunderbolt/pci.c
+++ b/drivers/thunderbolt/pci.c
@@ -62,6 +62,27 @@ static void nhi_pci_check_quirks(struct tb_nhi_pci *nhi_pci)
nhi->quirks |= QUIRK_E2E;
break;
}
+ } else if (pdev->vendor == PCI_VENDOR_ID_AMD) {
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_AMD_1AH_M60H_NHI0:
+ case PCI_DEVICE_ID_AMD_1AH_M60H_NHI1:
+ case PCI_DEVICE_ID_AMD_1AH_M68H_NHI0:
+ case PCI_DEVICE_ID_AMD_1AH_M68H_NHI1:
+ case PCI_DEVICE_ID_AMD_1AH_M80H_NHI0:
+ case PCI_DEVICE_ID_AMD_1AH_M80H_NHI1:
+ case PCI_DEVICE_ID_AMD_1AH_M80H_NHI2:
+ case PCI_DEVICE_ID_AMD_1AH_M24H_NHI0:
+ case PCI_DEVICE_ID_AMD_1AH_M24H_NHI1:
+ case PCI_DEVICE_ID_AMD_1AH_M70H_NHI0:
+ case PCI_DEVICE_ID_AMD_1AH_M70H_NHI1:
+ /*
+ * These AMD hosts may hang the Tx ring when the
+ * DMA paths are torn down so they need the host
+ * interface reset after each teardown.
+ */
+ nhi->quirks |= QUIRK_RESET_DMA_ON_TEARDOWN;
+ break;
+ }
}
}
@@ -258,6 +279,7 @@ static const struct tb_nhi_ops pci_nhi_default_ops = {
.shutdown = nhi_pci_release_irq,
.is_present = nhi_pci_is_present,
.init_interrupts = nhi_pci_init_msi,
+ .reset_interface = nhi_reset_interface,
};
/* Ice Lake specific NHI operations */
@@ -441,6 +463,7 @@ static const struct tb_nhi_ops icl_nhi_ops = {
.release_ring_irq = nhi_pci_ring_release_msix,
.is_present = nhi_pci_is_present,
.init_interrupts = nhi_pci_init_msi,
+ .reset_interface = nhi_reset_interface,
};
static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH -next v3] thunderbolt: Add quirk to reset host interface on DMA path teardown for AMD USB4 routers
2026-08-06 12:59 [PATCH -next v3] thunderbolt: Add quirk to reset host interface on DMA path teardown for AMD USB4 routers Basavaraj Natikar
@ 2026-08-07 6:16 ` Mika Westerberg
2026-08-07 8:13 ` Basavaraj Natikar
0 siblings, 1 reply; 3+ messages in thread
From: Mika Westerberg @ 2026-08-07 6:16 UTC (permalink / raw)
To: Basavaraj Natikar
Cc: andreas.noever, westeri, YehezkelShB, linux-usb,
Mario.Limonciello, Sanath S
Hi,
On Thu, Aug 06, 2026 at 06:29:29PM +0530, Basavaraj Natikar wrote:
> Some AMD USB4 host routers have a bug in the Host Interface where
> DMA path setup and teardown cycles may cause the Tx ring to hang.
>
> Fix this by issuing a Host Interface Reset on every DMA path teardown
> for affected routers. The Host Interface Reset brings the registers in
> the memory BAR to their default state and clears the End-to-End Flow
> Control state, preventing the hang condition.
>
> Co-developed-by: Sanath S <Sanath.S@amd.com>
> Signed-off-by: Sanath S <Sanath.S@amd.com>
> Signed-off-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
I did few minor cosmetic tweaks and applied to thunderbolt.git/next,
thanks!
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH -next v3] thunderbolt: Add quirk to reset host interface on DMA path teardown for AMD USB4 routers
2026-08-07 6:16 ` Mika Westerberg
@ 2026-08-07 8:13 ` Basavaraj Natikar
0 siblings, 0 replies; 3+ messages in thread
From: Basavaraj Natikar @ 2026-08-07 8:13 UTC (permalink / raw)
To: Mika Westerberg, Basavaraj Natikar
Cc: andreas.noever, westeri, YehezkelShB, linux-usb,
Mario.Limonciello, Sanath S
On 8/7/2026 11:46 AM, Mika Westerberg wrote:
> Hi,
>
> On Thu, Aug 06, 2026 at 06:29:29PM +0530, Basavaraj Natikar wrote:
>> Some AMD USB4 host routers have a bug in the Host Interface where
>> DMA path setup and teardown cycles may cause the Tx ring to hang.
>>
>> Fix this by issuing a Host Interface Reset on every DMA path teardown
>> for affected routers. The Host Interface Reset brings the registers in
>> the memory BAR to their default state and clears the End-to-End Flow
>> Control state, preventing the hang condition.
>>
>> Co-developed-by: Sanath S <Sanath.S@amd.com>
>> Signed-off-by: Sanath S <Sanath.S@amd.com>
>> Signed-off-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
> I did few minor cosmetic tweaks and applied to thunderbolt.git/next,
Thanks a lot Mika, appreciate the review and the tweaks!
Thanks,
--
Basavaraj
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-07 8:14 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06 12:59 [PATCH -next v3] thunderbolt: Add quirk to reset host interface on DMA path teardown for AMD USB4 routers Basavaraj Natikar
2026-08-07 6:16 ` Mika Westerberg
2026-08-07 8:13 ` Basavaraj Natikar
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox