Commit 59189d06 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'remotes/lorenzo/pci/cadence'

- Retrain Link to work around Gen2 training defect (Nadeem Athani)

* remotes/lorenzo/pci/cadence:
  PCI: cadence: Retrain Link to work around Gen2 training defect
parents 93aed521 4740b969
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ enum j721e_pcie_mode {

struct j721e_pcie_data {
	enum j721e_pcie_mode	mode;
	bool quirk_retrain_flag;
};

static inline u32 j721e_pcie_user_readl(struct j721e_pcie *pcie, u32 offset)
@@ -280,6 +281,7 @@ static struct pci_ops cdns_ti_pcie_host_ops = {

static const struct j721e_pcie_data j721e_pcie_rc_data = {
	.mode = PCI_MODE_RC,
	.quirk_retrain_flag = true,
};

static const struct j721e_pcie_data j721e_pcie_ep_data = {
@@ -388,6 +390,7 @@ static int j721e_pcie_probe(struct platform_device *pdev)

		bridge->ops = &cdns_ti_pcie_host_ops;
		rc = pci_host_bridge_priv(bridge);
		rc->quirk_retrain_flag = data->quirk_retrain_flag;

		cdns_pcie = &rc->pcie;
		cdns_pcie->dev = dev;
+63 −18
Original line number Diff line number Diff line
@@ -77,6 +77,68 @@ static struct pci_ops cdns_pcie_host_ops = {
	.write		= pci_generic_config_write,
};

static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie)
{
	struct device *dev = pcie->dev;
	int retries;

	/* Check if the link is up or not */
	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
		if (cdns_pcie_link_up(pcie)) {
			dev_info(dev, "Link up\n");
			return 0;
		}
		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
	}

	return -ETIMEDOUT;
}

static int cdns_pcie_retrain(struct cdns_pcie *pcie)
{
	u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
	u16 lnk_stat, lnk_ctl;
	int ret = 0;

	/*
	 * Set retrain bit if current speed is 2.5 GB/s,
	 * but the PCIe root port support is > 2.5 GB/s.
	 */

	lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off +
					     PCI_EXP_LNKCAP));
	if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
		return ret;

	lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA);
	if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
		lnk_ctl = cdns_pcie_rp_readw(pcie,
					     pcie_cap_off + PCI_EXP_LNKCTL);
		lnk_ctl |= PCI_EXP_LNKCTL_RL;
		cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL,
				    lnk_ctl);

		ret = cdns_pcie_host_wait_for_link(pcie);
	}
	return ret;
}

static int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc)
{
	struct cdns_pcie *pcie = &rc->pcie;
	int ret;

	ret = cdns_pcie_host_wait_for_link(pcie);

	/*
	 * Retrain link for Gen2 training defect
	 * if quirk flag is set.
	 */
	if (!ret && rc->quirk_retrain_flag)
		ret = cdns_pcie_retrain(pcie);

	return ret;
}

static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc)
{
@@ -399,23 +461,6 @@ static int cdns_pcie_host_init(struct device *dev,
	return cdns_pcie_host_init_address_translation(rc);
}

static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie)
{
	struct device *dev = pcie->dev;
	int retries;

	/* Check if the link is up or not */
	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
		if (cdns_pcie_link_up(pcie)) {
			dev_info(dev, "Link up\n");
			return 0;
		}
		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
	}

	return -ETIMEDOUT;
}

int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
{
	struct device *dev = rc->pcie.dev;
@@ -458,7 +503,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
		return ret;
	}

	ret = cdns_pcie_host_wait_for_link(pcie);
	ret = cdns_pcie_host_start_link(rc);
	if (ret)
		dev_dbg(dev, "PCIe link never came up\n");

+10 −1
Original line number Diff line number Diff line
@@ -119,7 +119,7 @@
 * Root Port Registers (PCI configuration space for the root port function)
 */
#define CDNS_PCIE_RP_BASE	0x00200000

#define CDNS_PCIE_RP_CAP_OFFSET 0xc0

/*
 * Address Translation Registers
@@ -291,6 +291,7 @@ struct cdns_pcie {
 * @device_id: PCI device ID
 * @avail_ib_bar: Satus of RP_BAR0, RP_BAR1 and	RP_NO_BAR if it's free or
 *                available
 * @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2
 */
struct cdns_pcie_rc {
	struct cdns_pcie	pcie;
@@ -299,6 +300,7 @@ struct cdns_pcie_rc {
	u32			vendor_id;
	u32			device_id;
	bool			avail_ib_bar[CDNS_PCIE_RP_MAX_IB];
	bool                    quirk_retrain_flag;
};

/**
@@ -414,6 +416,13 @@ static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie,
	cdns_pcie_write_sz(addr, 0x2, value);
}

static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg)
{
	void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg;

	return cdns_pcie_read_sz(addr, 0x2);
}

/* Endpoint Function register access */
static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn,
					  u32 reg, u8 value)