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
This commit is contained in:
Bjorn Helgaas 2013-08-01 11:03:00 -06:00
commit 28fa60a830
2 changed files with 127 additions and 16 deletions

View File

@ -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);
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;
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)
return false;
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;
}

View File

@ -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 }
};