diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2017-04-28 18:33:55 +0300 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-04-28 18:33:55 +0300 |
commit | f503ee4cbef9c5459d9e176542e4d0e0ed3915f6 (patch) | |
tree | 0d2c86dd676b14ff8b39b4ded4bb741e99b8e8a2 /drivers/pci | |
parent | d060c6fcef972e6a8d0898c55f1b7b6da89a6744 (diff) | |
parent | 9b70ae4951746146838b474c3a90722666edf4c1 (diff) | |
download | linux-f503ee4cbef9c5459d9e176542e4d0e0ed3915f6.tar.xz |
Merge branch 'pci/enumeration' into next
* pci/enumeration:
PCI: Include PCI-to-PCIe bridges as "Downstream Ports"
PCI: Improve __pci_read_base() robustness
PCI: Short-circuit pci_device_is_present() for disconnected devices
PCI/MSI: Skip disabling disconnected devices
PCI: Don't attempt config access to disconnected devices
PCI: Add device disconnected state
PCI: Export PCI device config accessors
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/access.c | 59 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_pci.c | 6 | ||||
-rw-r--r-- | drivers/pci/msi.c | 7 | ||||
-rw-r--r-- | drivers/pci/pci.c | 2 | ||||
-rw-r--r-- | drivers/pci/pci.h | 14 | ||||
-rw-r--r-- | drivers/pci/pcie/pcie-dpc.c | 5 | ||||
-rw-r--r-- | drivers/pci/probe.c | 2 |
7 files changed, 92 insertions, 3 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 8b7382705bf2..9b09cd31158c 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -700,7 +700,8 @@ static bool pcie_downstream_port(const struct pci_dev *dev) int type = pci_pcie_type(dev); return type == PCI_EXP_TYPE_ROOT_PORT || - type == PCI_EXP_TYPE_DOWNSTREAM; + type == PCI_EXP_TYPE_DOWNSTREAM || + type == PCI_EXP_TYPE_PCIE_BRIDGE; } bool pcie_cap_has_lnkctl(const struct pci_dev *dev) @@ -890,3 +891,59 @@ int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos, return ret; } EXPORT_SYMBOL(pcie_capability_clear_and_set_dword); + +int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val) +{ + if (pci_dev_is_disconnected(dev)) { + *val = ~0; + return -ENODEV; + } + return pci_bus_read_config_byte(dev->bus, dev->devfn, where, val); +} +EXPORT_SYMBOL(pci_read_config_byte); + +int pci_read_config_word(const struct pci_dev *dev, int where, u16 *val) +{ + if (pci_dev_is_disconnected(dev)) { + *val = ~0; + return -ENODEV; + } + return pci_bus_read_config_word(dev->bus, dev->devfn, where, val); +} +EXPORT_SYMBOL(pci_read_config_word); + +int pci_read_config_dword(const struct pci_dev *dev, int where, + u32 *val) +{ + if (pci_dev_is_disconnected(dev)) { + *val = ~0; + return -ENODEV; + } + return pci_bus_read_config_dword(dev->bus, dev->devfn, where, val); +} +EXPORT_SYMBOL(pci_read_config_dword); + +int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val) +{ + if (pci_dev_is_disconnected(dev)) + return -ENODEV; + return pci_bus_write_config_byte(dev->bus, dev->devfn, where, val); +} +EXPORT_SYMBOL(pci_write_config_byte); + +int pci_write_config_word(const struct pci_dev *dev, int where, u16 val) +{ + if (pci_dev_is_disconnected(dev)) + return -ENODEV; + return pci_bus_write_config_word(dev->bus, dev->devfn, where, val); +} +EXPORT_SYMBOL(pci_write_config_word); + +int pci_write_config_dword(const struct pci_dev *dev, int where, + u32 val) +{ + if (pci_dev_is_disconnected(dev)) + return -ENODEV; + return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val); +} +EXPORT_SYMBOL(pci_write_config_dword); diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 9e69403be632..19f30a9f461d 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -109,6 +109,12 @@ int pciehp_unconfigure_device(struct slot *p_slot) break; } } + if (!presence) { + pci_dev_set_disconnected(dev, NULL); + if (pci_has_subordinate(dev)) + pci_walk_bus(dev->subordinate, + pci_dev_set_disconnected, NULL); + } pci_stop_and_remove_bus_device(dev); /* * Ensure that no new Requests will be generated from diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index d571bc330686..33e766a9dd91 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -298,7 +298,7 @@ void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { struct pci_dev *dev = msi_desc_to_pci_dev(entry); - if (dev->current_state != PCI_D0) { + if (dev->current_state != PCI_D0 || pci_dev_is_disconnected(dev)) { /* Don't touch the hardware now */ } else if (entry->msi_attrib.is_msix) { void __iomem *base = pci_msix_desc_addr(entry); @@ -1001,6 +1001,11 @@ void pci_msix_shutdown(struct pci_dev *dev) if (!pci_msi_enable || !dev || !dev->msix_enabled) return; + if (pci_dev_is_disconnected(dev)) { + dev->msix_enabled = 0; + return; + } + /* Return the device with MSI-X masked as initial states */ for_each_pci_msi_entry(entry, dev) { /* Keep cached states to be restored */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c87d1edf0203..f85774b3da1e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4934,6 +4934,8 @@ bool pci_device_is_present(struct pci_dev *pdev) { u32 v; + if (pci_dev_is_disconnected(pdev)) + return false; return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0); } EXPORT_SYMBOL_GPL(pci_device_is_present); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8dd38e69d6f2..245719c3e409 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -274,6 +274,20 @@ struct pci_sriov { resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */ }; +/* pci_dev priv_flags */ +#define PCI_DEV_DISCONNECTED 0 + +static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused) +{ + set_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags); + return 0; +} + +static inline bool pci_dev_is_disconnected(const struct pci_dev *dev) +{ + return test_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags); +} + #ifdef CONFIG_PCI_ATS void pci_restore_ats_state(struct pci_dev *dev); #else diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c index d4d70ef4a2d7..77d2ca99d2ec 100644 --- a/drivers/pci/pcie/pcie-dpc.c +++ b/drivers/pci/pcie/pcie-dpc.c @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/pci.h> #include <linux/pcieport_if.h> +#include "../pci.h" struct dpc_dev { struct pcie_device *dev; @@ -66,6 +67,10 @@ static void interrupt_event_handler(struct work_struct *work) list_for_each_entry_safe_reverse(dev, temp, &parent->devices, bus_list) { pci_dev_get(dev); + pci_dev_set_disconnected(dev, NULL); + if (pci_has_subordinate(dev)) + pci_walk_bus(dev->subordinate, + pci_dev_set_disconnected, NULL); pci_stop_and_remove_bus_device(dev); pci_dev_put(dev); } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dfc9a2794141..f2bd0024cb88 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -175,7 +175,7 @@ static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar) int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int pos) { - u32 l, sz, mask; + u32 l = 0, sz = 0, mask; u64 l64, sz64, mask64; u16 orig_cmd; struct pci_bus_region region, inverted_region; |