diff options
Diffstat (limited to 'drivers/pci/controller/cadence/pcie-cadence-host.c')
| -rw-r--r-- | drivers/pci/controller/cadence/pcie-cadence-host.c | 86 | 
1 files changed, 66 insertions, 20 deletions
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 811c1cb2e8de..73dcf8cf98fb 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -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)  { @@ -321,9 +383,10 @@ static int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc)  	resource_list_for_each_entry(entry, &bridge->dma_ranges) {  		err = cdns_pcie_host_bar_config(rc, entry); -		if (err) +		if (err) {  			dev_err(dev, "Fail to configure IB using dma-ranges\n"); -		return err; +			return err; +		}  	}  	return 0; @@ -398,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; @@ -457,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");  | 
