Merge branch 'pci/virtualization' into next

* pci/virtualization:
  PCI: Enable quirks for PCIe ACS on Intel PCH root ports
  PCI: Add pci_dev_flag for ACS enable quirks
  PCI: Add device-specific PCI ACS enable
This commit is contained in:
Bjorn Helgaas 2014-02-14 14:37:18 -07:00
commit c80ef97ae4
3 changed files with 209 additions and 6 deletions

View File

@ -2180,21 +2180,18 @@ void pci_request_acs(void)
}
/**
* pci_enable_acs - enable ACS if hardware support it
* pci_std_enable_acs - enable ACS on devices using standard ACS capabilites
* @dev: the PCI device
*/
void pci_enable_acs(struct pci_dev *dev)
static int pci_std_enable_acs(struct pci_dev *dev)
{
int pos;
u16 cap;
u16 ctrl;
if (!pci_acs_enable)
return;
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
if (!pos)
return;
return -ENODEV;
pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
@ -2212,6 +2209,23 @@ void pci_enable_acs(struct pci_dev *dev)
ctrl |= (cap & PCI_ACS_UF);
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
return 0;
}
/**
* pci_enable_acs - enable ACS if hardware support it
* @dev: the PCI device
*/
void pci_enable_acs(struct pci_dev *dev)
{
if (!pci_acs_enable)
return;
if (!pci_std_enable_acs(dev))
return;
pci_dev_specific_enable_acs(dev);
}
static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)

View File

