diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-qcom-ep.c')
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom-ep.c | 81 | 
1 files changed, 78 insertions, 3 deletions
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 0fe7f06f2102..8bd8107690a6 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -13,6 +13,7 @@  #include <linux/debugfs.h>  #include <linux/delay.h>  #include <linux/gpio/consumer.h> +#include <linux/interconnect.h>  #include <linux/mfd/syscon.h>  #include <linux/phy/pcie.h>  #include <linux/phy/phy.h> @@ -74,6 +75,7 @@  #define PARF_INT_ALL_PLS_ERR			BIT(15)  #define PARF_INT_ALL_PME_LEGACY			BIT(16)  #define PARF_INT_ALL_PLS_PME			BIT(17) +#define PARF_INT_ALL_EDMA			BIT(22)  /* PARF_BDF_TO_SID_CFG register fields */  #define PARF_BDF_TO_SID_BYPASS			BIT(0) @@ -133,6 +135,11 @@  #define CORE_RESET_TIME_US_MAX			1005  #define WAKE_DELAY_US				2000 /* 2 ms */ +#define PCIE_GEN1_BW_MBPS			250 +#define PCIE_GEN2_BW_MBPS			500 +#define PCIE_GEN3_BW_MBPS			985 +#define PCIE_GEN4_BW_MBPS			1969 +  #define to_pcie_ep(x)				dev_get_drvdata((x)->dev)  enum qcom_pcie_ep_link_status { @@ -155,6 +162,7 @@ enum qcom_pcie_ep_link_status {   * @wake: WAKE# GPIO   * @phy: PHY controller block   * @debugfs: PCIe Endpoint Debugfs directory + * @icc_mem: Handle to an interconnect path between PCIe and MEM   * @clks: PCIe clocks   * @num_clks: PCIe clocks count   * @perst_en: Flag for PERST enable @@ -178,6 +186,8 @@ struct qcom_pcie_ep {  	struct phy *phy;  	struct dentry *debugfs; +	struct icc_path *icc_mem; +  	struct clk_bulk_data *clks;  	int num_clks; @@ -253,8 +263,49 @@ static void qcom_pcie_dw_stop_link(struct dw_pcie *pci)  	disable_irq(pcie_ep->perst_irq);  } +static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep) +{ +	struct dw_pcie *pci = &pcie_ep->pci; +	u32 offset, status, bw; +	int speed, width; +	int ret; + +	if (!pcie_ep->icc_mem) +		return; + +	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); +	status = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA); + +	speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status); +	width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status); + +	switch (speed) { +	case 1: +		bw = MBps_to_icc(PCIE_GEN1_BW_MBPS); +		break; +	case 2: +		bw = MBps_to_icc(PCIE_GEN2_BW_MBPS); +		break; +	case 3: +		bw = MBps_to_icc(PCIE_GEN3_BW_MBPS); +		break; +	default: +		dev_warn(pci->dev, "using default GEN4 bandwidth\n"); +		fallthrough; +	case 4: +		bw = MBps_to_icc(PCIE_GEN4_BW_MBPS); +		break; +	} + +	ret = icc_set_bw(pcie_ep->icc_mem, 0, width * bw); +	if (ret) +		dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", +			ret); +} +  static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep)  { +	struct dw_pcie *pci = &pcie_ep->pci;  	int ret;  	ret = clk_bulk_prepare_enable(pcie_ep->num_clks, pcie_ep->clks); @@ -277,8 +328,24 @@ static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep)  	if (ret)  		goto err_phy_exit; +	/* +	 * 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_ep->icc_mem, 0, MBps_to_icc(PCIE_GEN1_BW_MBPS)); +	if (ret) { +		dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", +			ret); +		goto err_phy_off; +	} +  	return 0; +err_phy_off: +	phy_power_off(pcie_ep->phy);  err_phy_exit:  	phy_exit(pcie_ep->phy);  err_disable_clk: @@ -289,6 +356,7 @@ err_disable_clk:  static void qcom_pcie_disable_resources(struct qcom_pcie_ep *pcie_ep)  { +	icc_set_bw(pcie_ep->icc_mem, 0, 0);  	phy_power_off(pcie_ep->phy);  	phy_exit(pcie_ep->phy);  	clk_bulk_disable_unprepare(pcie_ep->num_clks, pcie_ep->clks); @@ -395,7 +463,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)  	writel_relaxed(0, pcie_ep->parf + PARF_INT_ALL_MASK);  	val = PARF_INT_ALL_LINK_DOWN | PARF_INT_ALL_BME |  	      PARF_INT_ALL_PM_TURNOFF | PARF_INT_ALL_DSTATE_CHANGE | -	      PARF_INT_ALL_LINK_UP; +	      PARF_INT_ALL_LINK_UP | PARF_INT_ALL_EDMA;  	writel_relaxed(val, pcie_ep->parf + PARF_INT_ALL_MASK);  	ret = dw_pcie_ep_init_complete(&pcie_ep->pci.ep); @@ -415,7 +483,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)  	/* Gate Master AXI clock to MHI bus during L1SS */  	val = readl_relaxed(pcie_ep->parf + PARF_MHI_CLOCK_RESET_CTRL);  	val &= ~PARF_MSTR_AXI_CLK_EN; -	val = readl_relaxed(pcie_ep->parf + PARF_MHI_CLOCK_RESET_CTRL); +	writel_relaxed(val, pcie_ep->parf + PARF_MHI_CLOCK_RESET_CTRL);  	dw_pcie_ep_init_notify(&pcie_ep->pci.ep); @@ -550,6 +618,10 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev,  	if (IS_ERR(pcie_ep->phy))  		ret = PTR_ERR(pcie_ep->phy); +	pcie_ep->icc_mem = devm_of_icc_get(dev, "pcie-mem"); +	if (IS_ERR(pcie_ep->icc_mem)) +		ret = PTR_ERR(pcie_ep->icc_mem); +  	return ret;  } @@ -573,6 +645,7 @@ static irqreturn_t qcom_pcie_ep_global_irq_thread(int irq, void *data)  	} else if (FIELD_GET(PARF_INT_ALL_BME, status)) {  		dev_dbg(dev, "Received BME event. Link is enabled!\n");  		pcie_ep->link_status = QCOM_PCIE_EP_LINK_ENABLED; +		qcom_pcie_ep_icc_update(pcie_ep);  		pci_epc_bme_notify(pci->ep.epc);  	} else if (FIELD_GET(PARF_INT_ALL_PM_TURNOFF, status)) {  		dev_dbg(dev, "Received PM Turn-off event! Entering L23\n"); @@ -593,7 +666,7 @@ static irqreturn_t qcom_pcie_ep_global_irq_thread(int irq, void *data)  		dw_pcie_ep_linkup(&pci->ep);  		pcie_ep->link_status = QCOM_PCIE_EP_LINK_UP;  	} else { -		dev_dbg(dev, "Received unknown event: %d\n", status); +		dev_err(dev, "Received unknown event: %d\n", status);  	}  	return IRQ_HANDLED; @@ -706,6 +779,7 @@ static const struct pci_epc_features qcom_pcie_epc_features = {  	.core_init_notifier = true,  	.msi_capable = true,  	.msix_capable = false, +	.align = SZ_4K,  };  static const struct pci_epc_features * @@ -743,6 +817,7 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)  	pcie_ep->pci.dev = dev;  	pcie_ep->pci.ops = &pci_ops;  	pcie_ep->pci.ep.ops = &pci_ep_ops; +	pcie_ep->pci.edma.nr_irqs = 1;  	platform_set_drvdata(pdev, pcie_ep);  	ret = qcom_pcie_ep_get_resources(pdev, pcie_ep);  |