diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-08 15:51:57 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-08 15:51:57 -0700 |
commit | f263fbb8d60824993c1b64385056a3cfdbb21d45 (patch) | |
tree | 9ce14a362e1cb0aef2b4a79d6fb84d883118cce3 /drivers/pci/pci.c | |
parent | 026d15f6b9878794fae1f794cae881ccd65052e5 (diff) | |
parent | 6aed468480e8b03ece5a395fe8013e66348a2547 (diff) | |
download | op-kernel-dev-f263fbb8d60824993c1b64385056a3cfdbb21d45.zip op-kernel-dev-f263fbb8d60824993c1b64385056a3cfdbb21d45.tar.gz |
Merge tag 'pci-v4.13-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI updates from Bjorn Helgaas:
- add sysfs max_link_speed/width, current_link_speed/width (Wong Vee
Khee)
- make host bridge IRQ mapping much more generic (Matthew Minter,
Lorenzo Pieralisi)
- convert most drivers to pci_scan_root_bus_bridge() (Lorenzo
Pieralisi)
- mutex sriov_configure() (Jakub Kicinski)
- mutex pci_error_handlers callbacks (Christoph Hellwig)
- split ->reset_notify() into ->reset_prepare()/reset_done()
(Christoph Hellwig)
- support multiple PCIe portdrv interrupts for MSI as well as MSI-X
(Gabriele Paoloni)
- allocate MSI/MSI-X vector for Downstream Port Containment (Gabriele
Paoloni)
- fix MSI IRQ affinity pre/post/min_vecs issue (Michael Hernandez)
- test INTx masking during enumeration, not at run-time (Piotr Gregor)
- avoid using device_may_wakeup() for runtime PM (Rafael J. Wysocki)
- restore the status of PCI devices across hibernation (Chen Yu)
- keep parent resources that start at 0x0 (Ard Biesheuvel)
- enable ECRC only if device supports it (Bjorn Helgaas)
- restore PRI and PASID state after Function-Level Reset (CQ Tang)
- skip DPC event if device is not present (Keith Busch)
- check domain when matching SMBIOS info (Sujith Pandel)
- mark Intel XXV710 NIC INTx masking as broken (Alex Williamson)
- avoid AMD SB7xx EHCI USB wakeup defect (Kai-Heng Feng)
- work around long-standing Macbook Pro poweroff issue (Bjorn Helgaas)
- add Switchtec "running" status flag (Logan Gunthorpe)
- fix dra7xx incorrect RW1C IRQ register usage (Arvind Yadav)
- modify xilinx-nwl IRQ chip for legacy interrupts (Bharat Kumar
Gogada)
- move VMD SRCU cleanup after bus, child device removal (Jon Derrick)
- add Faraday clock handling (Linus Walleij)
- configure Rockchip MPS and reorganize (Shawn Lin)
- limit Qualcomm TLP size to 2K (hardware issue) (Srinivas Kandagatla)
- support Tegra MSI 64-bit addressing (Thierry Reding)
- use Rockchip normal (not privileged) register bank (Shawn Lin)
- add HiSilicon Kirin SoC PCIe controller driver (Xiaowei Song)
- add Sigma Designs Tango SMP8759 PCIe controller driver (Marc
Gonzalez)
- add MediaTek PCIe host controller support (Ryder Lee)
- add Qualcomm IPQ4019 support (John Crispin)
- add HyperV vPCI protocol v1.2 support (Jork Loeser)
- add i.MX6 regulator support (Quentin Schulz)
* tag 'pci-v4.13-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (113 commits)
PCI: tango: Add Sigma Designs Tango SMP8759 PCIe host bridge support
PCI: Add DT binding for Sigma Designs Tango PCIe controller
PCI: rockchip: Use normal register bank for config accessors
dt-bindings: PCI: Add documentation for MediaTek PCIe
PCI: Remove __pci_dev_reset() and pci_dev_reset()
PCI: Split ->reset_notify() method into ->reset_prepare() and ->reset_done()
PCI: xilinx: Make of_device_ids const
PCI: xilinx-nwl: Modify IRQ chip for legacy interrupts
PCI: vmd: Move SRCU cleanup after bus, child device removal
PCI: vmd: Correct comment: VMD domains start at 0x10000, not 0x1000
PCI: versatile: Add local struct device pointers
PCI: tegra: Do not allocate MSI target memory
PCI: tegra: Support MSI 64-bit addressing
PCI: rockchip: Use local struct device pointer consistently
PCI: rockchip: Check for clk_prepare_enable() errors during resume
MAINTAINERS: Remove Wenrui Li as Rockchip PCIe driver maintainer
PCI: rockchip: Configure RC's MPS setting
PCI: rockchip: Reconfigure configuration space header type
PCI: rockchip: Split out rockchip_pcie_cfg_configuration_accesses()
PCI: rockchip: Move configuration accesses into rockchip_pcie_cfg_atu()
...
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 227 |
1 files changed, 98 insertions, 129 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0b5302a..d88edf5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -28,6 +28,7 @@ #include <linux/pm_runtime.h> #include <linux/pci_hotplug.h> #include <linux/vmalloc.h> +#include <linux/pci-ats.h> #include <asm/setup.h> #include <asm/dma.h> #include <linux/aer.h> @@ -455,7 +456,7 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev, pci_bus_for_each_resource(bus, r, i) { if (!r) continue; - if (res->start && resource_contains(r, res)) { + if (resource_contains(r, res)) { /* * If the window is prefetchable but the BAR is @@ -1166,6 +1167,8 @@ void pci_restore_state(struct pci_dev *dev) /* PCI Express register must be restored first */ pci_restore_pcie_state(dev); + pci_restore_pasid_state(dev); + pci_restore_pri_state(dev); pci_restore_ats_state(dev); pci_restore_vc_state(dev); @@ -1966,12 +1969,13 @@ EXPORT_SYMBOL(pci_wake_from_d3); /** * pci_target_state - find an appropriate low power state for a given PCI dev * @dev: PCI device + * @wakeup: Whether or not wakeup functionality will be enabled for the device. * * Use underlying platform code to find a supported low power state for @dev. * If the platform can't manage @dev, return the deepest state from which it * can generate wake events, based on any available PME info. */ -static pci_power_t pci_target_state(struct pci_dev *dev) +static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup) { pci_power_t target_state = PCI_D3hot; @@ -2008,7 +2012,7 @@ static pci_power_t pci_target_state(struct pci_dev *dev) if (dev->current_state == PCI_D3cold) target_state = PCI_D3cold; - if (device_may_wakeup(&dev->dev)) { + if (wakeup) { /* * Find the deepest state from which the device can generate * wake-up events, make it the target state and enable device @@ -2034,13 +2038,14 @@ static pci_power_t pci_target_state(struct pci_dev *dev) */ int pci_prepare_to_sleep(struct pci_dev *dev) { - pci_power_t target_state = pci_target_state(dev); + bool wakeup = device_may_wakeup(&dev->dev); + pci_power_t target_state = pci_target_state(dev, wakeup); int error; if (target_state == PCI_POWER_ERROR) return -EIO; - pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev)); + pci_enable_wake(dev, target_state, wakeup); error = pci_set_power_state(dev, target_state); @@ -2073,9 +2078,10 @@ EXPORT_SYMBOL(pci_back_from_sleep); */ int pci_finish_runtime_suspend(struct pci_dev *dev) { - pci_power_t target_state = pci_target_state(dev); + pci_power_t target_state; int error; + target_state = pci_target_state(dev, device_can_wakeup(&dev->dev)); if (target_state == PCI_POWER_ERROR) return -EIO; @@ -2111,8 +2117,8 @@ bool pci_dev_run_wake(struct pci_dev *dev) if (!dev->pme_support) return false; - /* PME-capable in principle, but not from the intended sleep state */ - if (!pci_pme_capable(dev, pci_target_state(dev))) + /* PME-capable in principle, but not from the target power state */ + if (!pci_pme_capable(dev, pci_target_state(dev, false))) return false; while (bus->parent) { @@ -2147,9 +2153,10 @@ EXPORT_SYMBOL_GPL(pci_dev_run_wake); bool pci_dev_keep_suspended(struct pci_dev *pci_dev) { struct device *dev = &pci_dev->dev; + bool wakeup = device_may_wakeup(dev); if (!pm_runtime_suspended(dev) - || pci_target_state(pci_dev) != pci_dev->current_state + || pci_target_state(pci_dev, wakeup) != pci_dev->current_state || platform_pci_need_resume(pci_dev) || (pci_dev->dev_flags & PCI_DEV_FLAGS_NEEDS_RESUME)) return false; @@ -2167,7 +2174,7 @@ bool pci_dev_keep_suspended(struct pci_dev *pci_dev) spin_lock_irq(&dev->power.lock); if (pm_runtime_suspended(dev) && pci_dev->current_state < PCI_D3cold && - !device_may_wakeup(dev)) + !wakeup) __pci_pme_active(pci_dev, false); spin_unlock_irq(&dev->power.lock); @@ -3715,46 +3722,6 @@ void pci_intx(struct pci_dev *pdev, int enable) } EXPORT_SYMBOL_GPL(pci_intx); -/** - * pci_intx_mask_supported - probe for INTx masking support - * @dev: the PCI device to operate on - * - * Check if the device dev support INTx masking via the config space - * command word. - */ -bool pci_intx_mask_supported(struct pci_dev *dev) -{ - bool mask_supported = false; - u16 orig, new; - - if (dev->broken_intx_masking) - return false; - - pci_cfg_access_lock(dev); - - pci_read_config_word(dev, PCI_COMMAND, &orig); - pci_write_config_word(dev, PCI_COMMAND, - orig ^ PCI_COMMAND_INTX_DISABLE); - pci_read_config_word(dev, PCI_COMMAND, &new); - - /* - * There's no way to protect against hardware bugs or detect them - * reliably, but as long as we know what the value should be, let's - * go ahead and check it. - */ - if ((new ^ orig) & ~PCI_COMMAND_INTX_DISABLE) { - dev_err(&dev->dev, "Command register changed from 0x%x to 0x%x: driver or hardware bug?\n", - orig, new); - } else if ((new ^ orig) & PCI_COMMAND_INTX_DISABLE) { - mask_supported = true; - pci_write_config_word(dev, PCI_COMMAND, orig); - } - - pci_cfg_access_unlock(dev); - return mask_supported; -} -EXPORT_SYMBOL_GPL(pci_intx_mask_supported); - static bool pci_check_and_set_intx_mask(struct pci_dev *dev, bool mask) { struct pci_bus *bus = dev->bus; @@ -3805,7 +3772,7 @@ done: * @dev: the PCI device to operate on * * Check if the device dev has its INTx line asserted, mask it and - * return true in that case. False is returned if not interrupt was + * return true in that case. False is returned if no interrupt was * pending. */ bool pci_check_and_mask_intx(struct pci_dev *dev) @@ -4075,40 +4042,6 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe) return pci_reset_hotplug_slot(dev->slot->hotplug, probe); } -static int __pci_dev_reset(struct pci_dev *dev, int probe) -{ - int rc; - - might_sleep(); - - rc = pci_dev_specific_reset(dev, probe); - if (rc != -ENOTTY) - goto done; - - if (pcie_has_flr(dev)) { - if (!probe) - pcie_flr(dev); - rc = 0; - goto done; - } - - rc = pci_af_flr(dev, probe); - if (rc != -ENOTTY) - goto done; - - rc = pci_pm_reset(dev, probe); - if (rc != -ENOTTY) - goto done; - - rc = pci_dev_reset_slot_function(dev, probe); - if (rc != -ENOTTY) - goto done; - - rc = pci_parent_bus_reset(dev, probe); -done: - return rc; -} - static void pci_dev_lock(struct pci_dev *dev) { pci_cfg_access_lock(dev); @@ -4134,26 +4067,18 @@ static void pci_dev_unlock(struct pci_dev *dev) pci_cfg_access_unlock(dev); } -/** - * pci_reset_notify - notify device driver of reset - * @dev: device to be notified of reset - * @prepare: 'true' if device is about to be reset; 'false' if reset attempt - * completed - * - * Must be called prior to device access being disabled and after device - * access is restored. - */ -static void pci_reset_notify(struct pci_dev *dev, bool prepare) +static void pci_dev_save_and_disable(struct pci_dev *dev) { const struct pci_error_handlers *err_handler = dev->driver ? dev->driver->err_handler : NULL; - if (err_handler && err_handler->reset_notify) - err_handler->reset_notify(dev, prepare); -} -static void pci_dev_save_and_disable(struct pci_dev *dev) -{ - pci_reset_notify(dev, true); + /* + * dev->driver->err_handler->reset_prepare() is protected against + * races with ->remove() by the device lock, which must be held by + * the caller. + */ + if (err_handler && err_handler->reset_prepare) + err_handler->reset_prepare(dev); /* * Wake-up device prior to save. PM registers default to D0 after @@ -4175,23 +4100,18 @@ static void pci_dev_save_and_disable(struct pci_dev *dev) static void pci_dev_restore(struct pci_dev *dev) { - pci_restore_state(dev); - pci_reset_notify(dev, false); -} - -static int pci_dev_reset(struct pci_dev *dev, int probe) -{ - int rc; - - if (!probe) - pci_dev_lock(dev); - - rc = __pci_dev_reset(dev, probe); + const struct pci_error_handlers *err_handler = + dev->driver ? dev->driver->err_handler : NULL; - if (!probe) - pci_dev_unlock(dev); + pci_restore_state(dev); - return rc; + /* + * dev->driver->err_handler->reset_done() is protected against + * races with ->remove() by the device lock, which must be held by + * the caller. + */ + if (err_handler && err_handler->reset_done) + err_handler->reset_done(dev); } /** @@ -4213,7 +4133,13 @@ static int pci_dev_reset(struct pci_dev *dev, int probe) */ int __pci_reset_function(struct pci_dev *dev) { - return pci_dev_reset(dev, 0); + int ret; + + pci_dev_lock(dev); + ret = __pci_reset_function_locked(dev); + pci_dev_unlock(dev); + + return ret; } EXPORT_SYMBOL_GPL(__pci_reset_function); @@ -4238,7 +4164,27 @@ EXPORT_SYMBOL_GPL(__pci_reset_function); */ int __pci_reset_function_locked(struct pci_dev *dev) { - return __pci_dev_reset(dev, 0); + int rc; + + might_sleep(); + + rc = pci_dev_specific_reset(dev, 0); + if (rc != -ENOTTY) + return rc; + if (pcie_has_flr(dev)) { + pcie_flr(dev); + return 0; + } + rc = pci_af_flr(dev, 0); + if (rc != -ENOTTY) + return rc; + rc = pci_pm_reset(dev, 0); + if (rc != -ENOTTY) + return rc; + rc = pci_dev_reset_slot_function(dev, 0); + if (rc != -ENOTTY) + return rc; + return pci_parent_bus_reset(dev, 0); } EXPORT_SYMBOL_GPL(__pci_reset_function_locked); @@ -4255,7 +4201,26 @@ EXPORT_SYMBOL_GPL(__pci_reset_function_locked); */ int pci_probe_reset_function(struct pci_dev *dev) { - return pci_dev_reset(dev, 1); + int rc; + + might_sleep(); + + rc = pci_dev_specific_reset(dev, 1); + if (rc != -ENOTTY) + return rc; + if (pcie_has_flr(dev)) + return 0; + rc = pci_af_flr(dev, 1); + if (rc != -ENOTTY) + return rc; + rc = pci_pm_reset(dev, 1); + if (rc != -ENOTTY) + return rc; + rc = pci_dev_reset_slot_function(dev, 1); + if (rc != -ENOTTY) + return rc; + + return pci_parent_bus_reset(dev, 1); } /** @@ -4278,15 +4243,17 @@ int pci_reset_function(struct pci_dev *dev) { int rc; - rc = pci_dev_reset(dev, 1); + rc = pci_probe_reset_function(dev); if (rc) return rc; + pci_dev_lock(dev); pci_dev_save_and_disable(dev); - rc = pci_dev_reset(dev, 0); + rc = __pci_reset_function_locked(dev); pci_dev_restore(dev); + pci_dev_unlock(dev); return rc; } @@ -4302,20 +4269,18 @@ int pci_try_reset_function(struct pci_dev *dev) { int rc; - rc = pci_dev_reset(dev, 1); + rc = pci_probe_reset_function(dev); if (rc) return rc; - pci_dev_save_and_disable(dev); + if (!pci_dev_trylock(dev)) + return -EAGAIN; - if (pci_dev_trylock(dev)) { - rc = __pci_dev_reset(dev, 0); - pci_dev_unlock(dev); - } else - rc = -EAGAIN; + pci_dev_save_and_disable(dev); + rc = __pci_reset_function_locked(dev); + pci_dev_unlock(dev); pci_dev_restore(dev); - return rc; } EXPORT_SYMBOL_GPL(pci_try_reset_function); @@ -4465,7 +4430,9 @@ static void pci_bus_save_and_disable(struct pci_bus *bus) struct pci_dev *dev; list_for_each_entry(dev, &bus->devices, bus_list) { + pci_dev_lock(dev); pci_dev_save_and_disable(dev); + pci_dev_unlock(dev); if (dev->subordinate) pci_bus_save_and_disable(dev->subordinate); } @@ -4480,7 +4447,9 @@ static void pci_bus_restore(struct pci_bus *bus) struct pci_dev *dev; list_for_each_entry(dev, &bus->devices, bus_list) { + pci_dev_lock(dev); pci_dev_restore(dev); + pci_dev_unlock(dev); if (dev->subordinate) pci_bus_restore(dev->subordinate); } |