diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-qcom.c')
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 78 | 
1 files changed, 77 insertions, 1 deletions
| diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index f711acacaeaf..38d5d46487bb 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -12,6 +12,7 @@  #include <linux/crc8.h>  #include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/interconnect.h>  #include <linux/interrupt.h>  #include <linux/io.h>  #include <linux/iopoll.h> @@ -223,6 +224,7 @@ struct qcom_pcie {  	union qcom_pcie_resources res;  	struct phy *phy;  	struct gpio_desc *reset; +	struct icc_path *icc_mem;  	const struct qcom_pcie_cfg *cfg;  }; @@ -1236,7 +1238,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)  	ret = reset_control_assert(res->pci_reset);  	if (ret < 0) { -		dev_err(dev, "cannot deassert pci reset\n"); +		dev_err(dev, "cannot assert pci reset\n");  		goto err_disable_clocks;  	} @@ -1639,6 +1641,74 @@ static const struct dw_pcie_ops dw_pcie_ops = {  	.start_link = qcom_pcie_start_link,  }; +static int qcom_pcie_icc_init(struct qcom_pcie *pcie) +{ +	struct dw_pcie *pci = pcie->pci; +	int ret; + +	pcie->icc_mem = devm_of_icc_get(pci->dev, "pcie-mem"); +	if (IS_ERR(pcie->icc_mem)) +		return PTR_ERR(pcie->icc_mem); + +	/* +	 * Some Qualcomm platforms require interconnect bandwidth constraints +	 * to be set before enabling interconnect clocks. +	 * +	 * Set an initial peak bandwidth corresponding to single-lane Gen 1 +	 * for the pcie-mem path. +	 */ +	ret = icc_set_bw(pcie->icc_mem, 0, MBps_to_icc(250)); +	if (ret) { +		dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", +			ret); +		return ret; +	} + +	return 0; +} + +static void qcom_pcie_icc_update(struct qcom_pcie *pcie) +{ +	struct dw_pcie *pci = pcie->pci; +	u32 offset, status, bw; +	int speed, width; +	int ret; + +	if (!pcie->icc_mem) +		return; + +	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); +	status = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA); + +	/* Only update constraints if link is up. */ +	if (!(status & PCI_EXP_LNKSTA_DLLLA)) +		return; + +	speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status); +	width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status); + +	switch (speed) { +	case 1: +		bw = MBps_to_icc(250); +		break; +	case 2: +		bw = MBps_to_icc(500); +		break; +	default: +		WARN_ON_ONCE(1); +		fallthrough; +	case 3: +		bw = MBps_to_icc(985); +		break; +	} + +	ret = icc_set_bw(pcie->icc_mem, 0, width * bw); +	if (ret) { +		dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", +			ret); +	} +} +  static int qcom_pcie_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -1699,6 +1769,10 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		goto err_pm_runtime_put;  	} +	ret = qcom_pcie_icc_init(pcie); +	if (ret) +		goto err_pm_runtime_put; +  	ret = pcie->cfg->ops->get_resources(pcie);  	if (ret)  		goto err_pm_runtime_put; @@ -1717,6 +1791,8 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		goto err_phy_exit;  	} +	qcom_pcie_icc_update(pcie); +  	return 0;  err_phy_exit: |