PCI: Add device-specific PCI ACS enable
authorAlex Williamson <alex.williamson@redhat.com>
Mon, 3 Feb 2014 21:27:33 +0000 (14:27 -0700)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 11 Feb 2014 20:49:56 +0000 (13:49 -0700)
Some devices support PCI ACS-like features, but don't report it using the
standard PCIe capabilities.  We already provide hooks for device-specific
testing of ACS, but not for device-specific enabling of ACS.  This provides
that setup hook.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/pci.c
drivers/pci/quirks.c
include/linux/pci.h

index 1febe90831b442303b7414faec770d4850ca53ed..b89502ff3139f83430e68c1825dc6787ff861358 100644 (file)
@@ -2180,21 +2180,18 @@ void pci_request_acs(void)
 }
 
 /**
- * pci_enable_acs - enable ACS if hardware support it
+ * pci_std_enable_acs - enable ACS on devices using standard ACS capabilites
  * @dev: the PCI device
  */
-void pci_enable_acs(struct pci_dev *dev)
+static int pci_std_enable_acs(struct pci_dev *dev)
 {
        int pos;
        u16 cap;
        u16 ctrl;
 
-       if (!pci_acs_enable)
-               return;
-
        pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
        if (!pos)
-               return;
+               return -ENODEV;
 
        pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
        pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
@@ -2212,6 +2209,23 @@ void pci_enable_acs(struct pci_dev *dev)
        ctrl |= (cap & PCI_ACS_UF);
 
        pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
+
+       return 0;
+}
+
+/**
+ * pci_enable_acs - enable ACS if hardware support it
+ * @dev: the PCI device
+ */
+void pci_enable_acs(struct pci_dev *dev)
+{
+       if (!pci_acs_enable)
+               return;
+
+       if (!pci_std_enable_acs(dev))
+               return;
+
+       pci_dev_specific_enable_acs(dev);
 }
 
 static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
index 5cb726c193de824db34ece33610cc6b3bec86eaa..f681fb08f5e4afc51ba9769bc02b2f6f28b89689 100644 (file)
@@ -3461,3 +3461,28 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
 
        return -ENOTTY;
 }
+
+static const struct pci_dev_enable_acs {
+       u16 vendor;
+       u16 device;
+       int (*enable_acs)(struct pci_dev *dev);
+} pci_dev_enable_acs[] = {
+       { 0 }
+};
+
+void pci_dev_specific_enable_acs(struct pci_dev *dev)
+{
+       const struct pci_dev_enable_acs *i;
+       int ret;
+
+       for (i = pci_dev_enable_acs; i->enable_acs; i++) {
+               if ((i->vendor == dev->vendor ||
+                    i->vendor == (u16)PCI_ANY_ID) &&
+                   (i->device == dev->device ||
+                    i->device == (u16)PCI_ANY_ID)) {
+                       ret = i->enable_acs(dev);
+                       if (ret >= 0)
+                               return;
+               }
+       }
+}
index fb57c892b214d6b3482c63a28061130f3a700a9c..0f76d0f66365c8ab5b0a0eddd4f0b8453e440fa2 100644 (file)
@@ -1510,6 +1510,7 @@ enum pci_fixup_pass {
 void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
 struct pci_dev *pci_get_dma_source(struct pci_dev *dev);
 int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
+void pci_dev_specific_enable_acs(struct pci_dev *dev);
 #else
 static inline void pci_fixup_device(enum pci_fixup_pass pass,
                                    struct pci_dev *dev) { }
@@ -1522,6 +1523,7 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
 {
        return -ENOTTY;
 }
+static inline void pci_dev_specific_enable_acs(struct pci_dev *dev) { }
 #endif
 
 void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen);