diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-qcom.c')
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 198 | 
1 files changed, 121 insertions, 77 deletions
| diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 138e1a2d21cc..3aac77a295ba 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -27,6 +27,7 @@  #include <linux/slab.h>  #include <linux/types.h> +#include "../../pci.h"  #include "pcie-designware.h"  #define PCIE20_PARF_SYS_CTRL			0x00 @@ -39,13 +40,14 @@  #define L23_CLK_RMV_DIS				BIT(2)  #define L1_CLK_RMV_DIS				BIT(1) -#define PCIE20_COMMAND_STATUS			0x04 -#define CMD_BME_VAL				0x4 -#define PCIE20_DEVICE_CONTROL2_STATUS2		0x98 -#define PCIE_CAP_CPL_TIMEOUT_DISABLE		0x10 -  #define PCIE20_PARF_PHY_CTRL			0x40 +#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK	GENMASK(20, 16) +#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x)		((x) << 16) +  #define PCIE20_PARF_PHY_REFCLK			0x4C +#define PHY_REFCLK_SSP_EN			BIT(16) +#define PHY_REFCLK_USE_PAD			BIT(12) +  #define PCIE20_PARF_DBI_BASE_ADDR		0x168  #define PCIE20_PARF_SLV_ADDR_SPACE_SIZE		0x16C  #define PCIE20_PARF_MHI_CLOCK_RESET_CTRL	0x174 @@ -66,8 +68,8 @@  #define CFG_BRIDGE_SB_INIT			BIT(0)  #define PCIE20_CAP				0x70 -#define PCIE20_CAP_LINK_CAPABILITIES		(PCIE20_CAP + 0xC) -#define PCIE20_CAP_ACTIVE_STATE_LINK_PM_SUPPORT	(BIT(10) | BIT(11)) +#define PCIE20_DEVICE_CONTROL2_STATUS2		(PCIE20_CAP + PCI_EXP_DEVCTL2) +#define PCIE20_CAP_LINK_CAPABILITIES		(PCIE20_CAP + PCI_EXP_LNKCAP)  #define PCIE20_CAP_LINK_1			(PCIE20_CAP + 0x14)  #define PCIE_CAP_LINK1_VAL			0x2FD7F @@ -77,22 +79,36 @@  #define DBI_RO_WR_EN				1  #define PERST_DELAY_US				1000 +/* PARF registers */ +#define PCIE20_PARF_PCS_DEEMPH			0x34 +#define PCS_DEEMPH_TX_DEEMPH_GEN1(x)		((x) << 16) +#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x)	((x) << 8) +#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x)	((x) << 0) + +#define PCIE20_PARF_PCS_SWING			0x38 +#define PCS_SWING_TX_SWING_FULL(x)		((x) << 8) +#define PCS_SWING_TX_SWING_LOW(x)		((x) << 0) + +#define PCIE20_PARF_CONFIG_BITS		0x50 +#define PHY_RX0_EQ(x)				((x) << 24)  #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE	0x358  #define SLV_ADDR_SPACE_SZ			0x10000000 +#define PCIE20_LNK_CONTROL2_LINK_STATUS2	0xa0 +  #define DEVICE_TYPE_RC				0x4  #define QCOM_PCIE_2_1_0_MAX_SUPPLY	3 +#define QCOM_PCIE_2_1_0_MAX_CLOCKS	5  struct qcom_pcie_resources_2_1_0 { -	struct clk *iface_clk; -	struct clk *core_clk; -	struct clk *phy_clk; +	struct clk_bulk_data clks[QCOM_PCIE_2_1_0_MAX_CLOCKS];  	struct reset_control *pci_reset;  	struct reset_control *axi_reset;  	struct reset_control *ahb_reset;  	struct reset_control *por_reset;  	struct reset_control *phy_reset; +	struct reset_control *ext_reset;  	struct regulator_bulk_data supplies[QCOM_PCIE_2_1_0_MAX_SUPPLY];  }; @@ -177,6 +193,7 @@ struct qcom_pcie {  	struct phy *phy;  	struct gpio_desc *reset;  	const struct qcom_pcie_ops *ops; +	int gen;  };  #define to_qcom_pcie(x)		dev_get_drvdata((x)->dev) @@ -234,17 +251,21 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie)  	if (ret)  		return ret; -	res->iface_clk = devm_clk_get(dev, "iface"); -	if (IS_ERR(res->iface_clk)) -		return PTR_ERR(res->iface_clk); +	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"; -	res->core_clk = devm_clk_get(dev, "core"); -	if (IS_ERR(res->core_clk)) -		return PTR_ERR(res->core_clk); +	/* iface, core, phy are required */ +	ret = devm_clk_bulk_get(dev, 3, res->clks); +	if (ret < 0) +		return ret; -	res->phy_clk = devm_clk_get(dev, "phy"); -	if (IS_ERR(res->phy_clk)) -		return PTR_ERR(res->phy_clk); +	/* aux, ref are optional */ +	ret = devm_clk_bulk_get_optional(dev, 2, res->clks + 3); +	if (ret < 0) +		return ret;  	res->pci_reset = devm_reset_control_get_exclusive(dev, "pci");  	if (IS_ERR(res->pci_reset)) @@ -262,6 +283,10 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie)  	if (IS_ERR(res->por_reset))  		return PTR_ERR(res->por_reset); +	res->ext_reset = devm_reset_control_get_optional_exclusive(dev, "ext"); +	if (IS_ERR(res->ext_reset)) +		return PTR_ERR(res->ext_reset); +  	res->phy_reset = devm_reset_control_get_exclusive(dev, "phy");  	return PTR_ERR_OR_ZERO(res->phy_reset);  } @@ -270,14 +295,13 @@ 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);  	reset_control_assert(res->pci_reset);  	reset_control_assert(res->axi_reset);  	reset_control_assert(res->ahb_reset);  	reset_control_assert(res->por_reset); -	reset_control_assert(res->pci_reset); -	clk_disable_unprepare(res->iface_clk); -	clk_disable_unprepare(res->core_clk); -	clk_disable_unprepare(res->phy_clk); +	reset_control_assert(res->ext_reset); +	reset_control_assert(res->phy_reset);  	regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);  } @@ -286,6 +310,7 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie)  	struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0;  	struct dw_pcie *pci = pcie->pci;  	struct device *dev = pci->dev; +	struct device_node *node = dev->of_node;  	u32 val;  	int ret; @@ -295,73 +320,85 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie)  		return ret;  	} -	ret = reset_control_assert(res->ahb_reset); -	if (ret) { -		dev_err(dev, "cannot assert ahb reset\n"); -		goto err_assert_ahb; -	} - -	ret = clk_prepare_enable(res->iface_clk); -	if (ret) { -		dev_err(dev, "cannot prepare/enable iface clock\n"); -		goto err_assert_ahb; -	} - -	ret = clk_prepare_enable(res->phy_clk); -	if (ret) { -		dev_err(dev, "cannot prepare/enable phy clock\n"); -		goto err_clk_phy; -	} - -	ret = clk_prepare_enable(res->core_clk); -	if (ret) { -		dev_err(dev, "cannot prepare/enable core clock\n"); -		goto err_clk_core; -	} -  	ret = reset_control_deassert(res->ahb_reset);  	if (ret) {  		dev_err(dev, "cannot deassert ahb reset\n");  		goto err_deassert_ahb;  	} -	/* enable PCIe clocks and resets */ -	val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); -	val &= ~BIT(0); -	writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); - -	/* enable external reference clock */ -	val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); -	val |= BIT(16); -	writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); +	ret = reset_control_deassert(res->ext_reset); +	if (ret) { +		dev_err(dev, "cannot deassert ext reset\n"); +		goto err_deassert_ext; +	}  	ret = reset_control_deassert(res->phy_reset);  	if (ret) {  		dev_err(dev, "cannot deassert phy reset\n"); -		return ret; +		goto err_deassert_phy;  	}  	ret = reset_control_deassert(res->pci_reset);  	if (ret) {  		dev_err(dev, "cannot deassert pci reset\n"); -		return ret; +		goto err_deassert_pci;  	}  	ret = reset_control_deassert(res->por_reset);  	if (ret) {  		dev_err(dev, "cannot deassert por reset\n"); -		return ret; +		goto err_deassert_por;  	}  	ret = reset_control_deassert(res->axi_reset);  	if (ret) {  		dev_err(dev, "cannot deassert axi reset\n"); -		return ret; +		goto err_deassert_axi;  	} +	ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks); +	if (ret) +		goto err_clks; + +	/* enable PCIe clocks and resets */ +	val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); +	val &= ~BIT(0); +	writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); + +	if (of_device_is_compatible(node, "qcom,pcie-ipq8064") || +	    of_device_is_compatible(node, "qcom,pcie-ipq8064-v2")) { +		writel(PCS_DEEMPH_TX_DEEMPH_GEN1(24) | +			       PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(24) | +			       PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(34), +		       pcie->parf + PCIE20_PARF_PCS_DEEMPH); +		writel(PCS_SWING_TX_SWING_FULL(120) | +			       PCS_SWING_TX_SWING_LOW(120), +		       pcie->parf + PCIE20_PARF_PCS_SWING); +		writel(PHY_RX0_EQ(4), pcie->parf + PCIE20_PARF_CONFIG_BITS); +	} + +	if (of_device_is_compatible(node, "qcom,pcie-ipq8064")) { +		/* set TX termination offset */ +		val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); +		val &= ~PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK; +		val |= PHY_CTRL_PHY_TX0_TERM_OFFSET(7); +		writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL); +	} + +	/* enable external reference clock */ +	val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK); +	val &= ~PHY_REFCLK_USE_PAD; +	val |= PHY_REFCLK_SSP_EN; +	writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK); +  	/* wait for clock acquisition */  	usleep_range(1000, 1500); +	if (pcie->gen == 1) { +		val = readl(pci->dbi_base + PCIE20_LNK_CONTROL2_LINK_STATUS2); +		val |= PCI_EXP_LNKSTA_CLS_2_5GB; +		writel(val, pci->dbi_base + PCIE20_LNK_CONTROL2_LINK_STATUS2); +	}  	/* Set the Max TLP size to 2K, instead of using default of 4K */  	writel(CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K, @@ -371,13 +408,19 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie)  	return 0; +err_clks: +	reset_control_assert(res->axi_reset); +err_deassert_axi: +	reset_control_assert(res->por_reset); +err_deassert_por: +	reset_control_assert(res->pci_reset); +err_deassert_pci: +	reset_control_assert(res->phy_reset); +err_deassert_phy: +	reset_control_assert(res->ext_reset); +err_deassert_ext: +	reset_control_assert(res->ahb_reset);  err_deassert_ahb: -	clk_disable_unprepare(res->core_clk); -err_clk_core: -	clk_disable_unprepare(res->phy_clk); -err_clk_phy: -	clk_disable_unprepare(res->iface_clk); -err_assert_ahb:  	regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);  	return ret; @@ -1047,15 +1090,15 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie)  		pcie->parf + PCIE20_PARF_SYS_CTRL);  	writel(0, pcie->parf + PCIE20_PARF_Q2A_FLUSH); -	writel(CMD_BME_VAL, pci->dbi_base + PCIE20_COMMAND_STATUS); +	writel(PCI_COMMAND_MASTER, pci->dbi_base + PCI_COMMAND);  	writel(DBI_RO_WR_EN, pci->dbi_base + PCIE20_MISC_CONTROL_1_REG);  	writel(PCIE_CAP_LINK1_VAL, pci->dbi_base + PCIE20_CAP_LINK_1);  	val = readl(pci->dbi_base + PCIE20_CAP_LINK_CAPABILITIES); -	val &= ~PCIE20_CAP_ACTIVE_STATE_LINK_PM_SUPPORT; +	val &= ~PCI_EXP_LNKCAP_ASPMS;  	writel(val, pci->dbi_base + PCIE20_CAP_LINK_CAPABILITIES); -	writel(PCIE_CAP_CPL_TIMEOUT_DISABLE, pci->dbi_base + +	writel(PCI_EXP_DEVCTL2_COMP_TMOUT_DIS, pci->dbi_base +  		PCIE20_DEVICE_CONTROL2_STATUS2);  	return 0; @@ -1339,10 +1382,8 @@ static int qcom_pcie_probe(struct platform_device *pdev)  	pm_runtime_enable(dev);  	ret = pm_runtime_get_sync(dev); -	if (ret < 0) { -		pm_runtime_disable(dev); -		return ret; -	} +	if (ret < 0) +		goto err_pm_runtime_put;  	pci->dev = dev;  	pci->ops = &dw_pcie_ops; @@ -1358,8 +1399,11 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		goto err_pm_runtime_put;  	} -	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); -	pcie->parf = devm_ioremap_resource(dev, res); +	pcie->gen = of_pci_get_max_link_speed(pdev->dev.of_node); +	if (pcie->gen < 0) +		pcie->gen = 2; + +	pcie->parf = devm_platform_ioremap_resource_byname(pdev, "parf");  	if (IS_ERR(pcie->parf)) {  		ret = PTR_ERR(pcie->parf);  		goto err_pm_runtime_put; @@ -1372,8 +1416,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)  		goto err_pm_runtime_put;  	} -	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); -	pcie->elbi = devm_ioremap_resource(dev, res); +	pcie->elbi = devm_platform_ioremap_resource_byname(pdev, "elbi");  	if (IS_ERR(pcie->elbi)) {  		ret = PTR_ERR(pcie->elbi);  		goto err_pm_runtime_put; @@ -1426,6 +1469,7 @@ err_pm_runtime_put:  static const struct of_device_id qcom_pcie_match[] = {  	{ .compatible = "qcom,pcie-apq8084", .data = &ops_1_0_0 },  	{ .compatible = "qcom,pcie-ipq8064", .data = &ops_2_1_0 }, +	{ .compatible = "qcom,pcie-ipq8064-v2", .data = &ops_2_1_0 },  	{ .compatible = "qcom,pcie-apq8064", .data = &ops_2_1_0 },  	{ .compatible = "qcom,pcie-msm8996", .data = &ops_2_3_2 },  	{ .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 }, |