On Intel platforms, if the debug target is connected with debug host, enabling DCE bit in command register leads to a port hung state. In the hung state, the host system will not see a port connected status bit set. Hence debug target fails to be probed. The state could be resolved by performing a port reset to the debug port from the host xHCI. This patch introduces this work around. Signed-off-by: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx> --- drivers/usb/early/xhci-dbc.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/xhci-dbc.h | 2 ++ 2 files changed, 54 insertions(+) diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c index 22a1de9..6b23f09 100644 --- a/drivers/usb/early/xhci-dbc.c +++ b/drivers/usb/early/xhci-dbc.c @@ -255,6 +255,8 @@ static void __iomem *xdbc_map_pci_mmio(u32 bus, xdbcp->bar = bar; xdbcp->xhci_base = base; xdbcp->xhci_length = sz64; + xdbcp->vendor = read_pci_config_16(bus, dev, func, PCI_VENDOR_ID); + xdbcp->device = read_pci_config_16(bus, dev, func, PCI_DEVICE_ID); if (length) *length = sz64; @@ -651,6 +653,52 @@ static int xdbc_mem_init(void) return 0; } +static void xdbc_reset_debug_port_callback(int cap_offset, void *data) +{ + u8 major; + u32 val, port_offset, port_count; + u32 cap_length; + void __iomem *ops_reg; + void __iomem *portsc; + int i; + + val = readl(xdbcp->xhci_base + cap_offset); + major = (u8) XHCI_EXT_PORT_MAJOR(val); + + /* only reset super-speed port */ + if (major != 0x3) + return; + + val = readl(xdbcp->xhci_base + cap_offset + 8); + port_offset = XHCI_EXT_PORT_OFF(val); + port_count = XHCI_EXT_PORT_COUNT(val); + xdbc_trace("Extcap Port offset %d count %d\n", + port_offset, port_count); + + cap_length = readl(xdbcp->xhci_base) & 0xff; + ops_reg = xdbcp->xhci_base + cap_length; + + port_offset--; + for (i = port_offset; i < (port_offset + port_count); i++) { + portsc = ops_reg + 0x400 + i * 0x10; + val = readl(portsc); + /* reset the port if CCS bit is cleared */ + if (!(val & 0x1)) + writel(val | (1 << 4), portsc); + } +} + +static void xdbc_reset_debug_port(void) +{ + xdbc_walk_excap(xdbcp->bus, + xdbcp->dev, + xdbcp->func, + XHCI_EXT_CAPS_PROTOCOL, + false, + xdbc_reset_debug_port_callback, + NULL); +} + /* * xdbc_start: start DbC * @@ -669,6 +717,10 @@ static int xdbc_start(void) return -ENODEV; } + /* reset port to avoid bus hang */ + if (xdbcp->vendor == PCI_VENDOR_ID_INTEL) + xdbc_reset_debug_port(); + /* wait for port connection */ if (handshake(&xdbcp->xdbc_reg->portsc, PORTSC_CCS, PORTSC_CCS, 5000000, 100) < 0) { diff --git a/include/linux/usb/xhci-dbc.h b/include/linux/usb/xhci-dbc.h index 153fb87..fc0ef9a 100644 --- a/include/linux/usb/xhci-dbc.h +++ b/include/linux/usb/xhci-dbc.h @@ -128,6 +128,8 @@ struct xdbc_state { u32 dev; u32 func; u8 bar; + u16 vendor; + u16 device; void __iomem *xhci_base; size_t xhci_length; #define XDBC_PCI_MAX_BUSES 256 -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html