diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-13 17:04:54 -0600 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-13 17:04:54 -0600 |
commit | cc2fa3fa320d5f40a12713c104bbe5d3da4636e4 (patch) | |
tree | 342445a784c566116505ab8c9e7a24803a6e70c4 /drivers/pci/pci.c | |
parent | 10c480933d0ad2ea27630cbaa723a5d33dbece00 (diff) | |
parent | a0dee2ed0cdc666b5622f1fc74979355a6b36850 (diff) | |
download | blackbird-op-linux-cc2fa3fa320d5f40a12713c104bbe5d3da4636e4.tar.gz blackbird-op-linux-cc2fa3fa320d5f40a12713c104bbe5d3da4636e4.zip |
Merge branch 'topic/alex-vfio-prep' into next
* topic/alex-vfio-prep:
PCI: misc pci_reg additions
PCI: create common pcibios_err_to_errno
PCI: export pci_user functions for use by other drivers
PCI: add ACS validation utility
PCI: add PCI DMA source ID quirk
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a23b071798f6..b743a9afb4dd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2293,6 +2293,75 @@ void pci_enable_acs(struct pci_dev *dev) } /** + * 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 * @dev: the PCI device * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD) |