Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0eeb4f2a authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/iommu' into next

* pci/iommu:
  PCI: Add bridge DMA alias quirk for ASMedia and Tundra bridges
  PCI: Add support for PCIe-to-PCI bridge DMA alias quirks
  PCI: Add function 1 DMA alias quirk for Marvell devices
  PCI: Add function 0 DMA alias quirk for Ricoh devices
  PCI: Add support for DMA alias quirks
  PCI: Convert pci_dev_flags definitions to bit shifts
  PCI: Add DMA alias iterator
parents 617b4157 ebdb51eb
Loading
Loading
Loading
Loading
+75 −0
Original line number Diff line number Diff line
@@ -3342,6 +3342,81 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe)
	return -ENOTTY;
}

static void quirk_dma_func0_alias(struct pci_dev *dev)
{
	if (PCI_FUNC(dev->devfn) != 0) {
		dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0);
		dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
	}
}

/*
 * https://bugzilla.redhat.com/show_bug.cgi?id=605888
 *
 * Some Ricoh devices use function 0 as the PCIe requester ID for DMA.
 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe832, quirk_dma_func0_alias);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias);

static void quirk_dma_func1_alias(struct pci_dev *dev)
{
	if (PCI_FUNC(dev->devfn) != 1) {
		dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 1);
		dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
	}
}

/*
 * Marvell 88SE9123 uses function 1 as the requester ID for DMA.  In some
 * SKUs function 1 is present and is a legacy IDE controller, in other
 * SKUs this function is not present, making this a ghost requester.
 * https://bugzilla.kernel.org/show_bug.cgi?id=42679
 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123,
			 quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9130,
			 quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c47 + c57 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9172,
			 quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c59 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x917a,
			 quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0,
			 quirk_dma_func1_alias);
/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230,
			 quirk_dma_func1_alias);
/* https://bugs.gentoo.org/show_bug.cgi?id=497630 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JMICRON,
			 PCI_DEVICE_ID_JMICRON_JMB388_ESD,
			 quirk_dma_func1_alias);

/*
 * A few PCIe-to-PCI bridges fail to expose a PCIe capability, resulting in
 * using the wrong DMA alias for the device.  Some of these devices can be
 * used as either forward or reverse bridges, so we need to test whether the
 * device is operating in the correct mode.  We could probably apply this
 * quirk to PCI_ANY_ID, but for now we'll just use known offenders.  The test
 * is for a non-root, non-PCIe bridge where the upstream device is PCIe and
 * is not a PCIe-to-PCI bridge, then @pdev is actually a PCIe-to-PCI bridge.
 */
static void quirk_use_pcie_bridge_dma_alias(struct pci_dev *pdev)
{
	if (!pci_is_root_bus(pdev->bus) &&
	    pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
	    !pci_is_pcie(pdev) && pci_is_pcie(pdev->bus->self) &&
	    pci_pcie_type(pdev->bus->self) != PCI_EXP_TYPE_PCI_BRIDGE)
		pdev->dev_flags |= PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS;
}
/* ASM1083/1085, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c46 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASMEDIA, 0x1080,
			 quirk_use_pcie_bridge_dma_alias);
/* Tundra 8113, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c43 */
DECLARE_PCI_FIXUP_HEADER(0x10e3, 0x8113, quirk_use_pcie_bridge_dma_alias);

