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:
commit
0ea77d2b20
|
@ -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");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue