Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ea77d2b authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

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
parents 1974704e 0043d4ae
Loading
Loading
Loading
Loading
+133 −26
Original line number Diff line number Diff line
@@ -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");
+8 −0
Original line number Diff line number Diff line
@@ -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;
+3 −0
Original line number Diff line number Diff line
@@ -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

/*