static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev)
{
	if (!PCI_FUNC(dev->devfn))
+87 −0
Original line number Diff line number Diff line
@@ -16,6 +16,93 @@
DECLARE_RWSEM(pci_bus_sem);
EXPORT_SYMBOL_GPL(pci_bus_sem);

/*
 * pci_for_each_dma_alias - Iterate over DMA aliases for a device
 * @pdev: starting downstream device
 * @fn: function to call for each alias
 * @data: opaque data to pass to @fn
 *
 * Starting @pdev, walk up the bus calling @fn for each possible alias
 * of @pdev at the root bus.
 */
int pci_for_each_dma_alias(struct pci_dev *pdev,
			   int (*fn)(struct pci_dev *pdev,
				     u16 alias, void *data), void *data)
{
	struct pci_bus *bus;
	int ret;

	ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data);
	if (ret)
		return ret;

	/*
	 * If the device is broken and uses an alias requester ID for
	 * DMA, iterate over that too.
	 */
	if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) {
		ret = fn(pdev, PCI_DEVID(pdev->bus->number,
					 pdev->dma_alias_devfn), data);
		if (ret)
			return ret;
	}

	for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
		struct pci_dev *tmp;

		/* Skip virtual buses */
		if (!bus->self)
			continue;

		tmp = bus->self;

		/*
		 * PCIe-to-PCI/X bridges alias transactions from downstream
		 * devices using the subordinate bus number (PCI Express to
		 * PCI/PCI-X Bridge Spec, rev 1.0, sec 2.3).  For all cases
		 * where the upstream bus is PCI/X we alias to the bridge
		 * (there are various conditions in the previous reference
		 * where the bridge may take ownership of transactions, even
		 * when the secondary interface is PCI-X).
		 */
		if (pci_is_pcie(tmp)) {
			switch (pci_pcie_type(tmp)) {
			case PCI_EXP_TYPE_ROOT_PORT:
			case PCI_EXP_TYPE_UPSTREAM:
			case PCI_EXP_TYPE_DOWNSTREAM:
				continue;
			case PCI_EXP_TYPE_PCI_BRIDGE:
				ret = fn(tmp,
					 PCI_DEVID(tmp->subordinate->number,
						   PCI_DEVFN(0, 0)), data);
				if (ret)
					return ret;
				continue;
			case PCI_EXP_TYPE_PCIE_BRIDGE:
				ret = fn(tmp,
					 PCI_DEVID(tmp->bus->number,
						   tmp->devfn), data);
				if (ret)
					return ret;
				continue;
			}
		} else {
			if (tmp->dev_flags & PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS)
				ret = fn(tmp,
					 PCI_DEVID(tmp->subordinate->number,
						   PCI_DEVFN(0, 0)), data);
			else
				ret = fn(tmp,
					 PCI_DEVID(tmp->bus->number,
						   tmp->devfn), data);
			if (ret)
				return ret;
		}
	}

	return ret;
}

/*
 * find the upstream PCIe-to-PCI bridge of a PCI device
 * if the device is PCIE, return NULL
+13 −4
Original line number Diff line number Diff line
@@ -164,13 +164,17 @@ enum pci_dev_flags {
	/* INTX_DISABLE in PCI_COMMAND register disables MSI
	 * generation too.
	 */
	PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1,
	PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) (1 << 0),
	/* Device configuration is irrevocably lost if disabled into D3 */
	PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2,
	PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) (1 << 1),
	/* Provide indication device is assigned by a Virtual Machine Manager */
	PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4,
	PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2),
	/* Flag for quirk use to store if quirk-specific ACS is enabled */
	PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) 8,
	PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3),
	/* Flag to indicate the device uses dma_alias_devfn */
	PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4),
	/* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */
	PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5),
};

enum pci_irq_reroute_variant {
@@ -268,6 +272,7 @@ struct pci_dev {
	u8		rom_base_reg;	/* which config register controls the ROM */
	u8		pin;		/* which interrupt pin this device uses */
	u16		pcie_flags_reg;	/* cached PCIe Capabilities Register */
	u8		dma_alias_devfn;/* devfn of DMA alias, if any */

	struct pci_driver *driver;	/* which driver has allocated this device */
	u64		dma_mask;	/* Mask of the bits of bus address this
@@ -1809,6 +1814,10 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
}
#endif

int pci_for_each_dma_alias(struct pci_dev *pdev,
			   int (*fn)(struct pci_dev *pdev,
				     u16 alias, void *data), void *data);

/**
 * pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device
 * @pdev: the PCI device