diff mbox series

pcie: qcom: Add support for sdm845 PCIe controller

Message ID 20190226070125.22318-1-bjorn.andersson@linaro.org (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show
Series pcie: qcom: Add support for sdm845 PCIe controller | expand

Commit Message

Bjorn Andersson Feb. 26, 2019, 7:01 a.m. UTC
The SDM845 has one Gen2 and one Gen3 controller, add support for these.

Due to lack of hardware only the Gen2 controller has been verified.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
 .../devicetree/bindings/pci/qcom,pcie.txt     |  19 +++
 drivers/pci/controller/dwc/pcie-qcom.c        | 146 ++++++++++++++++++
 2 files changed, 165 insertions(+)

Comments

Bjorn Helgaas Feb. 28, 2019, 8:39 p.m. UTC | #1
On Tue, Feb 26, 2019 at 1:00 AM Bjorn Andersson
<bjorn.andersson@linaro.org> wrote:
>
> The SDM845 has one Gen2 and one Gen3 controller, add support for these.
>
> Due to lack of hardware only the Gen2 controller has been verified.

Hi Bjorn,

I like your name :)

If you post a v2 for any reason, please update the subject like to:

  PCI: qcom: Add support for sdm845 PCIe controller

That way it matches the existing conventions and "git log --oneline
drivers/pci/controller/dwc/pcie-qcom.c" looks nice.  If there's no
need for a v2, Lorenzo will likely fix that up for you.

Bjorn
Stanimir Varbanov March 1, 2019, 12:53 p.m. UTC | #2
Hi Bjorn,

Thanks for the patch!

On 2/26/19 9:01 AM, Bjorn Andersson wrote:
> The SDM845 has one Gen2 and one Gen3 controller, add support for these.
> 
> Due to lack of hardware only the Gen2 controller has been verified.
> 
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> ---
>  .../devicetree/bindings/pci/qcom,pcie.txt     |  19 +++
>  drivers/pci/controller/dwc/pcie-qcom.c        | 146 ++++++++++++++++++
>  2 files changed, 165 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
> index 1fd703bd73e0..2cf92ed39499 100644
> --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt
> +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
> @@ -10,6 +10,7 @@
>  			- "qcom,pcie-msm8996" for msm8996 or apq8096
>  			- "qcom,pcie-ipq4019" for ipq4019
>  			- "qcom,pcie-ipq8074" for ipq8074
> +			- "qcom,pcie-sdm845" for sdm845
>  
>  - reg:
>  	Usage: required
> @@ -116,6 +117,18 @@
>  			- "ahb"		AHB clock
>  			- "aux"		Auxiliary clock
>  
> +- clock-names:
> +	Usage: required for sdm845
> +	Value type: <stringlist>
> +	Definition: Should contain the following entries
> +			- "aux"		Auxiliary clock
> +			- "cfg"		Configuration clock
> +			- "bus_master"	Master AXI clock
> +			- "bus_slave"	Slave AXI clock
> +			- "slave_q2a"	Slave Q2A clock

What means Q2A? It'd be nice to describe it.

> +			- "tbu"		PCIe TBU clock

Is TBU related to SMMU or to something else?

