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:
commit
28fa60a830
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue