Merge branch 'remotes/lorenzo/pci/iproc'

- Add more devices to Broadcom PAXC quirk (Ray Jui)

  - Work around corrupted Broadcom PAXC config space to enable SMMU and
    GICv3 ITS (Ray Jui)

  - Disable MSI parsing to work around broken Broadcom PAXC logic in some
    devices (Ray Jui)

  - Hide unconfigured functions to work around a Broadcom PAXC defect (Ray
    Jui)

  - Lower iproc log level to reduce console output during boot (Ray Jui)

* remotes/lorenzo/pci/iproc:
  PCI: iproc: Reduce inbound/outbound mapping print level
  PCI: iproc: Reject unconfigured physical functions from PAXC
  PCI: iproc: Disable MSI parsing in certain PAXC blocks
  PCI: iproc: Fix up corrupted PAXC root complex config registers
  PCI: iproc: Activate PAXC bridge quirk for more devices
This commit is contained in:
Bjorn Helgaas 2018-08-15 14:59:13 -05:00
commit 0ea77d2b20
3 changed files with 144 additions and 26 deletions

View File

@ -85,6 +85,8 @@
#define IMAP_VALID_SHIFT 0 #define IMAP_VALID_SHIFT 0
#define IMAP_VALID BIT(IMAP_VALID_SHIFT) #define IMAP_VALID BIT(IMAP_VALID_SHIFT)
#define IPROC_PCI_PM_CAP 0x48
#define IPROC_PCI_PM_CAP_MASK 0xffff
#define IPROC_PCI_EXP_CAP 0xac #define IPROC_PCI_EXP_CAP 0xac
#define IPROC_PCIE_REG_INVALID 0xffff #define IPROC_PCIE_REG_INVALID 0xffff
@ -375,6 +377,17 @@ static const u16 iproc_pcie_reg_paxc_v2[] = {
[IPROC_PCIE_CFG_DATA] = 0x1fc, [IPROC_PCIE_CFG_DATA] = 0x1fc,
}; };
/*
* List of device IDs of controllers that have corrupted capability list that
* require SW fixup
*/
static const u16 iproc_pcie_corrupt_cap_did[] = {
0x16cd,
0x16f0,
0xd802,
0xd804
};
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus) static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
{ {
struct iproc_pcie *pcie = bus->sysdata; struct iproc_pcie *pcie = bus->sysdata;
@ -495,6 +508,49 @@ static unsigned int iproc_pcie_cfg_retry(void __iomem *cfg_data_p)
return data; return data;
} }
static void iproc_pcie_fix_cap(struct iproc_pcie *pcie, int where, u32 *val)
{
u32 i, dev_id;
switch (where & ~0x3) {
case PCI_VENDOR_ID:
dev_id = *val >> 16;
/*
* Activate fixup for those controllers that have corrupted
* capability list registers
*/
for (i = 0; i < ARRAY_SIZE(iproc_pcie_corrupt_cap_did); i++)
if (dev_id == iproc_pcie_corrupt_cap_did[i])
pcie->fix_paxc_cap = true;
break;
case IPROC_PCI_PM_CAP:
if (pcie->fix_paxc_cap) {
/* advertise PM, force next capability to PCIe */
*val &= ~IPROC_PCI_PM_CAP_MASK;
*val |= IPROC_PCI_EXP_CAP << 8 | PCI_CAP_ID_PM;
}
break;
case IPROC_PCI_EXP_CAP:
if (pcie->fix_paxc_cap) {
/* advertise root port, version 2, terminate here */
*val = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2) << 16 |
PCI_CAP_ID_EXP;
}
break;
case IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL:
/* Don't advertise CRS SV support */
*val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
break;
default:
break;
}
}
static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn, static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val) int where, int size, u32 *val)
{ {
@ -509,13 +565,10 @@ static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
/* root complex access */ /* root complex access */
if (busno == 0) { if (busno == 0) {
ret = pci_generic_config_read32(bus, devfn, where, size, val); ret = pci_generic_config_read32(bus, devfn, where, size, val);
if (ret != PCIBIOS_SUCCESSFUL) if (ret == PCIBIOS_SUCCESSFUL)
return ret; iproc_pcie_fix_cap(pcie, where, val);
/* Don't advertise CRS SV support */ return ret;
if ((where & ~0x3) == IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL)
*val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
return PCIBIOS_SUCCESSFUL;
} }
cfg_data_p = iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where); cfg_data_p = iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where);
@ -529,6 +582,25 @@ static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
if (size <= 2) if (size <= 2)
*val = (data >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); *val = (data >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
/*
* For PAXC and PAXCv2, the total number of PFs that one can enumerate
* depends on the firmware configuration. Unfortunately, due to an ASIC
* bug, unconfigured PFs cannot be properly hidden from the root
* complex. As a result, write access to these PFs will cause bus lock
* up on the embedded processor
*
* Since all unconfigured PFs are left with an incorrect, staled device
* ID of 0x168e (PCI_DEVICE_ID_NX2_57810), we try to catch those access
* early here and reject them all
*/
#define DEVICE_ID_MASK 0xffff0000
#define DEVICE_ID_SHIFT 16
if (pcie->rej_unconfig_pf &&
(where & CFG_ADDR_REG_NUM_MASK) == PCI_VENDOR_ID)
if ((*val & DEVICE_ID_MASK) ==
(PCI_DEVICE_ID_NX2_57810 << DEVICE_ID_SHIFT))
return PCIBIOS_FUNC_NOT_SUPPORTED;
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
} }
@ -628,7 +700,7 @@ static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
struct iproc_pcie *pcie = iproc_data(bus); struct iproc_pcie *pcie = iproc_data(bus);
iproc_pcie_apb_err_disable(bus, true); iproc_pcie_apb_err_disable(bus, true);
if (pcie->type == IPROC_PCIE_PAXB_V2) if (pcie->iproc_cfg_read)
ret = iproc_pcie_config_read(bus, devfn, where, size, val); ret = iproc_pcie_config_read(bus, devfn, where, size, val);
else else
ret = pci_generic_config_read32(bus, devfn, where, size, val); ret = pci_generic_config_read32(bus, devfn, where, size, val);
@ -808,14 +880,14 @@ static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx,
writel(lower_32_bits(pci_addr), pcie->base + omap_offset); writel(lower_32_bits(pci_addr), pcie->base + omap_offset);
writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4); writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4);
dev_info(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n", dev_dbg(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n",
window_idx, oarr_offset, &axi_addr, &pci_addr); window_idx, oarr_offset, &axi_addr, &pci_addr);
dev_info(dev, "oarr lo 0x%x oarr hi 0x%x\n", dev_dbg(dev, "oarr lo 0x%x oarr hi 0x%x\n",
readl(pcie->base + oarr_offset), readl(pcie->base + oarr_offset),
readl(pcie->base + oarr_offset + 4)); readl(pcie->base + oarr_offset + 4));
dev_info(dev, "omap lo 0x%x omap hi 0x%x\n", dev_dbg(dev, "omap lo 0x%x omap hi 0x%x\n",
readl(pcie->base + omap_offset), readl(pcie->base + omap_offset),
readl(pcie->base + omap_offset + 4)); readl(pcie->base + omap_offset + 4));
return 0; return 0;
} }
@ -982,8 +1054,8 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
iproc_pcie_reg_is_invalid(imap_offset)) iproc_pcie_reg_is_invalid(imap_offset))
return -EINVAL; return -EINVAL;
dev_info(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n", dev_dbg(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n",
region_idx, iarr_offset, &axi_addr, &pci_addr); region_idx, iarr_offset, &axi_addr, &pci_addr);
/* /*
* Program the IARR registers. The upper 32-bit IARR register is * Program the IARR registers. The upper 32-bit IARR register is
@ -993,9 +1065,9 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
pcie->base + iarr_offset); pcie->base + iarr_offset);
writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4); writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4);
dev_info(dev, "iarr lo 0x%x iarr hi 0x%x\n", dev_dbg(dev, "iarr lo 0x%x iarr hi 0x%x\n",
readl(pcie->base + iarr_offset), readl(pcie->base + iarr_offset),
readl(pcie->base + iarr_offset + 4)); readl(pcie->base + iarr_offset + 4));
/* /*
* Now program the IMAP registers. Each IARR region may have one or * Now program the IMAP registers. Each IARR region may have one or
@ -1009,10 +1081,10 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
writel(upper_32_bits(axi_addr), writel(upper_32_bits(axi_addr),
pcie->base + imap_offset + ib_map->imap_addr_offset); pcie->base + imap_offset + ib_map->imap_addr_offset);
dev_info(dev, "imap window [%d] lo 0x%x hi 0x%x\n", dev_dbg(dev, "imap window [%d] lo 0x%x hi 0x%x\n",
window_idx, readl(pcie->base + imap_offset), window_idx, readl(pcie->base + imap_offset),
readl(pcie->base + imap_offset + readl(pcie->base + imap_offset +
ib_map->imap_addr_offset)); ib_map->imap_addr_offset));
imap_offset += ib_map->imap_window_offset; imap_offset += ib_map->imap_window_offset;
axi_addr += size; axi_addr += size;
@ -1144,10 +1216,22 @@ static int iproc_pcie_paxb_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr)
return ret; return ret;
} }
static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr) static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr,
bool enable)
{ {
u32 val; u32 val;
if (!enable) {
/*
* Disable PAXC MSI steering. All write transfers will be
* treated as non-MSI transfers
*/
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_EN_CFG);
val &= ~MSI_ENABLE_CFG;
iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_EN_CFG, val);
return;
}
/* /*
* Program bits [43:13] of address of GITS_TRANSLATER register into * Program bits [43:13] of address of GITS_TRANSLATER register into
* bits [30:0] of the MSI base address register. In fact, in all iProc * bits [30:0] of the MSI base address register. In fact, in all iProc
@ -1201,7 +1285,7 @@ static int iproc_pcie_msi_steer(struct iproc_pcie *pcie,
return ret; return ret;
break; break;
case IPROC_PCIE_PAXC_V2: case IPROC_PCIE_PAXC_V2:
iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr); iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr, true);
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -1271,6 +1355,7 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
break; break;
case IPROC_PCIE_PAXB: case IPROC_PCIE_PAXB:
regs = iproc_pcie_reg_paxb; regs = iproc_pcie_reg_paxb;
pcie->iproc_cfg_read = true;
pcie->has_apb_err_disable = true; pcie->has_apb_err_disable = true;
if (pcie->need_ob_cfg) { if (pcie->need_ob_cfg) {
pcie->ob_map = paxb_ob_map; pcie->ob_map = paxb_ob_map;
@ -1293,10 +1378,14 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
case IPROC_PCIE_PAXC: case IPROC_PCIE_PAXC:
regs = iproc_pcie_reg_paxc; regs = iproc_pcie_reg_paxc;
pcie->ep_is_internal = true; pcie->ep_is_internal = true;
pcie->iproc_cfg_read = true;
pcie->rej_unconfig_pf = true;
break; break;
case IPROC_PCIE_PAXC_V2: case IPROC_PCIE_PAXC_V2:
regs = iproc_pcie_reg_paxc_v2; regs = iproc_pcie_reg_paxc_v2;
pcie->ep_is_internal = true; pcie->ep_is_internal = true;
pcie->iproc_cfg_read = true;
pcie->rej_unconfig_pf = true;
pcie->need_msi_steer = true; pcie->need_msi_steer = true;
break; break;
default: default:
@ -1427,6 +1516,24 @@ int iproc_pcie_remove(struct iproc_pcie *pcie)
} }
EXPORT_SYMBOL(iproc_pcie_remove); EXPORT_SYMBOL(iproc_pcie_remove);
/*
* The MSI parsing logic in certain revisions of Broadcom PAXC based root
* complex does not work and needs to be disabled
*/
static void quirk_paxc_disable_msi_parsing(struct pci_dev *pdev)
{
struct iproc_pcie *pcie = iproc_data(pdev->bus);
if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
iproc_pcie_paxc_v2_msi_steer(pcie, 0, false);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0,
quirk_paxc_disable_msi_parsing);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802,
quirk_paxc_disable_msi_parsing);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804,
quirk_paxc_disable_msi_parsing);
MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver"); MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -58,8 +58,13 @@ struct iproc_msi;
* @phy: optional PHY device that controls the Serdes * @phy: optional PHY device that controls the Serdes
* @map_irq: function callback to map interrupts * @map_irq: function callback to map interrupts
* @ep_is_internal: indicates an internal emulated endpoint device is connected * @ep_is_internal: indicates an internal emulated endpoint device is connected
* @iproc_cfg_read: indicates the iProc config read function should be used
* @rej_unconfig_pf: indicates the root complex needs to detect and reject
* enumeration against unconfigured physical functions emulated in the ASIC
* @has_apb_err_disable: indicates the controller can be configured to prevent * @has_apb_err_disable: indicates the controller can be configured to prevent
* unsupported request from being forwarded as an APB bus error * unsupported request from being forwarded as an APB bus error
* @fix_paxc_cap: indicates the controller has corrupted capability list in its
* config space registers and requires SW based fixup
* *
* @need_ob_cfg: indicates SW needs to configure the outbound mapping window * @need_ob_cfg: indicates SW needs to configure the outbound mapping window
* @ob: outbound mapping related parameters * @ob: outbound mapping related parameters
@ -84,7 +89,10 @@ struct iproc_pcie {
struct phy *phy; struct phy *phy;
int (*map_irq)(const struct pci_dev *, u8, u8); int (*map_irq)(const struct pci_dev *, u8, u8);
bool ep_is_internal; bool ep_is_internal;
bool iproc_cfg_read;
bool rej_unconfig_pf;
bool has_apb_err_disable; bool has_apb_err_disable;
bool fix_paxc_cap;
bool need_ob_cfg; bool need_ob_cfg;
struct iproc_pcie_ob ob; struct iproc_pcie_ob ob;

View File

@ -2356,6 +2356,9 @@ static void quirk_paxc_bridge(struct pci_dev *pdev)
} }
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge);
#endif #endif
/* /*