> +			- "pipe"	PIPE clock
> +
>  - resets:
>  	Usage: required
>  	Value type: <prop-encoded-array>
> @@ -167,6 +180,12 @@
>  			- "ahb"			AHB Reset
>  			- "axi_m_sticky"	AXI Master Sticky reset
>  
> +- reset-names:
> +	Usage: required for sdm845
> +	Value type: <stringlist>
> +	Definition: Should contain the following entries
> +			- "pci"			PCIe core reset
> +
>  - power-domains:
>  	Usage: required for apq8084 and msm8996/apq8096
>  	Value type: <prop-encoded-array>
> diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
> index d185ea5fe996..5147454a6ae5 100644
> --- a/drivers/pci/controller/dwc/pcie-qcom.c
> +++ b/drivers/pci/controller/dwc/pcie-qcom.c
> @@ -54,6 +54,7 @@
>  #define PCIE20_PARF_LTSSM			0x1B0
>  #define PCIE20_PARF_SID_OFFSET			0x234
>  #define PCIE20_PARF_BDF_TRANSLATE_CFG		0x24C
> +#define PCIE20_PARF_DEVICE_TYPE			0x1000
>  
>  #define PCIE20_ELBI_SYS_CTRL			0x04
>  #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE		BIT(0)
> @@ -80,6 +81,8 @@
>  #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE	0x358
>  #define SLV_ADDR_SPACE_SZ			0x10000000
>  
> +#define DEVICE_TYPE_RC				0x4
> +
>  #define QCOM_PCIE_2_1_0_MAX_SUPPLY	3
>  struct qcom_pcie_resources_2_1_0 {
>  	struct clk *iface_clk;
> @@ -139,12 +142,21 @@ struct qcom_pcie_resources_2_3_3 {
>  	struct reset_control *rst[7];
>  };
>  
> +struct qcom_pcie_resources_2_7_0 {
> +	struct clk_bulk_data clks[6];
> +	struct regulator_bulk_data supplies[2];
> +

please drop the blank line.

> +	struct reset_control *pci_reset;
> +	struct clk *pipe_clk;
> +};
> +
>  union qcom_pcie_resources {
>  	struct qcom_pcie_resources_1_0_0 v1_0_0;
>  	struct qcom_pcie_resources_2_1_0 v2_1_0;
>  	struct qcom_pcie_resources_2_3_2 v2_3_2;
>  	struct qcom_pcie_resources_2_3_3 v2_3_3;
>  	struct qcom_pcie_resources_2_4_0 v2_4_0;
> +	struct qcom_pcie_resources_2_7_0 v2_7_0;
>  };
>  
>  struct qcom_pcie;
> @@ -1076,6 +1088,129 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie)
>  	return ret;
>  }
>  
> +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;
> +	int ret;
> +
> +	res->pci_reset = devm_reset_control_get_exclusive(dev, "pci");
> +	if (IS_ERR(res->pci_reset))
> +		return PTR_ERR(res->pci_reset);
> +
> +	res->supplies[0].supply = "vdda";
> +	res->supplies[1].supply = "vddpe-3v3";
> +	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies),
> +				      res->supplies);
> +	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";
> +	res->clks[4].id = "slave_q2a";
> +	res->clks[5].id = "tbu";
> +
> +	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks);
> +	if (ret < 0)
> +		return ret;
> +
> +	res->pipe_clk = devm_clk_get(dev, "pipe");
> +	return PTR_ERR_OR_ZERO(res->pipe_clk);
> +}
> +
> +static int qcom_pcie_init_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;
> +	u32 val;
> +	int ret;
> +
> +	ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies);
> +	if (ret < 0) {
> +		dev_err(dev, "cannot enable regulators\n");
> +		return ret;
> +	}
> +
> +	ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks);
> +	if (ret < 0)

you could add dev_err() as well (to be aligned with
regulator_bulk_enable and reset_control_assert).

> +		goto err_disable_regulators;
> +
> +	ret = reset_control_assert(res->pci_reset);
> +	if (ret) {
> +		dev_err(dev, "cannot deassert pci reset\n");
> +		return ret;
> +	}
> +
> +	msleep(10);

for sleeping interval 10us - 20ms you should use usleep_range, please
fix it.

> +
> +	ret = reset_control_deassert(res->pci_reset);
> +	if (ret) {
> +		dev_err(dev, "cannot deassert pci reset\n");
> +		return ret;
> +	}
> +
> +	clk_prepare_enable(res->pipe_clk);

check for errors, please.

> +
> +	/* configure PCIe to RC mode */
> +	writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE);
> +
> +	/* enable PCIe clocks and resets */
> +	val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
> +	val &= ~BIT(0);
> +	writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
> +
> +	/* change DBI base address */
> +	writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
> +
> +	/* MAC PHY_POWERDOWN MUX DISABLE  */
> +	val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL);
> +	val &= ~BIT(29);
> +	writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL);
> +
> +	val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
> +	val |= BIT(4);
> +	writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
> +
> +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
> +		val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
> +		val |= BIT(31);
> +		writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
> +	}
> +
> +	return 0;
> +
> +err_disable_regulators:
> +	regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
> +
> +	return ret;
> +}
> +

<cut>
Bjorn Andersson March 1, 2019, 6:44 p.m. UTC | #3
On Fri 01 Mar 04:53 PST 2019, Stanimir Varbanov wrote:

> Hi Bjorn,
> 
> Thanks for the patch!
> 
> On 2/26/19 9:01 AM, Bjorn Andersson wrote:
> > The SDM845 has one Gen2 and one Gen3 controller, add support for these.
> > 
> > Due to lack of hardware only the Gen2 controller has been verified.
> > 
> > Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> > ---
> >  .../devicetree/bindings/pci/qcom,pcie.txt     |  19 +++
> >  drivers/pci/controller/dwc/pcie-qcom.c        | 146 ++++++++++++++++++
> >  2 files changed, 165 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
> > index 1fd703bd73e0..2cf92ed39499 100644
> > --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt
> > +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
> > @@ -10,6 +10,7 @@
> >  			- "qcom,pcie-msm8996" for msm8996 or apq8096
> >  			- "qcom,pcie-ipq4019" for ipq4019
> >  			- "qcom,pcie-ipq8074" for ipq8074
> > +			- "qcom,pcie-sdm845" for sdm845
> >  
> >  - reg:
> >  	Usage: required
> > @@ -116,6 +117,18 @@
> >  			- "ahb"		AHB clock
> >  			- "aux"		Auxiliary clock
> >  
> > +- clock-names:
> > +	Usage: required for sdm845
> > +	Value type: <stringlist>
> > +	Definition: Should contain the following entries
> > +			- "aux"		Auxiliary clock
> > +			- "cfg"		Configuration clock
> > +			- "bus_master"	Master AXI clock
> > +			- "bus_slave"	Slave AXI clock
> > +			- "slave_q2a"	Slave Q2A clock
> 
> What means Q2A? It'd be nice to describe it.
> 

I'll see what I can do.

> > +			- "tbu"		PCIe TBU clock
> 
> Is TBU related to SMMU or to something else?
> 

Yes, the ARM SMMU is split in a centralized controller (TCU) and
translation blocks (TBU) for each hardware peripheral. So this is the
clock for the translation block sitting between the two PCIe controllers
and the system NOC.

The clock is described here, rather than in the SMMU node in the
upstream way of representing client-related resources - although we
don't use the device_link to toggle it in the implementation below.

