[regression] Force hard reset of Renesas uPD72020x USB controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Dear all,

I ran into a regression with an ExpressCard/54 USB 3.0 expansion card
that uses the Renesas uPD72020x chipset on a Lenovo X220i Laptop (the
described behavior has been reproduced with kernel versions 4.12.8,
4.12.13, 4.13.1 and 4.13.2).

Once booting such a kernel, the system becomes very sluggish with
considerable mouse pointer delays that are followed by display driver
errors such as
 - pipe A vblank wait timed out
 - pipe B vblank wait timed out
after some minutes.

Please refer to 
  https://weichselbraun.net/tmp/dmesg-pipe-a-vblank-timeout.log
  https://weichselbraun.net/tmp/dmesg-pipe-b-vblank-timeout.log
for the corresponding log messages.

Removing the expansion card or booting with a pre 4.12.8 kernel
resolves the issue.

I traced the problem back to the following patch that has been
introduced in 4.12.8 and seems to force a PCI reset on the device. 

<snip url="https://patchwork.ozlabs.org/patch/786253/";>;;;
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-
quirks.c
index a9a1e4c40480..5bed002c7dd3 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -1096,3 +1096,23 @@  static void quirk_usb_early_handoff(struct
pci_dev *pdev)
 }
 DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
 			PCI_CLASS_SERIAL_USB, 8,
quirk_usb_early_handoff);
+
+bool usb_xhci_needs_pci_reset(struct pci_dev *pdev)
+{
+	/*
+	 * Our dear uPD72020{1,2} friend only partially resets when
+	 * asked to via the XHCI interface, and may end-up doing DMA
+	 * at the wrong addresses, as it keeps the top 32bit of some
+	 * addresses from its previous programming under obscure
+	 * circumstances.
+	 * Give it a good wack at probe time. Unfortunately, this
+	 * needs to happen before we've had a chance to discover any
+	 * quirk, or the system will be in a rather bad state.
+	 */
+	if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
+	    (pdev->device == 0x0014 || pdev->device == 0x0015))
+		return true;
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(usb_xhci_needs_pci_reset);
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-
quirks.h
index 0222195bd5b0..dcbe4b097b33 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -14,6 +14,7 @@  void usb_amd_quirk_pll_enable(void);
 void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
 void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
 void sb800_prefetch(struct device *dev, int on);
+bool usb_xhci_needs_pci_reset(struct pci_dev *pdev);
 #else
 struct pci_dev;
 static inline void usb_amd_quirk_pll_disable(void) {}
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 1bcf971141c0..3831705c2817 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -267,6 +267,13 @@  static int xhci_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
 
 	driver = (struct hc_driver *)id->driver_data;
 
+	/* For some HW implementation, a XHCI reset is just not
enough... */
+	if (usb_xhci_needs_pci_reset(dev)) {
+		dev_info(&dev->dev, "Resetting\n");
+		if (pci_reset_function_locked(dev))
+			dev_warn(&dev->dev, "Reset failed");
+	}
+
 	/* Prevent runtime suspending between USB-2 and USB-3
initialization */
 	pm_runtime_get_noresume(&dev->dev);
</snip>

What seems to happen on my machine is that the reset fails leaving the
device in an undefined state:

<snip>
Sep 17 08:35:19 trinity kernel: [    2.874329] xhci_hcd 0000:05:00.0: 
  xHCI host controller not responding, assume dead
Sep 17 08:35:19 trinity kernel: [    2.874341] xhci_hcd 0000:05:00.0: 
  remove, state 1
Sep 17 08:35:19 trinity kernel: [    2.874350] usb usb4: USB 
  disconnect, device number 1
Sep 17 08:35:19 trinity kernel: [    2.874608] xhci_hcd 0000:05:00.0: 
  USB bus 4 deregistered
Sep 17 08:35:19 trinity kernel: [    2.875433] xhci_hcd 0000:05:00.0: 
  remove, state 1
Sep 17 08:35:19 trinity kernel: [    2.875440] usb usb3: USB 
  disconnect, device number 1
Sep 17 08:35:19 trinity kernel: [    2.875535] clocksource: Switched
to 
  clocksource tsc
Sep 17 08:35:19 trinity kernel: [    2.875769] xhci_hcd 0000:05:00.0: 
  Host halt failed, -19
Sep 17 08:35:19 trinity kernel: [    2.875777] xhci_hcd 0000:05:00.0: 
  Host not accessible, reset failed.
Sep 17 08:35:19 trinity kernel: [    2.876148] xhci_hcd 0000:05:00.0: 
  USB bus 3 deregistered
</snip>

The full dmesg output is available at
  https://weichselbraun.net/tmp/dmesg-4.13.2-unpatched

Reverting the changes (tested on 4.12.8, 4.13.1 and 4.13.2) solves this
issue on my system.  
  https://weichselbraun.net/tmp/dmesg-4.13.2-patched
 
lspci:
  05:00.0 USB controller: Renesas Technology Corp. uPD720202 USB 3.0 
    Host Controller (rev 02)

Please let me know if there is any additional information I can provide
to help narrowing this down.

Cheers,
  albert :)

Attachment: signature.asc
Description: This is a digitally signed message part


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux