Skip to content

Commit

Permalink
xhci: Reset Renesas uPD72020x USB controller for 32-bit DMA issue
Browse files Browse the repository at this point in the history
The Renesas uPD72020x XHCI controller seems to suffer from a really
annoying bug, where it may retain some of its DMA programming across a XHCI
reset, and despite the driver correctly programming new DMA addresses.
This is visible if the device has been using 64-bit DMA addresses, and is
then switched to using 32-bit DMA addresses.  The top 32 bits of the
address (now zero) are ignored are replaced by the 32 bits from the
*previous* programming.  Sticking with 64-bit DMA always works, but doesn't
seem very appropriate.

A PCI reset of the device restores the normal functionality, which is done
at probe time.  Unfortunately, this has to be done before any quirk has
been discovered, hence the intrusive nature of the fix.

Tested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Mathias Nyman <mathias.nyman@linux.intel.com>
CC: stable@vger.kernel.org	# v4.11+
  • Loading branch information
Marc Zyngier authored and Bjorn Helgaas committed Aug 2, 2017
1 parent a477b9c commit 8466489
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 0 deletions.
20 changes: 20 additions & 0 deletions drivers/usb/host/pci-quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1150,3 +1150,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);
1 change: 1 addition & 0 deletions drivers/usb/host/pci-quirks.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev);
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) {}
Expand Down
7 changes: 7 additions & 0 deletions drivers/usb/host/xhci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,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);

Expand Down

0 comments on commit 8466489

Please sign in to comment.