> > +			- "pipe"	PIPE clock
> > +
> >  - resets:
> >  	Usage: required
> >  	Value type: <prop-encoded-array>
> > @@ -167,6 +180,12 @@
> >  			- "ahb"			AHB Reset
> >  			- "axi_m_sticky"	AXI Master Sticky reset
> >  
> > +- reset-names:
> > +	Usage: required for sdm845
> > +	Value type: <stringlist>
> > +	Definition: Should contain the following entries
> > +			- "pci"			PCIe core reset
> > +
> >  - power-domains:
> >  	Usage: required for apq8084 and msm8996/apq8096
> >  	Value type: <prop-encoded-array>
> > diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
> > index d185ea5fe996..5147454a6ae5 100644
> > --- a/drivers/pci/controller/dwc/pcie-qcom.c
> > +++ b/drivers/pci/controller/dwc/pcie-qcom.c
> > @@ -54,6 +54,7 @@
> >  #define PCIE20_PARF_LTSSM			0x1B0
> >  #define PCIE20_PARF_SID_OFFSET			0x234
> >  #define PCIE20_PARF_BDF_TRANSLATE_CFG		0x24C
> > +#define PCIE20_PARF_DEVICE_TYPE			0x1000
> >  
> >  #define PCIE20_ELBI_SYS_CTRL			0x04
> >  #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE		BIT(0)
> > @@ -80,6 +81,8 @@
> >  #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE	0x358
> >  #define SLV_ADDR_SPACE_SZ			0x10000000
> >  
> > +#define DEVICE_TYPE_RC				0x4
> > +
> >  #define QCOM_PCIE_2_1_0_MAX_SUPPLY	3
> >  struct qcom_pcie_resources_2_1_0 {
> >  	struct clk *iface_clk;
> > @@ -139,12 +142,21 @@ struct qcom_pcie_resources_2_3_3 {
> >  	struct reset_control *rst[7];
> >  };
> >  
> > +struct qcom_pcie_resources_2_7_0 {
> > +	struct clk_bulk_data clks[6];
> > +	struct regulator_bulk_data supplies[2];
> > +
> 
> please drop the blank line.
> 

Sure thing, and I'll throw in some defines for the two 2s, per your
feedback on the pending QCS404 patch.

> > +	struct reset_control *pci_reset;
> > +	struct clk *pipe_clk;
> > +};
> > +
> >  union qcom_pcie_resources {
> >  	struct qcom_pcie_resources_1_0_0 v1_0_0;
> >  	struct qcom_pcie_resources_2_1_0 v2_1_0;
> >  	struct qcom_pcie_resources_2_3_2 v2_3_2;
> >  	struct qcom_pcie_resources_2_3_3 v2_3_3;
> >  	struct qcom_pcie_resources_2_4_0 v2_4_0;
> > +	struct qcom_pcie_resources_2_7_0 v2_7_0;
> >  };
> >  
> >  struct qcom_pcie;
> > @@ -1076,6 +1088,129 @@ static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie)
> >  	return ret;
> >  }
> >  
> > +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;
> > +	int ret;
> > +
> > +	res->pci_reset = devm_reset_control_get_exclusive(dev, "pci");
> > +	if (IS_ERR(res->pci_reset))
> > +		return PTR_ERR(res->pci_reset);
> > +
> > +	res->supplies[0].supply = "vdda";
> > +	res->supplies[1].supply = "vddpe-3v3";
> > +	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies),
> > +				      res->supplies);
> > +	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";
> > +	res->clks[4].id = "slave_q2a";
> > +	res->clks[5].id = "tbu";
> > +
> > +	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	res->pipe_clk = devm_clk_get(dev, "pipe");
> > +	return PTR_ERR_OR_ZERO(res->pipe_clk);
> > +}
> > +
> > +static int qcom_pcie_init_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;
> > +	u32 val;
> > +	int ret;
> > +
> > +	ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies);
> > +	if (ret < 0) {
> > +		dev_err(dev, "cannot enable regulators\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks);
> > +	if (ret < 0)
> 
> you could add dev_err() as well (to be aligned with
> regulator_bulk_enable and reset_control_assert).
> 

clk_bulk_prepare_enable() will print an error indicating which of the
multiple clocks that failed to turn on, so I don't think adding another
- more generic - error line will add value.


Looking again shows that I missed that regulator_bulk_enable() does the
same thing, so I will align the two by removing above error print.

> > +		goto err_disable_regulators;
> > +
> > +	ret = reset_control_assert(res->pci_reset);
> > +	if (ret) {
> > +		dev_err(dev, "cannot deassert pci reset\n");
> > +		return ret;
> > +	}
> > +
> > +	msleep(10);
> 
> for sleeping interval 10us - 20ms you should use usleep_range, please
> fix it.
> 

10ms looks arbitrary as well, I'll review this.

> > +
> > +	ret = reset_control_deassert(res->pci_reset);
> > +	if (ret) {
> > +		dev_err(dev, "cannot deassert pci reset\n");
> > +		return ret;
> > +	}
> > +
> > +	clk_prepare_enable(res->pipe_clk);
> 
> check for errors, please.
> 

Of course.

> > +
> > +	/* configure PCIe to RC mode */
> > +	writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE);
> > +
> > +	/* enable PCIe clocks and resets */
> > +	val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
> > +	val &= ~BIT(0);
> > +	writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
> > +
> > +	/* change DBI base address */
> > +	writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
> > +
> > +	/* MAC PHY_POWERDOWN MUX DISABLE  */
> > +	val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL);
> > +	val &= ~BIT(29);
> > +	writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL);
> > +
> > +	val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
> > +	val |= BIT(4);
> > +	writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
> > +
> > +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
> > +		val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
> > +		val |= BIT(31);
> > +		writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
> > +	}
> > +
> > +	return 0;
> > +
> > +err_disable_regulators:
> > +	regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
> > +
> > +	return ret;
> > +}
> > +
> 

Thanks for your review!

Regards,
Bjorn
diff mbox series

Patch

diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
index 1fd703bd73e0..2cf92ed39499 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt
@@ -10,6 +10,7 @@ 
 			- "qcom,pcie-msm8996" for msm8996 or apq8096
 			- "qcom,pcie-ipq4019" for ipq4019
 			- "qcom,pcie-ipq8074" for ipq8074
+			- "qcom,pcie-sdm845" for sdm845
 
 - reg:
 	Usage: required
@@ -116,6 +117,18 @@ 
 			- "ahb"		AHB clock
 			- "aux"		Auxiliary clock
 
+- clock-names:
+	Usage: required for sdm845
+	Value type: <stringlist>
+	Definition: Should contain the following entries
+			- "aux"		Auxiliary clock
+			- "cfg"		Configuration clock
+			- "bus_master"	Master AXI clock
+			- "bus_slave"	Slave AXI clock
+			- "slave_q2a"	Slave Q2A clock
+			- "tbu"		PCIe TBU clock
+			- "pipe"	PIPE clock
+
 - resets:
 	Usage: required
 	Value type: <prop-encoded-array>
@@ -167,6 +180,12 @@ 
 			- "ahb"			AHB Reset
 			- "axi_m_sticky"	AXI Master Sticky reset
 
+- reset-names:
+	Usage: required for sdm845
+	Value type: <stringlist>
+	Definition: Should contain the following entries
+			- "pci"			PCIe core reset
+
 - power-domains:
 	Usage: required for apq8084 and msm8996/apq8096
 	Value type: <prop-encoded-array>
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index d185ea5fe996..5147454a6ae5 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -54,6 +54,7 @@ 
 #define PCIE20_PARF_LTSSM			0x1B0
 #define PCIE20_PARF_SID_OFFSET			0x234
 #define PCIE20_PARF_BDF_TRANSLATE_CFG		0x24C
+#define PCIE20_PARF_DEVICE_TYPE			0x1000
 
 #define PCIE20_ELBI_SYS_CTRL			0x04
 #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE		BIT(0)
@@ -80,6 +81,8 @@ 
 #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE	0x358
 #define SLV_ADDR_SPACE_SZ			0x10000000
 
+#define DEVICE_TYPE_RC				0x4
+
 #define QCOM_PCIE_2_1_0_MAX_SUPPLY	3
 struct qcom_pcie_resources_2_1_0 {
 	struct clk *iface_clk;
@@ -139,12 +142,21 @@  struct qcom_pcie_resources_2_3_3 {
 	struct reset_control *rst[7];
 };
 
+struct qcom_pcie_resources_2_7_0 {
+	struct clk_bulk_data clks[6];
+	struct regulator_bulk_data supplies[2];
+
+	struct reset_control *pci_reset;
+	struct clk *pipe_clk;
+};
+
 union qcom_pcie_resources {
 	struct qcom_pcie_resources_1_0_0 v1_0_0;
 	struct qcom_pcie_resources_2_1_0 v2_1_0;
 	struct qcom_pcie_resources_2_3_2 v2_3_2;
 	struct qcom_pcie_resources_2_3_3 v2_3_3;
 	struct qcom_pcie_resources_2_4_0 v2_4_0;
+	struct qcom_pcie_resources_2_7_0 v2_7_0;
 };
 
 struct qcom_pcie;
@@ -1076,6 +1088,129 @@  static int qcom_pcie_init_2_3_3(struct qcom_pcie *pcie)
 	return ret;
 }
 
