mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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
					
				
					 3 changed files with 144 additions and 26 deletions
				
			
		| 
						 | 
				
			
			@ -85,6 +85,8 @@
 | 
			
		|||
#define IMAP_VALID_SHIFT		0
 | 
			
		||||
#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_PCIE_REG_INVALID		0xffff
 | 
			
		||||
| 
						 | 
				
			
			@ -375,6 +377,17 @@ static const u16 iproc_pcie_reg_paxc_v2[] = {
 | 
			
		|||
	[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)
 | 
			
		||||
{
 | 
			
		||||
	struct iproc_pcie *pcie = bus->sysdata;
 | 
			
		||||
| 
						 | 
				
			
			@ -495,6 +508,49 @@ static unsigned int iproc_pcie_cfg_retry(void __iomem *cfg_data_p)
 | 
			
		|||
	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,
 | 
			
		||||
				  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 */
 | 
			
		||||
	if (busno == 0) {
 | 
			
		||||
		ret = pci_generic_config_read32(bus, devfn, where, size, val);
 | 
			
		||||
		if (ret != PCIBIOS_SUCCESSFUL)
 | 
			
		||||
			return ret;
 | 
			
		||||
		if (ret == PCIBIOS_SUCCESSFUL)
 | 
			
		||||
			iproc_pcie_fix_cap(pcie, where, val);
 | 
			
		||||
 | 
			
		||||
		/* Don't advertise CRS SV support */
 | 
			
		||||
		if ((where & ~0x3) == IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL)
 | 
			
		||||
			*val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
 | 
			
		||||
		return PCIBIOS_SUCCESSFUL;
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
		*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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -628,7 +700,7 @@ static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
 | 
			
		|||
	struct iproc_pcie *pcie = iproc_data(bus);
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	else
 | 
			
		||||
		ret = pci_generic_config_read32(bus, devfn, where, size, val);
 | 
			
		||||
| 
						 | 
				
			
			@ -808,12 +880,12 @@ 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(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);
 | 
			
		||||
	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 + 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 + 4));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -982,7 +1054,7 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
 | 
			
		|||
	    iproc_pcie_reg_is_invalid(imap_offset))
 | 
			
		||||
		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);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
| 
						 | 
				
			
			@ -993,7 +1065,7 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
 | 
			
		|||
	       pcie->base + iarr_offset);
 | 
			
		||||
	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 + 4));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1009,7 +1081,7 @@ static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
 | 
			
		|||
		writel(upper_32_bits(axi_addr),
 | 
			
		||||
		       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),
 | 
			
		||||
			readl(pcie->base + imap_offset +
 | 
			
		||||
			      ib_map->imap_addr_offset));
 | 
			
		||||
| 
						 | 
				
			
			@ -1144,10 +1216,22 @@ static int iproc_pcie_paxb_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr)
 | 
			
		|||
	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;
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	 * 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;
 | 
			
		||||
		break;
 | 
			
		||||
	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;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
| 
						 | 
				
			
			@ -1271,6 +1355,7 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
 | 
			
		|||
		break;
 | 
			
		||||
	case IPROC_PCIE_PAXB:
 | 
			
		||||
		regs = iproc_pcie_reg_paxb;
 | 
			
		||||
		pcie->iproc_cfg_read = true;
 | 
			
		||||
		pcie->has_apb_err_disable = true;
 | 
			
		||||
		if (pcie->need_ob_cfg) {
 | 
			
		||||
			pcie->ob_map = paxb_ob_map;
 | 
			
		||||
| 
						 | 
				
			
			@ -1293,10 +1378,14 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
 | 
			
		|||
	case IPROC_PCIE_PAXC:
 | 
			
		||||
		regs = iproc_pcie_reg_paxc;
 | 
			
		||||
		pcie->ep_is_internal = true;
 | 
			
		||||
		pcie->iproc_cfg_read = true;
 | 
			
		||||
		pcie->rej_unconfig_pf = true;
 | 
			
		||||
		break;
 | 
			
		||||
	case IPROC_PCIE_PAXC_V2:
 | 
			
		||||
		regs = iproc_pcie_reg_paxc_v2;
 | 
			
		||||
		pcie->ep_is_internal = true;
 | 
			
		||||
		pcie->iproc_cfg_read = true;
 | 
			
		||||
		pcie->rej_unconfig_pf = true;
 | 
			
		||||
		pcie->need_msi_steer = true;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
| 
						 | 
				
			
			@ -1427,6 +1516,24 @@ int iproc_pcie_remove(struct iproc_pcie *pcie)
 | 
			
		|||
}
 | 
			
		||||
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_DESCRIPTION("Broadcom iPROC PCIe common driver");
 | 
			
		||||
MODULE_LICENSE("GPL v2");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,8 +58,13 @@ struct iproc_msi;
 | 
			
		|||
 * @phy: optional PHY device that controls the Serdes
 | 
			
		||||
 * @map_irq: function callback to map interrupts
 | 
			
		||||
 * @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
 | 
			
		||||
 * 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
 | 
			
		||||
 * @ob: outbound mapping related parameters
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +89,10 @@ struct iproc_pcie {
 | 
			
		|||
	struct phy *phy;
 | 
			
		||||
	int (*map_irq)(const struct pci_dev *, u8, u8);
 | 
			
		||||
	bool ep_is_internal;
 | 
			
		||||
	bool iproc_cfg_read;
 | 
			
		||||
	bool rej_unconfig_pf;
 | 
			
		||||
	bool has_apb_err_disable;
 | 
			
		||||
	bool fix_paxc_cap;
 | 
			
		||||
 | 
			
		||||
	bool need_ob_cfg;
 | 
			
		||||
	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, 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
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue