diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-qcom.c')
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 346 | 
1 files changed, 203 insertions, 143 deletions
| diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 14772edcf0d3..0180edf3310e 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -18,10 +18,11 @@  #include <linux/io.h>  #include <linux/iopoll.h>  #include <linux/kernel.h> +#include <linux/limits.h>  #include <linux/init.h>  #include <linux/of.h> -#include <linux/of_gpio.h>  #include <linux/pci.h> +#include <linux/pm_opp.h>  #include <linux/pm_runtime.h>  #include <linux/platform_device.h>  #include <linux/phy/pcie.h> @@ -30,6 +31,7 @@  #include <linux/reset.h>  #include <linux/slab.h>  #include <linux/types.h> +#include <linux/units.h>  #include "../../pci.h"  #include "pcie-designware.h" @@ -51,6 +53,7 @@  #define PARF_SID_OFFSET				0x234  #define PARF_BDF_TRANSLATE_CFG			0x24c  #define PARF_SLV_ADDR_SPACE_SIZE		0x358 +#define PARF_NO_SNOOP_OVERIDE			0x3d4  #define PARF_DEVICE_TYPE			0x1000  #define PARF_BDF_TO_SID_TABLE_N			0x2000  #define PARF_BDF_TO_SID_CFG			0x2c00 @@ -118,6 +121,10 @@  /* PARF_LTSSM register fields */  #define LTSSM_EN				BIT(8) +/* PARF_NO_SNOOP_OVERIDE register fields */ +#define WR_NO_SNOOP_OVERIDE_EN			BIT(1) +#define RD_NO_SNOOP_OVERIDE_EN			BIT(3) +  /* PARF_DEVICE_TYPE register fields */  #define DEVICE_TYPE_RC				0x4 @@ -154,58 +161,56 @@  #define QCOM_PCIE_LINK_SPEED_TO_BW(speed) \  		Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_link_speed[speed])) -#define QCOM_PCIE_1_0_0_MAX_CLOCKS		4  struct qcom_pcie_resources_1_0_0 { -	struct clk_bulk_data clks[QCOM_PCIE_1_0_0_MAX_CLOCKS]; +	struct clk_bulk_data *clks; +	int num_clks;  	struct reset_control *core;  	struct regulator *vdda;  }; -#define QCOM_PCIE_2_1_0_MAX_CLOCKS		5  #define QCOM_PCIE_2_1_0_MAX_RESETS		6  #define QCOM_PCIE_2_1_0_MAX_SUPPLY		3  struct qcom_pcie_resources_2_1_0 { -	struct clk_bulk_data clks[QCOM_PCIE_2_1_0_MAX_CLOCKS]; +	struct clk_bulk_data *clks; +	int num_clks;  	struct reset_control_bulk_data resets[QCOM_PCIE_2_1_0_MAX_RESETS];  	int num_resets;  	struct regulator_bulk_data supplies[QCOM_PCIE_2_1_0_MAX_SUPPLY];  }; -#define QCOM_PCIE_2_3_2_MAX_CLOCKS		4  #define QCOM_PCIE_2_3_2_MAX_SUPPLY		2  struct qcom_pcie_resources_2_3_2 { -	struct clk_bulk_data clks[QCOM_PCIE_2_3_2_MAX_CLOCKS]; +	struct clk_bulk_data *clks; +	int num_clks;  	struct regulator_bulk_data supplies[QCOM_PCIE_2_3_2_MAX_SUPPLY];  }; -#define QCOM_PCIE_2_3_3_MAX_CLOCKS		5  #define QCOM_PCIE_2_3_3_MAX_RESETS		7  struct qcom_pcie_resources_2_3_3 { -	struct clk_bulk_data clks[QCOM_PCIE_2_3_3_MAX_CLOCKS]; +	struct clk_bulk_data *clks; +	int num_clks;  	struct reset_control_bulk_data rst[QCOM_PCIE_2_3_3_MAX_RESETS];  }; -#define QCOM_PCIE_2_4_0_MAX_CLOCKS		4  #define QCOM_PCIE_2_4_0_MAX_RESETS		12  struct qcom_pcie_resources_2_4_0 { -	struct clk_bulk_data clks[QCOM_PCIE_2_4_0_MAX_CLOCKS]; +	struct clk_bulk_data *clks;  	int num_clks;  	struct reset_control_bulk_data resets[QCOM_PCIE_2_4_0_MAX_RESETS];  	int num_resets;  }; -#define QCOM_PCIE_2_7_0_MAX_CLOCKS		15  #define QCOM_PCIE_2_7_0_MAX_SUPPLIES		2  struct qcom_pcie_resources_2_7_0 { -	struct clk_bulk_data clks[QCOM_PCIE_2_7_0_MAX_CLOCKS]; +	struct clk_bulk_data *clks;  	int num_clks;  	struct regulator_bulk_data supplies[QCOM_PCIE_2_7_0_MAX_SUPPLIES];  	struct reset_control *rst;  }; -#define QCOM_PCIE_2_9_0_MAX_CLOCKS		5  struct qcom_pcie_resources_2_9_0 { -	struct clk_bulk_data clks[QCOM_PCIE_2_9_0_MAX_CLOCKS]; +	struct clk_bulk_data *clks; +	int num_clks;  	struct reset_control *rst;  }; @@ -231,8 +236,15 @@ struct qcom_pcie_ops {  	int (*config_sid)(struct qcom_pcie *pcie);  }; + /** +  * struct qcom_pcie_cfg - Per SoC config struct +  * @ops: qcom PCIe ops structure +  * @override_no_snoop: Override NO_SNOOP attribute in TLP to enable cache +  * snooping +  */  struct qcom_pcie_cfg {  	const struct qcom_pcie_ops *ops; +	bool override_no_snoop;  	bool no_l0s;  }; @@ -245,6 +257,7 @@ struct qcom_pcie {  	struct phy *phy;  	struct gpio_desc *reset;  	struct icc_path *icc_mem; +	struct icc_path *icc_cpu;  	const struct qcom_pcie_cfg *cfg;  	struct dentry *debugfs;  	bool suspended; @@ -337,21 +350,11 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie)  	if (ret)  		return ret; -	res->clks[0].id = "iface"; -	res->clks[1].id = "core"; -	res->clks[2].id = "phy"; -	res->clks[3].id = "aux"; -	res->clks[4].id = "ref"; - -	/* iface, core, phy are required */ -	ret = devm_clk_bulk_get(dev, 3, res->clks); -	if (ret < 0) -		return ret; - -	/* aux, ref are optional */ -	ret = devm_clk_bulk_get_optional(dev, 2, res->clks + 3); -	if (ret < 0) -		return ret; +	res->num_clks = devm_clk_bulk_get_all(dev, &res->clks); +	if (res->num_clks < 0) { +		dev_err(dev, "Failed to get clocks\n"); +		return res->num_clks; +	}  	res->resets[0].id = "pci";  	res->resets[1].id = "axi"; @@ -373,7 +376,7 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie)  {  	struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; -	clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); +	clk_bulk_disable_unprepare(res->num_clks, res->clks);  	reset_control_bulk_assert(res->num_resets, res->resets);  	writel(1, pcie->parf + PARF_PHY_CTRL); @@ -425,7 +428,7 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie)  	val &= ~PHY_TEST_PWR_DOWN;  	writel(val, pcie->parf + PARF_PHY_CTRL); -	ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); +	ret = clk_bulk_prepare_enable(res->num_clks, res->clks);  	if (ret)  		return ret; @@ -476,20 +479,16 @@ static int qcom_pcie_get_resources_1_0_0(struct qcom_pcie *pcie)  	struct qcom_pcie_resources_1_0_0 *res = &pcie->res.v1_0_0;  	struct dw_pcie *pci = pcie->pci;  	struct device *dev = pci->dev; -	int ret;  	res->vdda = devm_regulator_get(dev, "vdda");  	if (IS_ERR(res->vdda))  		return PTR_ERR(res->vdda); -	res->clks[0].id = "iface"; -	res->clks[1].id = "aux"; -	res->clks[2].id = "master_bus"; -	res->clks[3].id = "slave_bus"; - -	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); -	if (ret < 0) -		return ret; +	res->num_clks = devm_clk_bulk_get_all(dev, &res->clks); +	if (res->num_clks < 0) { +		dev_err(dev, "Failed to get clocks\n"); +		return res->num_clks; +	}  	res->core = devm_reset_control_get_exclusive(dev, "core");  	return PTR_ERR_OR_ZERO(res->core); @@ -500,7 +499,7 @@ static void qcom_pcie_deinit_1_0_0(struct qcom_pcie *pcie)  	struct qcom_pcie_resources_1_0_0 *res = &pcie->res.v1_0_0;  	reset_control_assert(res->core); -	clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); +	clk_bulk_disable_unprepare(res->num_clks, res->clks);  	regulator_disable(res->vdda);  } @@ -517,7 +516,7 @@ static int qcom_pcie_init_1_0_0(struct qcom_pcie *pcie)  		return ret;  	} -	ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); +	ret = clk_bulk_prepare_enable(res->num_clks, res->clks);  	if (ret) {  		dev_err(dev, "cannot prepare/enable clocks\n");  		goto err_assert_reset; @@ -532,7 +531,7 @@ static int qcom_pcie_init_1_0_0(struct qcom_pcie *pcie)  	return 0;  err_disable_clks: -	clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); +	clk_bulk_disable_unprepare(res->num_clks, res->clks);  err_assert_reset:  	reset_control_assert(res->core); @@ -580,14 +579,11 @@ static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie)  	if (ret)  		return ret; -	res->clks[0].id = "aux"; -	res->clks[1].id = "cfg"; -	res->clks[2].id = "bus_master"; -	res->clks[3].id = "bus_slave"; - -	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); -	if (ret < 0) -		return ret; +	res->num_clks = devm_clk_bulk_get_all(dev, &res->clks); +	if (res->num_clks < 0) { +		dev_err(dev, "Failed to get clocks\n"); +		return res->num_clks; +	}  	return 0;  } @@ -596,7 +592,7 @@ static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie)  {  	struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; -	clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); +	clk_bulk_disable_unprepare(res->num_clks, res->clks);  	regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);  } @@ -613,7 +609,7 @@ static int qcom_pcie_init_2_3_2(struct qcom_pcie *pcie)  		return ret;  	} -	ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); +	ret = clk_bulk_prepare_enable(res->num_clks, res->clks);  	if (ret) {  		dev_err(dev, "cannot prepare/enable clocks\n");  		regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); @@ -661,17 +657,11 @@ static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie)  	bool is_ipq = of_device_is_compatible(dev->of_node, "qcom,pcie-ipq4019");  	int ret; -	res->clks[0].id = "aux"; -	res->clks[1].id = "master_bus"; -	res->clks[2].id = "slave_bus"; -	res->clks[3].id = "iface"; - -	/* qcom,pcie-ipq4019 is defined without "iface" */ -	res->num_clks = is_ipq ? 3 : 4; - -	ret = devm_clk_bulk_get(dev, res->num_clks, res->clks); -	if (ret < 0) -		return ret; +	res->num_clks = devm_clk_bulk_get_all(dev, &res->clks); +	if (res->num_clks < 0) { +		dev_err(dev, "Failed to get clocks\n"); +		return res->num_clks; +	}  	res->resets[0].id = "axi_m";  	res->resets[1].id = "axi_s"; @@ -742,15 +732,11 @@ static int qcom_pcie_get_resources_2_3_3(struct qcom_pcie *pcie)  	struct device *dev = pci->dev;  	int ret; -	res->clks[0].id = "iface"; -	res->clks[1].id = "axi_m"; -	res->clks[2].id = "axi_s"; -	res->clks[3].id = "ahb"; -	res->clks[4].id = "aux"; - -	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); -	if (ret < 0) -		return ret; +	res->num_clks = devm_clk_bulk_get_all(dev, &res->clks); +	if (res->num_clks < 0) { +		dev_err(dev, "Failed to get clocks\n"); +		return res->num_clks; +	}  	res->rst[0].id = "axi_m";  	res->rst[1].id = "axi_s"; @@ -771,7 +757,7 @@ static void qcom_pcie_deinit_2_3_3(struct qcom_pcie *pcie)  {  	struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; -	clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); +	clk_bulk_disable_unprepare(res->num_clks, res->clks);  }  static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie) @@ -801,7 +787,7 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie)  	 */  	usleep_range(2000, 2500); -	ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); +	ret = clk_bulk_prepare_enable(res->num_clks, res->clks);  	if (ret) {  		dev_err(dev, "cannot prepare/enable clocks\n");  		goto err_assert_resets; @@ -862,8 +848,6 @@ static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie)  	struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;  	struct dw_pcie *pci = pcie->pci;  	struct device *dev = pci->dev; -	unsigned int num_clks, num_opt_clks; -	unsigned int idx;  	int ret;  	res->rst = devm_reset_control_array_get_exclusive(dev); @@ -877,36 +861,11 @@ static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie)  	if (ret)  		return ret; -	idx = 0; -	res->clks[idx++].id = "aux"; -	res->clks[idx++].id = "cfg"; -	res->clks[idx++].id = "bus_master"; -	res->clks[idx++].id = "bus_slave"; -	res->clks[idx++].id = "slave_q2a"; - -	num_clks = idx; - -	ret = devm_clk_bulk_get(dev, num_clks, res->clks); -	if (ret < 0) -		return ret; - -	res->clks[idx++].id = "tbu"; -	res->clks[idx++].id = "ddrss_sf_tbu"; -	res->clks[idx++].id = "aggre0"; -	res->clks[idx++].id = "aggre1"; -	res->clks[idx++].id = "noc_aggr"; -	res->clks[idx++].id = "noc_aggr_4"; -	res->clks[idx++].id = "noc_aggr_south_sf"; -	res->clks[idx++].id = "cnoc_qx"; -	res->clks[idx++].id = "sleep"; -	res->clks[idx++].id = "cnoc_sf_axi"; - -	num_opt_clks = idx - num_clks; -	res->num_clks = idx; - -	ret = devm_clk_bulk_get_optional(dev, num_opt_clks, res->clks + num_clks); -	if (ret < 0) -		return ret; +	res->num_clks = devm_clk_bulk_get_all(dev, &res->clks); +	if (res->num_clks < 0) { +		dev_err(dev, "Failed to get clocks\n"); +		return res->num_clks; +	}  	return 0;  } @@ -986,6 +945,12 @@ err_disable_regulators:  static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)  { +	const struct qcom_pcie_cfg *pcie_cfg = pcie->cfg; + +	if (pcie_cfg->override_no_snoop) +		writel(WR_NO_SNOOP_OVERIDE_EN | RD_NO_SNOOP_OVERIDE_EN, +				pcie->parf + PARF_NO_SNOOP_OVERIDE); +  	qcom_pcie_clear_aspm_l0s(pcie->pci);  	qcom_pcie_clear_hpc(pcie->pci); @@ -1101,17 +1066,12 @@ static int qcom_pcie_get_resources_2_9_0(struct qcom_pcie *pcie)  	struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0;  	struct dw_pcie *pci = pcie->pci;  	struct device *dev = pci->dev; -	int ret; -	res->clks[0].id = "iface"; -	res->clks[1].id = "axi_m"; -	res->clks[2].id = "axi_s"; -	res->clks[3].id = "axi_bridge"; -	res->clks[4].id = "rchng"; - -	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks); -	if (ret < 0) -		return ret; +	res->num_clks = devm_clk_bulk_get_all(dev, &res->clks); +	if (res->num_clks < 0) { +		dev_err(dev, "Failed to get clocks\n"); +		return res->num_clks; +	}  	res->rst = devm_reset_control_array_get_exclusive(dev);  	if (IS_ERR(res->rst)) @@ -1124,7 +1084,7 @@ static void qcom_pcie_deinit_2_9_0(struct qcom_pcie *pcie)  {  	struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0; -	clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks); +	clk_bulk_disable_unprepare(res->num_clks, res->clks);  }  static int qcom_pcie_init_2_9_0(struct qcom_pcie *pcie) @@ -1153,7 +1113,7 @@ static int qcom_pcie_init_2_9_0(struct qcom_pcie *pcie)  	usleep_range(2000, 2500); -	return clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); +	return clk_bulk_prepare_enable(res->num_clks, res->clks);  }  static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) @@ -1366,6 +1326,11 @@ static const struct qcom_pcie_cfg cfg_1_9_0 = {  	.ops = &ops_1_9_0,  }; +static const struct qcom_pcie_cfg cfg_1_34_0 = { +	.ops = &ops_1_9_0, +	.override_no_snoop = true, +}; +  static const struct qcom_pcie_cfg cfg_2_1_0 = {  	.ops = &ops_2_1_0,  }; @@ -1409,6 +1374,9 @@ static int qcom_pcie_icc_init(struct qcom_pcie *pcie)  	if (IS_ERR(pcie->icc_mem))  		return PTR_ERR(pcie->icc_mem); +	pcie->icc_cpu = devm_of_icc_get(pci->dev, "cpu-pcie"); +	if (IS_ERR(pcie->icc_cpu)) +		return PTR_ERR(pcie->icc_cpu);  	/*  	 * Some Qualcomm platforms require interconnect bandwidth constraints  	 * to be set before enabling interconnect clocks. @@ -1418,23 +1386,35 @@ static int qcom_pcie_icc_init(struct qcom_pcie *pcie)  	 */  	ret = icc_set_bw(pcie->icc_mem, 0, QCOM_PCIE_LINK_SPEED_TO_BW(1));  	if (ret) { -		dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", +		dev_err(pci->dev, "Failed to set bandwidth for PCIe-MEM interconnect path: %d\n", +			ret); +		return ret; +	} + +	/* +	 * Since the CPU-PCIe path is only used for activities like register +	 * access of the host controller and endpoint Config/BAR space access, +	 * HW team has recommended to use a minimal bandwidth of 1KBps just to +	 * keep the path active. +	 */ +	ret = icc_set_bw(pcie->icc_cpu, 0, kBps_to_icc(1)); +	if (ret) { +		dev_err(pci->dev, "Failed to set bandwidth for CPU-PCIe interconnect path: %d\n",  			ret); +		icc_set_bw(pcie->icc_mem, 0, 0);  		return ret;  	}  	return 0;  } -static void qcom_pcie_icc_update(struct qcom_pcie *pcie) +static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie)  { +	u32 offset, status, width, speed;  	struct dw_pcie *pci = pcie->pci; -	u32 offset, status; -	int speed, width; -	int ret; - -	if (!pcie->icc_mem) -		return; +	unsigned long freq_kbps; +	struct dev_pm_opp *opp; +	int ret, freq_mbps;  	offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);  	status = readw(pci->dbi_base + offset + PCI_EXP_LNKSTA); @@ -1446,10 +1426,28 @@ static void qcom_pcie_icc_update(struct qcom_pcie *pcie)  	speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status);  	width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status); -	ret = icc_set_bw(pcie->icc_mem, 0, width * QCOM_PCIE_LINK_SPEED_TO_BW(speed)); -	if (ret) { -		dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n", -			ret); +	if (pcie->icc_mem) { +		ret = icc_set_bw(pcie->icc_mem, 0, +				 width * QCOM_PCIE_LINK_SPEED_TO_BW(speed)); +		if (ret) { +			dev_err(pci->dev, "Failed to set bandwidth for PCIe-MEM interconnect path: %d\n", +				ret); +		} +	} else { +		freq_mbps = pcie_dev_speed_mbps(pcie_link_speed[speed]); +		if (freq_mbps < 0) +			return; + +		freq_kbps = freq_mbps * KILO; +		opp = dev_pm_opp_find_freq_exact(pci->dev, freq_kbps * width, +						 true); +		if (!IS_ERR(opp)) { +			ret = dev_pm_opp_set_opp(pci->dev, opp); +			if (ret) +				dev_err(pci->dev, "Failed to set OPP for freq (%lu): %d\n", +					freq_kbps * width, ret); +			dev_pm_opp_put(opp); +		}  	}  } @@ -1493,7 +1491,9 @@ static void qcom_pcie_init_debugfs(struct qcom_pcie *pcie)  static int qcom_pcie_probe(struct platform_device *pdev)  {  	const struct qcom_pcie_cfg *pcie_cfg; +	unsigned long max_freq = ULONG_MAX;  	struct device *dev = &pdev->dev; +	struct dev_pm_opp *opp;  	struct qcom_pcie *pcie;  	struct dw_pcie_rp *pp;  	struct resource *res; @@ -1561,9 +1561,43 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		goto err_pm_runtime_put;  	} -	ret = qcom_pcie_icc_init(pcie); -	if (ret) +	/* OPP table is optional */ +	ret = devm_pm_opp_of_add_table(dev); +	if (ret && ret != -ENODEV) { +		dev_err_probe(dev, ret, "Failed to add OPP table\n");  		goto err_pm_runtime_put; +	} + +	/* +	 * Before the PCIe link is initialized, vote for highest OPP in the OPP +	 * table, so that we are voting for maximum voltage corner for the +	 * link to come up in maximum supported speed. At the end of the +	 * probe(), OPP will be updated using qcom_pcie_icc_opp_update(). +	 */ +	if (!ret) { +		opp = dev_pm_opp_find_freq_floor(dev, &max_freq); +		if (IS_ERR(opp)) { +			ret = PTR_ERR(opp); +			dev_err_probe(pci->dev, ret, +				      "Unable to find max freq OPP\n"); +			goto err_pm_runtime_put; +		} else { +			ret = dev_pm_opp_set_opp(dev, opp); +		} + +		dev_pm_opp_put(opp); +		if (ret) { +			dev_err_probe(pci->dev, ret, +				      "Failed to set OPP for freq %lu\n", +				      max_freq); +			goto err_pm_runtime_put; +		} +	} else { +		/* Skip ICC init if OPP is supported as it is handled by OPP */ +		ret = qcom_pcie_icc_init(pcie); +		if (ret) +			goto err_pm_runtime_put; +	}  	ret = pcie->cfg->ops->get_resources(pcie);  	if (ret) @@ -1583,7 +1617,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		goto err_phy_exit;  	} -	qcom_pcie_icc_update(pcie); +	qcom_pcie_icc_opp_update(pcie);  	if (pcie->mhi)  		qcom_pcie_init_debugfs(pcie); @@ -1602,16 +1636,20 @@ err_pm_runtime_put:  static int qcom_pcie_suspend_noirq(struct device *dev)  {  	struct qcom_pcie *pcie = dev_get_drvdata(dev); -	int ret; +	int ret = 0;  	/*  	 * Set minimum bandwidth required to keep data path functional during  	 * suspend.  	 */ -	ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1)); -	if (ret) { -		dev_err(dev, "Failed to set interconnect bandwidth: %d\n", ret); -		return ret; +	if (pcie->icc_mem) { +		ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1)); +		if (ret) { +			dev_err(dev, +				"Failed to set bandwidth for PCIe-MEM interconnect path: %d\n", +				ret); +			return ret; +		}  	}  	/* @@ -1634,7 +1672,21 @@ static int qcom_pcie_suspend_noirq(struct device *dev)  		pcie->suspended = true;  	} -	return 0; +	/* +	 * Only disable CPU-PCIe interconnect path if the suspend is non-S2RAM. +	 * Because on some platforms, DBI access can happen very late during the +	 * S2RAM and a non-active CPU-PCIe interconnect path may lead to NoC +	 * error. +	 */ +	if (pm_suspend_target_state != PM_SUSPEND_MEM) { +		ret = icc_disable(pcie->icc_cpu); +		if (ret) +			dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", ret); + +		if (!pcie->icc_mem) +			dev_pm_opp_set_opp(pcie->pci->dev, NULL); +	} +	return ret;  }  static int qcom_pcie_resume_noirq(struct device *dev) @@ -1642,6 +1694,14 @@ static int qcom_pcie_resume_noirq(struct device *dev)  	struct qcom_pcie *pcie = dev_get_drvdata(dev);  	int ret; +	if (pm_suspend_target_state != PM_SUSPEND_MEM) { +		ret = icc_enable(pcie->icc_cpu); +		if (ret) { +			dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", ret); +			return ret; +		} +	} +  	if (pcie->suspended) {  		ret = qcom_pcie_host_init(&pcie->pci->pp);  		if (ret) @@ -1650,7 +1710,7 @@ static int qcom_pcie_resume_noirq(struct device *dev)  		pcie->suspended = false;  	} -	qcom_pcie_icc_update(pcie); +	qcom_pcie_icc_opp_update(pcie);  	return 0;  } @@ -1667,7 +1727,7 @@ static const struct of_device_id qcom_pcie_match[] = {  	{ .compatible = "qcom,pcie-msm8996", .data = &cfg_2_3_2 },  	{ .compatible = "qcom,pcie-qcs404", .data = &cfg_2_4_0 },  	{ .compatible = "qcom,pcie-sa8540p", .data = &cfg_sc8280xp }, -	{ .compatible = "qcom,pcie-sa8775p", .data = &cfg_1_9_0}, +	{ .compatible = "qcom,pcie-sa8775p", .data = &cfg_1_34_0},  	{ .compatible = "qcom,pcie-sc7280", .data = &cfg_1_9_0 },  	{ .compatible = "qcom,pcie-sc8180x", .data = &cfg_1_9_0 },  	{ .compatible = "qcom,pcie-sc8280xp", .data = &cfg_sc8280xp }, |