]> Pileus Git - ~andy/linux/blobdiff - drivers/pci/quirks.c
Merge branches 'iommu/fixes', 'x86/amd', 'groups', 'arm/tegra' and 'api/domain-attr...
[~andy/linux] / drivers / pci / quirks.c
index 2a75216775410df88e19fd9faa21e0045c16d15d..27e2c8f4ec7304de8bfa9aae257555a6cfe70999 100644 (file)
@@ -3179,3 +3179,87 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe)
 
        return -ENOTTY;
 }
+
+static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev)
+{
+       if (!PCI_FUNC(dev->devfn))
+               return pci_dev_get(dev);
+
+       return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
+}
+
+static const struct pci_dev_dma_source {
+       u16 vendor;
+       u16 device;
+       struct pci_dev *(*dma_source)(struct pci_dev *dev);
+} pci_dev_dma_source[] = {
+       /*
+        * https://bugzilla.redhat.com/show_bug.cgi?id=605888
+        *
+        * Some Ricoh devices use the function 0 source ID for DMA on
+        * other functions of a multifunction device.  The DMA devices
+        * is therefore function 0, which will have implications of the
+        * iommu grouping of these devices.
+        */
+       { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source },
+       { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source },
+       { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source },
+       { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source },
+       { 0 }
+};
+
+/*
+ * IOMMUs with isolation capabilities need to be programmed with the
+ * correct source ID of a device.  In most cases, the source ID matches
+ * the device doing the DMA, but sometimes hardware is broken and will
+ * tag the DMA as being sourced from a different device.  This function
+ * allows that translation.  Note that the reference count of the
+ * returned device is incremented on all paths.
+ */
+struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
+{
+       const struct pci_dev_dma_source *i;
+
+       for (i = pci_dev_dma_source; i->dma_source; i++) {
+               if ((i->vendor == dev->vendor ||
+                    i->vendor == (u16)PCI_ANY_ID) &&
+                   (i->device == dev->device ||
+                    i->device == (u16)PCI_ANY_ID))
+                       return i->dma_source(dev);
+       }
+
+       return pci_dev_get(dev);
+}
+
+static const struct pci_dev_acs_enabled {
+       u16 vendor;
+       u16 device;
+       int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags);
+} pci_dev_acs_enabled[] = {
+       { 0 }
+};
+
+int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
+{
+       const struct pci_dev_acs_enabled *i;
+       int ret;
+
+       /*
+        * Allow devices that do not expose standard PCIe ACS capabilities
+        * or control to indicate their support here.  Multi-function express
+        * devices which do not allow internal peer-to-peer between functions,
+        * but do not implement PCIe ACS may wish to return true here.
+        */
+       for (i = pci_dev_acs_enabled; i->acs_enabled; i++) {
+               if ((i->vendor == dev->vendor ||
+                    i->vendor == (u16)PCI_ANY_ID) &&
+                   (i->device == dev->device ||
+                    i->device == (u16)PCI_ANY_ID)) {
+                       ret = i->acs_enabled(dev, acs_flags);
+                       if (ret >= 0)
+                               return ret;
+               }
+       }
+
+       return -ENOTTY;
+}