PCI: Disable PCIe Relaxed Ordering if unsupported
[karo-tx-linux.git] / drivers / pci / probe.c
index c31310d..e6a917b 100644 (file)
@@ -1762,6 +1762,48 @@ static void pci_configure_extended_tags(struct pci_dev *dev)
                                         PCI_EXP_DEVCTL_EXT_TAG);
 }
 
+/**
+ * pcie_relaxed_ordering_enabled - Probe for PCIe relaxed ordering enable
+ * @dev: PCI device to query
+ *
+ * Returns true if the device has enabled relaxed ordering attribute.
+ */
+bool pcie_relaxed_ordering_enabled(struct pci_dev *dev)
+{
+       u16 v;
+
+       pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &v);
+
+       return !!(v & PCI_EXP_DEVCTL_RELAX_EN);
+}
+EXPORT_SYMBOL(pcie_relaxed_ordering_enabled);
+
+static void pci_configure_relaxed_ordering(struct pci_dev *dev)
+{
+       struct pci_dev *root;
+
+       /* PCI_EXP_DEVICE_RELAX_EN is RsvdP in VFs */
+       if (dev->is_virtfn)
+               return;
+
+       if (!pcie_relaxed_ordering_enabled(dev))
+               return;
+
+       /*
+        * For now, we only deal with Relaxed Ordering issues with Root
+        * Ports. Peer-to-Peer DMA is another can of worms.
+        */
+       root = pci_find_pcie_root_port(dev);
+       if (!root)
+               return;
+
+       if (root->dev_flags & PCI_DEV_FLAGS_NO_RELAXED_ORDERING) {
+               pcie_capability_clear_word(dev, PCI_EXP_DEVCTL,
+                                          PCI_EXP_DEVCTL_RELAX_EN);
+               dev_info(&dev->dev, "Disable Relaxed Ordering because the Root Port didn't support it\n");
+       }
+}
+
 static void pci_configure_device(struct pci_dev *dev)
 {
        struct hotplug_params hpp;
@@ -1769,6 +1811,7 @@ static void pci_configure_device(struct pci_dev *dev)
 
        pci_configure_mps(dev);
        pci_configure_extended_tags(dev);
+       pci_configure_relaxed_ordering(dev);
 
        memset(&hpp, 0, sizeof(hpp));
        ret = pci_get_hp_params(dev, &hpp);