diff options
Diffstat (limited to 'drivers/pci/quirks.c')
| -rw-r--r-- | drivers/pci/quirks.c | 51 | 
1 files changed, 51 insertions, 0 deletions
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a752167754..acd3956b44b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3179,3 +3179,54 @@ 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); +}  |