diff options
Diffstat (limited to 'drivers/pci/controller/pcie-iproc.c')
| -rw-r--r-- | drivers/pci/controller/pcie-iproc.c | 159 | 
1 files changed, 133 insertions, 26 deletions
| diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index 3c76c5fa4f32..3160e9342a2f 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -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,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(upper_32_bits(pci_addr), pcie->base + omap_offset + 4); -	dev_info(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", -		 readl(pcie->base + oarr_offset), -		 readl(pcie->base + oarr_offset + 4)); -	dev_info(dev, "omap lo 0x%x omap hi 0x%x\n", -		 readl(pcie->base + omap_offset), -		 readl(pcie->base + omap_offset + 4)); +	dev_dbg(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n", +		window_idx, oarr_offset, &axi_addr, &pci_addr); +	dev_dbg(dev, "oarr lo 0x%x oarr hi 0x%x\n", +		readl(pcie->base + oarr_offset), +		readl(pcie->base + oarr_offset + 4)); +	dev_dbg(dev, "omap lo 0x%x omap hi 0x%x\n", +		readl(pcie->base + omap_offset), +		readl(pcie->base + omap_offset + 4));  	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))  		return -EINVAL; -	dev_info(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n", -		 region_idx, iarr_offset, &axi_addr, &pci_addr); +	dev_dbg(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n", +		region_idx, iarr_offset, &axi_addr, &pci_addr);  	/*  	 * 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);  	writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4); -	dev_info(dev, "iarr lo 0x%x iarr hi 0x%x\n", -		 readl(pcie->base + iarr_offset), -		 readl(pcie->base + iarr_offset + 4)); +	dev_dbg(dev, "iarr lo 0x%x iarr hi 0x%x\n", +		readl(pcie->base + iarr_offset), +		readl(pcie->base + iarr_offset + 4));  	/*  	 * 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),  		       pcie->base + imap_offset + ib_map->imap_addr_offset); -		dev_info(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)); +		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));  		imap_offset += ib_map->imap_window_offset;  		axi_addr += size; @@ -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 <[email protected]>");  MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver");  MODULE_LICENSE("GPL v2"); |