]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blobdiff - drivers/pci/probe.c
Merge branch 'pci/virtualization'
[mirror_ubuntu-focal-kernel.git] / drivers / pci / probe.c
index 4c35c2909d577baa21ce7baefb054a95bfc45b4b..ec784009a36b8d7b694ebb8f0b26a3f3810636f5 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/cpumask.h>
-#include <linux/pci-aspm.h>
 #include <linux/aer.h>
 #include <linux/acpi.h>
 #include <linux/hypervisor.h>
@@ -1549,6 +1548,20 @@ static int pci_intx_mask_broken(struct pci_dev *dev)
        return 0;
 }
 
+static void early_dump_pci_device(struct pci_dev *pdev)
+{
+       u32 value[256 / 4];
+       int i;
+
+       pci_info(pdev, "config space:\n");
+
+       for (i = 0; i < 256; i += 4)
+               pci_read_config_dword(pdev, i, &value[i / 4]);
+
+       print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+                      value, 256, false);
+}
+
 /**
  * pci_setup_device - Fill in class and map information of a device
  * @dev: the device structure to fill
@@ -1598,6 +1611,9 @@ int pci_setup_device(struct pci_dev *dev)
        pci_printk(KERN_DEBUG, dev, "[%04x:%04x] type %02x class %#08x\n",
                   dev->vendor, dev->device, dev->hdr_type, dev->class);
 
+       if (pci_early_dump)
+               early_dump_pci_device(dev);
+
        /* Need to have dev->class ready */
        dev->cfg_size = pci_cfg_space_size(dev);
 
@@ -1725,11 +1741,15 @@ int pci_setup_device(struct pci_dev *dev)
 static void pci_configure_mps(struct pci_dev *dev)
 {
        struct pci_dev *bridge = pci_upstream_bridge(dev);
-       int mps, p_mps, rc;
+       int mps, mpss, p_mps, rc;
 
        if (!pci_is_pcie(dev) || !bridge || !pci_is_pcie(bridge))
                return;
 
+       /* MPS and MRRS fields are of type 'RsvdP' for VFs, short-circuit out */
+       if (dev->is_virtfn)
+               return;
+
        mps = pcie_get_mps(dev);
        p_mps = pcie_get_mps(bridge);
 
@@ -1749,6 +1769,14 @@ static void pci_configure_mps(struct pci_dev *dev)
        if (pcie_bus_config != PCIE_BUS_DEFAULT)
                return;
 
+       mpss = 128 << dev->pcie_mpss;
+       if (mpss < p_mps && pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT) {
+               pcie_set_mps(bridge, mpss);
+               pci_info(dev, "Upstream bridge's Max Payload Size set to %d (was %d, max %d)\n",
+                        mpss, p_mps, 128 << bridge->pcie_mpss);
+               p_mps = pcie_get_mps(bridge);
+       }
+
        rc = pcie_set_mps(dev, p_mps);
        if (rc) {
                pci_warn(dev, "can't set Max Payload Size to %d; if necessary, use \"pci=pcie_bus_safe\" and report a bug\n",
@@ -1757,7 +1785,7 @@ static void pci_configure_mps(struct pci_dev *dev)
        }
 
        pci_info(dev, "Max Payload Size set to %d (was %d, max %d)\n",
-                p_mps, mps, 128 << dev->pcie_mpss);
+                p_mps, mps, mpss);
 }
 
 static struct hpp_type0 pci_default_type0 = {
@@ -2088,6 +2116,7 @@ static void pci_configure_device(struct pci_dev *dev)
 
 static void pci_release_capabilities(struct pci_dev *dev)
 {
+       pci_aer_exit(dev);
        pci_vpd_release(dev);
        pci_iov_release(dev);
        pci_free_cap_save_buffers(dev);
@@ -2180,8 +2209,8 @@ static bool pci_bus_wait_crs(struct pci_bus *bus, int devfn, u32 *l,
        return true;
 }
 
-bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
-                               int timeout)
+bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
+                                       int timeout)
 {
        if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
                return false;
@@ -2196,6 +2225,24 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
 
        return true;
 }
+
+bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
+                               int timeout)
+{
+#ifdef CONFIG_PCI_QUIRKS
+       struct pci_dev *bridge = bus->self;
+
+       /*
+        * Certain IDT switches have an issue where they improperly trigger
+        * ACS Source Validation errors on completions for config reads.
+        */
+       if (bridge && bridge->vendor == PCI_VENDOR_ID_IDT &&
+           bridge->device == 0x80b5)
+               return pci_idt_bus_quirk(bus, devfn, l, timeout);
+#endif
+
+       return pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
+}
 EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
 
 /*
@@ -2229,6 +2276,25 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
        return dev;
 }
 
+static void pcie_report_downtraining(struct pci_dev *dev)
+{
+       if (!pci_is_pcie(dev))
+               return;
+
+       /* Look from the device up to avoid downstream ports with no devices */
+       if ((pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT) &&
+           (pci_pcie_type(dev) != PCI_EXP_TYPE_LEG_END) &&
+           (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM))
+               return;
+
+       /* Multi-function PCIe devices share the same link/status */
+       if (PCI_FUNC(dev->devfn) != 0 || dev->is_virtfn)
+               return;
+
+       /* Print link status only if the device is constrained by the fabric */
+       __pcie_print_link_status(dev, false);
+}
+
 static void pci_init_capabilities(struct pci_dev *dev)
 {
        /* Enhanced Allocation */
@@ -2264,6 +2330,8 @@ static void pci_init_capabilities(struct pci_dev *dev)
        /* Advanced Error Reporting */
        pci_aer_init(dev);
 
+       pcie_report_downtraining(dev);
+
        if (pci_probe_reset_function(dev) == 0)
                dev->reset_fn = 1;
 }
@@ -2457,13 +2525,13 @@ int pci_scan_slot(struct pci_bus *bus, int devfn)
        dev = pci_scan_single_device(bus, devfn);
        if (!dev)
                return 0;
-       if (!dev->is_added)
+       if (!pci_dev_is_added(dev))
                nr++;
 
        for (fn = next_fn(bus, dev, 0); fn > 0; fn = next_fn(bus, dev, fn)) {
                dev = pci_scan_single_device(bus, devfn + fn);
                if (dev) {
-                       if (!dev->is_added)
+                       if (!pci_dev_is_added(dev))
                                nr++;
                        dev->multifunction = 1;
                }