|
@@ -2359,6 +2359,75 @@ void pci_enable_acs(struct pci_dev *dev)
|
|
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
|
|
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * pci_acs_enabled - test ACS against required flags for a given device
|
|
|
|
+ * @pdev: device to test
|
|
|
|
+ * @acs_flags: required PCI ACS flags
|
|
|
|
+ *
|
|
|
|
+ * Return true if the device supports the provided flags. Automatically
|
|
|
|
+ * filters out flags that are not implemented on multifunction devices.
|
|
|
|
+ */
|
|
|
|
+bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
|
|
|
|
+{
|
|
|
|
+ int pos, ret;
|
|
|
|
+ u16 ctrl;
|
|
|
|
+
|
|
|
|
+ ret = pci_dev_specific_acs_enabled(pdev, acs_flags);
|
|
|
|
+ if (ret >= 0)
|
|
|
|
+ return ret > 0;
|
|
|
|
+
|
|
|
|
+ if (!pci_is_pcie(pdev))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ /* Filter out flags not applicable to multifunction */
|
|
|
|
+ if (pdev->multifunction)
|
|
|
|
+ acs_flags &= (PCI_ACS_RR | PCI_ACS_CR |
|
|
|
|
+ PCI_ACS_EC | PCI_ACS_DT);
|
|
|
|
+
|
|
|
|
+ if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM ||
|
|
|
|
+ pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
|
|
|
|
+ pdev->multifunction) {
|
|
|
|
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
|
|
|
|
+ if (!pos)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
|
|
|
|
+ if ((ctrl & acs_flags) != acs_flags)
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * pci_acs_path_enable - test ACS flags from start to end in a hierarchy
|
|
|
|
+ * @start: starting downstream device
|
|
|
|
+ * @end: ending upstream device or NULL to search to the root bus
|
|
|
|
+ * @acs_flags: required flags
|
|
|
|
+ *
|
|
|
|
+ * Walk up a device tree from start to end testing PCI ACS support. If
|
|
|
|
+ * any step along the way does not support the required flags, return false.
|
|
|
|
+ */
|
|
|
|
+bool pci_acs_path_enabled(struct pci_dev *start,
|
|
|
|
+ struct pci_dev *end, u16 acs_flags)
|
|
|
|
+{
|
|
|
|
+ struct pci_dev *pdev, *parent = start;
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ pdev = parent;
|
|
|
|
+
|
|
|
|
+ if (!pci_acs_enabled(pdev, acs_flags))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (pci_is_root_bus(pdev->bus))
|
|
|
|
+ return (end == NULL);
|
|
|
|
+
|
|
|
|
+ parent = pdev->bus->self;
|
|
|
|
+ } while (pdev != end);
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge
|
|
* pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge
|
|
* @dev: the PCI device
|
|
* @dev: the PCI device
|