From: Mathias Nyman Date: Wed, 3 Jun 2026 09:11:28 +0000 (+0300) Subject: xhci: dbc: detect and recover hung DbC during enumeraton X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a3d34e5525cef4a53b755b94807dbe8c2976d7e7;p=thirdparty%2Fkernel%2Flinux.git xhci: dbc: detect and recover hung DbC during enumeraton Add a timeout between the detection of the debug host connection and the DbC Run transition to ‘1’. Toggle the DCE bit to re-enable DbC in order to retry the debug device enumeration process if the DbC run transition takes too long. Set the timeout to 2 seconds See xhci specification section 7.6.4.1 "Debug Capability Initialization" Also detect cable disconnect during enable and connected state. Signed-off-by: Mathias Nyman Link: https://patch.msgid.link/20260603091132.1110849-12-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 34441ffa5e15..b1cabf5582fa 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -901,23 +901,49 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) return EVT_ERR; case DS_ENABLED: portsc = readl(&dbc->regs->portsc); + ctrl = readl(&dbc->regs->control); + if (portsc & DBC_PORTSC_CONN_STATUS) { xhci_dbc_set_state(dbc, DS_CONNECTED); dev_info(dbc->dev, "DbC connected\n"); + } else if (!(ctrl & DBC_CTRL_DBC_ENABLE)) { + dev_err(dbc->dev, "unexpected DbC disable, xHC reset?\n"); } return EVT_DONE; case DS_CONNECTED: ctrl = readl(&dbc->regs->control); + portsc = readl(&dbc->regs->portsc); if (ctrl & DBC_CTRL_DBC_RUN) { xhci_dbc_set_state(dbc, DS_CONFIGURED); dev_info(dbc->dev, "DbC configured\n"); - portsc = readl(&dbc->regs->portsc); writel(portsc, &dbc->regs->portsc); ret = EVT_GSER; break; } + /* Connection lost */ + if (!(portsc & DBC_PORTSC_CONN_STATUS)) { + /* covers DCE == 0 as it also sets CONN_STATUS to 0 */ + dev_warn(dbc->dev, "DbC connection lost mid enumeration\n"); + xhci_dbc_set_state(dbc, DS_ENABLED); + + return EVT_DONE; + } + + /* Enumeration timeout */ + if (time_is_before_jiffies(dbc->state_timestamp + + msecs_to_jiffies(DBC_ENUMERATION_TIMEOUT))) { + dev_err(dbc->dev, "DbC enumeration timeout, re-enabling DbC\n"); + dev_dbg(dbc->dev, "dcctrl %x, dcportsc %x\n", ctrl, portsc); + + /* Toggle DCE to retry enumeration */ + ret = xhci_dbc_enable_dce(dbc, false); + udelay(100); + ret = xhci_dbc_enable_dce(dbc, true); + xhci_dbc_set_state(dbc, DS_ENABLED); + } + return EVT_DONE; case DS_CONFIGURED: /* Handle cable unplug event: */ diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index b3efea43a6be..df7aca8bfe99 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -113,6 +113,7 @@ struct dbc_ep { #define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */ #define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */ #define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */ +#define DBC_ENUMERATION_TIMEOUT 2000 /* milliseconds */ /* * Private structure for DbC hardware state: */