+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;
+	int ret;
+
+	res->pci_reset = devm_reset_control_get_exclusive(dev, "pci");
+	if (IS_ERR(res->pci_reset))
+		return PTR_ERR(res->pci_reset);
+
+	res->supplies[0].supply = "vdda";
+	res->supplies[1].supply = "vddpe-3v3";
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies),
+				      res->supplies);
+	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";
+	res->clks[4].id = "slave_q2a";
+	res->clks[5].id = "tbu";
+
+	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks);
+	if (ret < 0)
+		return ret;
+
+	res->pipe_clk = devm_clk_get(dev, "pipe");
+	return PTR_ERR_OR_ZERO(res->pipe_clk);
+}
+
+static int qcom_pcie_init_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;
+	u32 val;
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies);
+	if (ret < 0) {
+		dev_err(dev, "cannot enable regulators\n");
+		return ret;
+	}
+
+	ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks);
+	if (ret < 0)
+		goto err_disable_regulators;
+
+	ret = reset_control_assert(res->pci_reset);
+	if (ret) {
+		dev_err(dev, "cannot deassert pci reset\n");
+		return ret;
+	}
+
+	msleep(10);
+
+	ret = reset_control_deassert(res->pci_reset);
+	if (ret) {
+		dev_err(dev, "cannot deassert pci reset\n");
+		return ret;
+	}
+
+	clk_prepare_enable(res->pipe_clk);
+
+	/* configure PCIe to RC mode */
+	writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE);
+
+	/* enable PCIe clocks and resets */
+	val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
+	val &= ~BIT(0);
+	writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
+
+	/* change DBI base address */
+	writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
+
+	/* MAC PHY_POWERDOWN MUX DISABLE  */
+	val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL);
+	val &= ~BIT(29);
+	writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL);
+
+	val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
+	val |= BIT(4);
+	writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
+
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
+		val |= BIT(31);
+		writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
+	}
+
+	return 0;
+
+err_disable_regulators:
+	regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
+
+	return ret;
+}
+
+static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
+{
+	struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
+
+	clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks);
+	regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
+}
+
+static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)
+{
+	struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
+
+	return clk_prepare_enable(res->pipe_clk);
+}
+
+static void qcom_pcie_post_deinit_2_7_0(struct qcom_pcie *pcie)
+{
+	struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
+
+	clk_disable_unprepare(res->pipe_clk);
+}
+
 static int qcom_pcie_link_up(struct dw_pcie *pci)
 {
 	u16 val = readw(pci->dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA);
@@ -1192,6 +1327,16 @@  static const struct qcom_pcie_ops ops_2_3_3 = {
 	.ltssm_enable = qcom_pcie_2_3_2_ltssm_enable,
 };
 
+/* Qcom IP rev.: 2.7.0	Synopsys IP rev.: 4.30a */
+static const struct qcom_pcie_ops ops_2_7_0 = {
+	.get_resources = qcom_pcie_get_resources_2_7_0,
+	.init = qcom_pcie_init_2_7_0,
+	.deinit = qcom_pcie_deinit_2_7_0,
+	.ltssm_enable = qcom_pcie_2_3_2_ltssm_enable,
+	.post_init = qcom_pcie_post_init_2_7_0,
+	.post_deinit = qcom_pcie_post_deinit_2_7_0,
+};
+
 static const struct dw_pcie_ops dw_pcie_ops = {
 	.link_up = qcom_pcie_link_up,
 };
@@ -1306,6 +1451,7 @@  static const struct of_device_id qcom_pcie_match[] = {
 	{ .compatible = "qcom,pcie-msm8996", .data = &ops_2_3_2 },
 	{ .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 },
 	{ .compatible = "qcom,pcie-ipq4019", .data = &ops_2_4_0 },
+	{ .compatible = "qcom,pcie-sdm845", .data = &ops_2_7_0 },
 	{ }
 };