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

Commit 28fa60a8 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/aw-acs-fixes-v2' into next

* pci/aw-acs-fixes-v2:
  PCI: Claim ACS support for AMD southbridge devices
  PCI: Differentiate ACS controllable from enabled
  PCI: Check all ACS features for multifunction downstream ports
parents 3b2f64d0 15b100df
Loading
Loading
Loading
Loading
+77 −16
Original line number Diff line number Diff line
@@ -2359,6 +2359,27 @@ void pci_enable_acs(struct pci_dev *dev)
	pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
}

static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
{
	int pos;
	u16 cap, ctrl;

	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
	if (!pos)
		return false;

	/*
	 * Except for egress control, capabilities are either required
	 * or only required if controllable.  Features missing from the
	 * capability field can therefore be assumed as hard-wired enabled.
	 */
	pci_read_config_word(pdev, pos + PCI_ACS_CAP, &cap);
	acs_flags &= (cap | PCI_ACS_EC);

	pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
	return (ctrl & acs_flags) == acs_flags;
}

/**
 * pci_acs_enabled - test ACS against required flags for a given device
 * @pdev: device to test
@@ -2366,36 +2387,76 @@ void pci_enable_acs(struct pci_dev *dev)
 *
 * Return true if the device supports the provided flags.  Automatically
 * filters out flags that are not implemented on multifunction devices.
 *
 * Note that this interface checks the effective ACS capabilities of the
 * device rather than the actual capabilities.  For instance, most single
 * function endpoints are not required to support ACS because they have no
 * opportunity for peer-to-peer access.  We therefore return 'true'
 * regardless of whether the device exposes an ACS capability.  This makes
 * it much easier for callers of this function to ignore the actual type
 * or topology of the device when testing ACS support.
 */
bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
{
	int pos, ret;
	u16 ctrl;
	int ret;

	ret = pci_dev_specific_acs_enabled(pdev, acs_flags);
	if (ret >= 0)
		return ret > 0;

	/*
	 * Conventional PCI and PCI-X devices never support ACS, either
	 * effectively or actually.  The shared bus topology implies that
	 * any device on the bus can receive or snoop DMA.
	 */
	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 (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM ||
	    pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
	    pdev->multifunction) {
		pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
		if (!pos)
	switch (pci_pcie_type(pdev)) {
	/*
	 * PCI/X-to-PCIe bridges are not specifically mentioned by the spec,
	 * but since their primary inteface is PCI/X, we conservatively
	 * handle them as we would a non-PCIe device.
	 */
	case PCI_EXP_TYPE_PCIE_BRIDGE:
	/*
	 * PCIe 3.0, 6.12.1 excludes ACS on these devices.  "ACS is never
	 * applicable... must never implement an ACS Extended Capability...".
	 * This seems arbitrary, but we take a conservative interpretation
	 * of this statement.
	 */
	case PCI_EXP_TYPE_PCI_BRIDGE:
	case PCI_EXP_TYPE_RC_EC:
		return false;
	/*
	 * PCIe 3.0, 6.12.1.1 specifies that downstream and root ports should
	 * implement ACS in order to indicate their peer-to-peer capabilities,
	 * regardless of whether they are single- or multi-function devices.
	 */
	case PCI_EXP_TYPE_DOWNSTREAM:
	case PCI_EXP_TYPE_ROOT_PORT:
		return pci_acs_flags_enabled(pdev, acs_flags);
	/*
	 * PCIe 3.0, 6.12.1.2 specifies ACS capabilities that should be
	 * implemented by the remaining PCIe types to indicate peer-to-peer
	 * capabilities, but only when they are part of a multifunciton
	 * device.  The footnote for section 6.12 indicates the specific
	 * PCIe types included here.
	 */
	case PCI_EXP_TYPE_ENDPOINT:
	case PCI_EXP_TYPE_UPSTREAM:
	case PCI_EXP_TYPE_LEG_END:
	case PCI_EXP_TYPE_RC_END:
		if (!pdev->multifunction)
			break;

		pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
		if ((ctrl & acs_flags) != acs_flags)
			return false;
		return pci_acs_flags_enabled(pdev, acs_flags);
	}

	/*
	 * PCIe 3.0, 6.12.1.3 specifies no ACS capabilties are applicable
	 * to single function devices with the exception of downstream ports.
	 */
	return true;
}

+50 −0
Original line number Diff line number Diff line
@@ -3295,11 +3295,61 @@ struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
	return pci_dev_get(dev);
}

/*
 * AMD has indicated that the devices below do not support peer-to-peer
 * in any system where they are found in the southbridge with an AMD
 * IOMMU in the system.  Multifunction devices that do not support
 * peer-to-peer between functions can claim to support a subset of ACS.
 * Such devices effectively enable request redirect (RR) and completion
 * redirect (CR) since all transactions are redirected to the upstream
 * root complex.
 *
 * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/94086
 * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/94102
 * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/99402
 *
 * 1002:4385 SBx00 SMBus Controller
 * 1002:439c SB7x0/SB8x0/SB9x0 IDE Controller
 * 1002:4383 SBx00 Azalia (Intel HDA)
 * 1002:439d SB7x0/SB8x0/SB9x0 LPC host controller
 * 1002:4384 SBx00 PCI to PCI Bridge
 * 1002:4399 SB7x0/SB8x0/SB9x0 USB OHCI2 Controller
 */
static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags)
{
#ifdef CONFIG_ACPI
	struct acpi_table_header *header = NULL;
	acpi_status status;

	/* Targeting multifunction devices on the SB (appears on root bus) */
	if (!dev->multifunction || !pci_is_root_bus(dev->bus))
		return -ENODEV;

	/* The IVRS table describes the AMD IOMMU */
	status = acpi_get_table("IVRS", 0, &header);
	if (ACPI_FAILURE(status))
		return -ENODEV;

	/* Filter out flags not applicable to multifunction */
	acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC | PCI_ACS_DT);

	return acs_flags & ~(PCI_ACS_RR | PCI_ACS_CR) ? 0 : 1;
#else
	return -ENODEV;
#endif
}

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[] = {
	{ PCI_VENDOR_ID_ATI, 0x4385, pci_quirk_amd_sb_acs },
	{ PCI_VENDOR_ID_ATI, 0x439c, pci_quirk_amd_sb_acs },
	{ PCI_VENDOR_ID_ATI, 0x4383, pci_quirk_amd_sb_acs },
	{ PCI_VENDOR_ID_ATI, 0x439d, pci_quirk_amd_sb_acs },
	{ PCI_VENDOR_ID_ATI, 0x4384, pci_quirk_amd_sb_acs },
	{ PCI_VENDOR_ID_ATI, 0x4399, pci_quirk_amd_sb_acs },
	{ 0 }
};