Loading drivers/pci/pci.c +20 −6 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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) Loading drivers/pci/quirks.c +185 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 } }; Loading Loading @@ -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; } } } include/linux/pci.h +4 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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) { } Loading @@ -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); Loading Loading
drivers/pci/pci.c +20 −6 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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) Loading
drivers/pci/quirks.c +185 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 } }; Loading Loading @@ -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; } } }
include/linux/pci.h +4 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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) { } Loading @@ -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); Loading