diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-08-10 17:52:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-08-10 17:52:45 -0400 |
commit | 4e082e9ba7cdd7466b1ea81527feb93f4da8c580 (patch) | |
tree | 68092acc840dc1df09813b2808a88fc7b0847a26 | |
parent | 26273939ace935dd7553b31d279eab30b40f7b9a (diff) | |
parent | 8466489ef5ba48272ba4fa4ea9f8f403306de4c7 (diff) |
Merge tag 'pci-v4.13-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI fix from Bjorn Helgaas:
"Work around Renesas uPD72020x 32-bit DMA issue"
* tag 'pci-v4.13-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci:
xhci: Reset Renesas uPD72020x USB controller for 32-bit DMA issue
PCI: Add pci_reset_function_locked()
-rw-r--r-- | drivers/pci/pci.c | 35 | ||||
-rw-r--r-- | drivers/usb/host/pci-quirks.c | 20 | ||||
-rw-r--r-- | drivers/usb/host/pci-quirks.h | 1 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 7 | ||||
-rw-r--r-- | include/linux/pci.h | 1 |
5 files changed, 64 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index af0cc3456dc1..b4b7eab29400 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -4260,6 +4260,41 @@ int pci_reset_function(struct pci_dev *dev) | |||
4260 | EXPORT_SYMBOL_GPL(pci_reset_function); | 4260 | EXPORT_SYMBOL_GPL(pci_reset_function); |
4261 | 4261 | ||
4262 | /** | 4262 | /** |
4263 | * pci_reset_function_locked - quiesce and reset a PCI device function | ||
4264 | * @dev: PCI device to reset | ||
4265 | * | ||
4266 | * Some devices allow an individual function to be reset without affecting | ||
4267 | * other functions in the same device. The PCI device must be responsive | ||
4268 | * to PCI config space in order to use this function. | ||
4269 | * | ||
4270 | * This function does not just reset the PCI portion of a device, but | ||
4271 | * clears all the state associated with the device. This function differs | ||
4272 | * from __pci_reset_function() in that it saves and restores device state | ||
4273 | * over the reset. It also differs from pci_reset_function() in that it | ||
4274 | * requires the PCI device lock to be held. | ||
4275 | * | ||
4276 | * Returns 0 if the device function was successfully reset or negative if the | ||
4277 | * device doesn't support resetting a single function. | ||
4278 | */ | ||
4279 | int pci_reset_function_locked(struct pci_dev *dev) | ||
4280 | { | ||
4281 | int rc; | ||
4282 | |||
4283 | rc = pci_probe_reset_function(dev); | ||
4284 | if (rc) | ||
4285 | return rc; | ||
4286 | |||
4287 | pci_dev_save_and_disable(dev); | ||
4288 | |||
4289 | rc = __pci_reset_function_locked(dev); | ||
4290 | |||
4291 | pci_dev_restore(dev); | ||
4292 | |||
4293 | return rc; | ||
4294 | } | ||
4295 | EXPORT_SYMBOL_GPL(pci_reset_function_locked); | ||
4296 | |||
4297 | /** | ||
4263 | * pci_try_reset_function - quiesce and reset a PCI device function | 4298 | * pci_try_reset_function - quiesce and reset a PCI device function |
4264 | * @dev: PCI device to reset | 4299 | * @dev: PCI device to reset |
4265 | * | 4300 | * |
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index c8989c62a262..858fefd67ebe 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c | |||
@@ -1150,3 +1150,23 @@ static void quirk_usb_early_handoff(struct pci_dev *pdev) | |||
1150 | } | 1150 | } |
1151 | DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, | 1151 | DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, |
1152 | PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff); | 1152 | PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff); |
1153 | |||
1154 | bool usb_xhci_needs_pci_reset(struct pci_dev *pdev) | ||
1155 | { | ||
1156 | /* | ||
1157 | * Our dear uPD72020{1,2} friend only partially resets when | ||
1158 | * asked to via the XHCI interface, and may end up doing DMA | ||
1159 | * at the wrong addresses, as it keeps the top 32bit of some | ||
1160 | * addresses from its previous programming under obscure | ||
1161 | * circumstances. | ||
1162 | * Give it a good wack at probe time. Unfortunately, this | ||
1163 | * needs to happen before we've had a chance to discover any | ||
1164 | * quirk, or the system will be in a rather bad state. | ||
1165 | */ | ||
1166 | if (pdev->vendor == PCI_VENDOR_ID_RENESAS && | ||
1167 | (pdev->device == 0x0014 || pdev->device == 0x0015)) | ||
1168 | return true; | ||
1169 | |||
1170 | return false; | ||
1171 | } | ||
1172 | 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 655994480198..5582cbafecd4 100644 --- a/drivers/usb/host/pci-quirks.h +++ b/drivers/usb/host/pci-quirks.h | |||
@@ -15,6 +15,7 @@ void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev); | |||
15 | void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev); | 15 | void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev); |
16 | void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); | 16 | void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); |
17 | void sb800_prefetch(struct device *dev, int on); | 17 | void sb800_prefetch(struct device *dev, int on); |
18 | bool usb_xhci_needs_pci_reset(struct pci_dev *pdev); | ||
18 | #else | 19 | #else |
19 | struct pci_dev; | 20 | struct pci_dev; |
20 | static inline void usb_amd_quirk_pll_disable(void) {} | 21 | 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 5b0fa553c8bc..8071c8fdd15e 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c | |||
@@ -284,6 +284,13 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
284 | 284 | ||
285 | driver = (struct hc_driver *)id->driver_data; | 285 | driver = (struct hc_driver *)id->driver_data; |
286 | 286 | ||
287 | /* For some HW implementation, a XHCI reset is just not enough... */ | ||
288 | if (usb_xhci_needs_pci_reset(dev)) { | ||
289 | dev_info(&dev->dev, "Resetting\n"); | ||
290 | if (pci_reset_function_locked(dev)) | ||
291 | dev_warn(&dev->dev, "Reset failed"); | ||
292 | } | ||
293 | |||
287 | /* Prevent runtime suspending between USB-2 and USB-3 initialization */ | 294 | /* Prevent runtime suspending between USB-2 and USB-3 initialization */ |
288 | pm_runtime_get_noresume(&dev->dev); | 295 | pm_runtime_get_noresume(&dev->dev); |
289 | 296 | ||
diff --git a/include/linux/pci.h b/include/linux/pci.h index 4869e66dd659..a75c13673852 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -1067,6 +1067,7 @@ void pcie_flr(struct pci_dev *dev); | |||
1067 | int __pci_reset_function(struct pci_dev *dev); | 1067 | int __pci_reset_function(struct pci_dev *dev); |
1068 | int __pci_reset_function_locked(struct pci_dev *dev); | 1068 | int __pci_reset_function_locked(struct pci_dev *dev); |
1069 | int pci_reset_function(struct pci_dev *dev); | 1069 | int pci_reset_function(struct pci_dev *dev); |
1070 | int pci_reset_function_locked(struct pci_dev *dev); | ||
1070 | int pci_try_reset_function(struct pci_dev *dev); | 1071 | int pci_try_reset_function(struct pci_dev *dev); |
1071 | int pci_probe_reset_slot(struct pci_slot *slot); | 1072 | int pci_probe_reset_slot(struct pci_slot *slot); |
1072 | int pci_reset_slot(struct pci_slot *slot); | 1073 | int pci_reset_slot(struct pci_slot *slot); |