[PATCH] xhci: Don't issue Reset Device command to Etron xHCI host

Kuangyi Chiang
2024-06-12T02:23:31Z

Sometimes hub driver does not recognize USB device that is connected
to external USB2.0 Hub when system resumes from S4.

This happens when xHCI driver issue Reset Device command to inform
Etron xHCI host that USB device has been reset.

Seems that Etron xHCI host can not perform this command correctly,
affecting that USB device.

Instead, to aviod this, xHCI driver should reassign device slot ID
by calling xhci_free_dev() and then xhci_alloc_dev(), the effect is
the same.

Add XHCI_ETRON_HOST quirk flag to invoke workaround in
xhci_discover_or_reset_device().

Cc: <stable@vger.kernel.org>
Signed-off-by: Kuangyi Chiang <ki.chiang65@gmail.com>
---
drivers/usb/host/xhci-pci.c | 2 ++
drivers/usb/host/xhci.c | 11 ++++++++++-
drivers/usb/host/xhci.h | 2 ++
3 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 05881153883e..c7a88340a6f8 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -395,11 +395,13 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == PCI_DEVICE_ID_EJ168) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci->quirks |= XHCI_BROKEN_STREAMS;
+ xhci->quirks |= XHCI_ETRON_HOST;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_EJ188) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci->quirks |= XHCI_BROKEN_STREAMS;
+ xhci->quirks |= XHCI_ETRON_HOST;
}

if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 37eb37b0affa..aba4164b0873 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3752,6 +3752,15 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
SLOT_STATE_DISABLED)
return 0;

+ if (xhci->quirks & XHCI_ETRON_HOST) {
+ xhci_free_dev(hcd, udev);
+ ret = xhci_alloc_dev(hcd, udev);
+ if (ret == 1)
+ return 0;
+ else
+ return -EINVAL;
+ }
+
trace_xhci_discover_or_reset_device(slot_ctx);

xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id);
@@ -3862,7 +3871,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
* disconnected, and all traffic has been stopped and the endpoints have been
* disabled. Free any HC data structures associated with that device.
*/
-static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
+void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_virt_device *virt_dev;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 30415158ed3c..f46b4dcb0613 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1627,6 +1627,7 @@ struct xhci_hcd {
#define XHCI_RESET_TO_DEFAULT BIT_ULL(44)
#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45)
#define XHCI_ZHAOXIN_HOST BIT_ULL(46)
+#define XHCI_ETRON_HOST BIT_ULL(47)

unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -1863,6 +1864,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg);
irqreturn_t xhci_irq(struct usb_hcd *hcd);
irqreturn_t xhci_msi_irq(int irq, void *hcd);
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
+void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev);
int xhci_alloc_tt_info(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
struct usb_device *hdev,
--
2.25.1