@ -3423,6 +3423,61 @@ static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags)
#endif
}
/*
* Many Intel PCH root ports do provide ACS-like features to disable peer
* transactions and validate bus numbers in requests, but do not provide an
* actual PCIe ACS capability. This is the list of device IDs known to fall
* into that category as provided by Intel in Red Hat bugzilla 1037684.
*/
static const u16 pci_quirk_intel_pch_acs_ids[] = {
/* Ibexpeak PCH */
0x3b42, 0x3b43, 0x3b44, 0x3b45, 0x3b46, 0x3b47, 0x3b48, 0x3b49,
0x3b4a, 0x3b4b, 0x3b4c, 0x3b4d, 0x3b4e, 0x3b4f, 0x3b50, 0x3b51,
/* Cougarpoint PCH */
0x1c10, 0x1c11, 0x1c12, 0x1c13, 0x1c14, 0x1c15, 0x1c16, 0x1c17,
0x1c18, 0x1c19, 0x1c1a, 0x1c1b, 0x1c1c, 0x1c1d, 0x1c1e, 0x1c1f,
/* Pantherpoint PCH */
0x1e10, 0x1e11, 0x1e12, 0x1e13, 0x1e14, 0x1e15, 0x1e16, 0x1e17,
0x1e18, 0x1e19, 0x1e1a, 0x1e1b, 0x1e1c, 0x1e1d, 0x1e1e, 0x1e1f,
/* Lynxpoint-H PCH */
0x8c10, 0x8c11, 0x8c12, 0x8c13, 0x8c14, 0x8c15, 0x8c16, 0x8c17,
0x8c18, 0x8c19, 0x8c1a, 0x8c1b, 0x8c1c, 0x8c1d, 0x8c1e, 0x8c1f,
/* Lynxpoint-LP PCH */
0x9c10, 0x9c11, 0x9c12, 0x9c13, 0x9c14, 0x9c15, 0x9c16, 0x9c17,
0x9c18, 0x9c19, 0x9c1a, 0x9c1b,
/* Wildcat PCH */
0x9c90, 0x9c91, 0x9c92, 0x9c93, 0x9c94, 0x9c95, 0x9c96, 0x9c97,
0x9c98, 0x9c99, 0x9c9a, 0x9c9b,
};
static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev)
{
int i;
/* Filter out a few obvious non-matches first */
if (!pci_is_pcie(dev) || pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT)
return false;
for (i = 0; i < ARRAY_SIZE(pci_quirk_intel_pch_acs_ids); i++)
if (pci_quirk_intel_pch_acs_ids[i] == dev->device)
return true;
return false;
}
#define INTEL_PCH_ACS_FLAGS (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV)
static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
{
u16 flags = dev->dev_flags & PCI_DEV_FLAGS_ACS_ENABLED_QUIRK ?
INTEL_PCH_ACS_FLAGS : 0;
if (!pci_quirk_intel_pch_acs_match(dev))
return -ENOTTY;
return acs_flags & ~flags ? 0 : 1;
}
static const struct pci_dev_acs_enabled {
u16 vendor;
u16 device;
@ -3434,6 +3489,7 @@ static const struct pci_dev_acs_enabled {
{ 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 },
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
{ 0 }
};
@ -3461,3 +3517,132 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags)
return -ENOTTY;
}
/* Config space offset of Root Complex Base Address register */
#define INTEL_LPC_RCBA_REG 0xf0
/* 31:14 RCBA address */
#define INTEL_LPC_RCBA_MASK 0xffffc000
/* RCBA Enable */
#define INTEL_LPC_RCBA_ENABLE (1 << 0)
/* Backbone Scratch Pad Register */
#define INTEL_BSPR_REG 0x1104
/* Backbone Peer Non-Posted Disable */
#define INTEL_BSPR_REG_BPNPD (1 << 8)
/* Backbone Peer Posted Disable */
#define INTEL_BSPR_REG_BPPD (1 << 9)
/* Upstream Peer Decode Configuration Register */
#define INTEL_UPDCR_REG 0x1114
/* 5:0 Peer Decode Enable bits */
#define INTEL_UPDCR_REG_MASK 0x3f
static int pci_quirk_enable_intel_lpc_acs(struct pci_dev *dev)
{
u32 rcba, bspr, updcr;
void __iomem *rcba_mem;
/*
* Read the RCBA register from the LPC (D31:F0). PCH root ports
* are D28:F* and therefore get probed before LPC, thus we can't
* use pci_get_slot/pci_read_config_dword here.
*/
pci_bus_read_config_dword(dev->bus, PCI_DEVFN(31, 0),
INTEL_LPC_RCBA_REG, &rcba);
if (!(rcba & INTEL_LPC_RCBA_ENABLE))
return -EINVAL;
rcba_mem = ioremap_nocache(rcba & INTEL_LPC_RCBA_MASK,
PAGE_ALIGN(INTEL_UPDCR_REG));
if (!rcba_mem)
return -ENOMEM;
/*
* The BSPR can disallow peer cycles, but it's set by soft strap and
* therefore read-only. If both posted and non-posted peer cycles are
* disallowed, we're ok. If either are allowed, then we need to use
* the UPDCR to disable peer decodes for each port. This provides the
* PCIe ACS equivalent of PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF
*/
bspr = readl(rcba_mem + INTEL_BSPR_REG);
bspr &= INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD;
if (bspr != (INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD)) {
updcr = readl(rcba_mem + INTEL_UPDCR_REG);
if (updcr & INTEL_UPDCR_REG_MASK) {
dev_info(&dev->dev, "Disabling UPDCR peer decodes\n");
updcr &= ~INTEL_UPDCR_REG_MASK;
writel(updcr, rcba_mem + INTEL_UPDCR_REG);
}
}
iounmap(rcba_mem);
return 0;
}
/* Miscellaneous Port Configuration register */
#define INTEL_MPC_REG 0xd8
/* MPC: Invalid Receive Bus Number Check Enable */
#define INTEL_MPC_REG_IRBNCE (1 << 26)
static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)
{
u32 mpc;
/*
* When enabled, the IRBNCE bit of the MPC register enables the
* equivalent of PCI ACS Source Validation (PCI_ACS_SV), which
* ensures that requester IDs fall within the bus number range
* of the bridge. Enable if not already.
*/
pci_read_config_dword(dev, INTEL_MPC_REG, &mpc);
if (!(mpc & INTEL_MPC_REG_IRBNCE)) {
dev_info(&dev->dev, "Enabling MPC IRBNCE\n");
mpc |= INTEL_MPC_REG_IRBNCE;
pci_write_config_word(dev, INTEL_MPC_REG, mpc);
}
}
static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev)
{
if (!pci_quirk_intel_pch_acs_match(dev))
return -ENOTTY;
if (pci_quirk_enable_intel_lpc_acs(dev)) {
dev_warn(&dev->dev, "Failed to enable Intel PCH ACS quirk\n");
return 0;
}
pci_quirk_enable_intel_rp_mpc_acs(dev);
dev->dev_flags |= PCI_DEV_FLAGS_ACS_ENABLED_QUIRK;
dev_info(&dev->dev, "Intel PCH root port ACS workaround enabled\n");
return 0;
}
static const struct pci_dev_enable_acs {
u16 vendor;
u16 device;
int (*enable_acs)(struct pci_dev *dev);
} pci_dev_enable_acs[] = {
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs },
{ 0 }
};
void pci_dev_specific_enable_acs(struct pci_dev *dev)
{
const struct pci_dev_enable_acs *i;
int ret;
for (i = pci_dev_enable_acs; i->enable_acs; i++) {
if ((i->vendor == dev->vendor ||
i->vendor == (u16)PCI_ANY_ID) &&
(i->device == dev->device ||
i->device == (u16)PCI_ANY_ID)) {
ret = i->enable_acs(dev);
if (ret >= 0)
return;
}
}
}

View File

@ -170,6 +170,8 @@ enum pci_dev_flags {
PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2,
/* Provide indication device is assigned by a Virtual Machine Manager */
PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4,
/* Flag for quirk use to store if quirk-specific ACS is enabled */
PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) 8,
};
enum pci_irq_reroute_variant {
@ -1510,6 +1512,7 @@ enum pci_fixup_pass {
void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
struct pci_dev *pci_get_dma_source(struct pci_dev *dev);
int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
void pci_dev_specific_enable_acs(struct pci_dev *dev);
#else
static inline void pci_fixup_device(enum pci_fixup_pass pass,
struct pci_dev *dev) { }
@ -1522,6 +1525,7 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
{
return -ENOTTY;
}
static inline void pci_dev_specific_enable_acs(struct pci_dev *dev) { }
#endif
void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen);