--- /dev/null
+Index: linux-4.9.20/drivers/pci/host/pcie-qcom.c
+===================================================================
+--- linux-4.9.20.orig/drivers/pci/host/pcie-qcom.c
++++ linux-4.9.20/drivers/pci/host/pcie-qcom.c
+@@ -36,53 +36,17 @@
+
+ #include "pcie-designware.h"
+
+-/* DBI registers */
+-#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818
+-#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c
+-
+-#define PCIE20_PLR_IATU_VIEWPORT 0x900
+-#define PCIE20_PLR_IATU_REGION_OUTBOUND (0x0 << 31)
+-#define PCIE20_PLR_IATU_REGION_INDEX(x) (x << 0)
+-
+-#define PCIE20_PLR_IATU_CTRL1 0x904
+-#define PCIE20_PLR_IATU_TYPE_CFG0 (0x4 << 0)
+-#define PCIE20_PLR_IATU_TYPE_MEM (0x0 << 0)
+-
+-#define PCIE20_PLR_IATU_CTRL2 0x908
+-#define PCIE20_PLR_IATU_ENABLE BIT(31)
+-
+-#define PCIE20_PLR_IATU_LBAR 0x90C
+-#define PCIE20_PLR_IATU_UBAR 0x910
+-#define PCIE20_PLR_IATU_LAR 0x914
+-#define PCIE20_PLR_IATU_LTAR 0x918
+-#define PCIE20_PLR_IATU_UTAR 0x91c
+-
+-#define MSM_PCIE_DEV_CFG_ADDR 0x01000000
+-
+-/* 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_SYS_CTRL 0x00
+ #define PCIE20_PARF_PHY_CTRL 0x40
+-#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK (0x1f << 16)
+-#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) (x << 16)
+-
+ #define PCIE20_PARF_PHY_REFCLK 0x4C
+-#define REF_SSP_EN BIT(16)
+-#define REF_USE_PAD BIT(12)
+-
+-#define PCIE20_PARF_CONFIG_BITS 0x50
+-#define PHY_RX0_EQ(x) (x << 24)
+-
+ #define PCIE20_PARF_DBI_BASE_ADDR 0x168
+-#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
++#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C
++#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174
+ #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
++#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8
++#define PCIE20_PARF_LTSSM 0x1B0
++#define PCIE20_PARF_SID_OFFSET 0x234
++#define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C
+
+ #define PCIE20_ELBI_SYS_CTRL 0x04
+ #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0)
+@@ -95,18 +59,14 @@ struct qcom_pcie_resources_v0 {
+ struct clk *iface_clk;
+ struct clk *core_clk;
+ struct clk *phy_clk;
+- struct clk *aux_clk;
+- struct clk *ref_clk;
+ 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 *vdda;
+ struct regulator *vdda_phy;
+ struct regulator *vdda_refclk;
+- uint8_t phy_tx0_term_offset;
+ };
+
+ struct qcom_pcie_resources_v1 {
+@@ -118,9 +78,40 @@ struct qcom_pcie_resources_v1 {
+ struct regulator *vdda;
+ };
+
++struct qcom_pcie_resources_v2 {
++ struct clk *aux_clk;
++ struct clk *master_clk;
++ struct clk *slave_clk;
++ struct clk *cfg_clk;
++ struct clk *pipe_clk;
++};
++
++struct qcom_pcie_resources_v3 {
++ struct clk *ahb_clk;
++ struct clk *axi_m_clk;
++ struct clk *axi_s_clk;
++ struct reset_control *axi_m_reset;
++ struct reset_control *axi_s_reset;
++ struct reset_control *pipe_reset;
++ struct reset_control *axi_m_vmid_reset;
++ struct reset_control *axi_s_xpu_reset;
++ struct reset_control *parf_reset;
++ struct reset_control *phy_reset;
++ struct reset_control *axi_m_sticky_reset;
++ struct reset_control *pipe_sticky_reset;
++ struct reset_control *pwr_reset;
++ struct reset_control *ahb_reset;
++ struct reset_control *phy_ahb_reset;
++ struct regulator *vdda;
++ struct regulator *vdda_phy;
++ struct regulator *vdda_refclk;
++};
++
+ union qcom_pcie_resources {
+ struct qcom_pcie_resources_v0 v0;
+ struct qcom_pcie_resources_v1 v1;
++ struct qcom_pcie_resources_v2 v2;
++ struct qcom_pcie_resources_v3 v3;
+ };
+
+ struct qcom_pcie;
+@@ -128,8 +119,9 @@ struct qcom_pcie;
+ struct qcom_pcie_ops {
+ int (*get_resources)(struct qcom_pcie *pcie);
+ int (*init)(struct qcom_pcie *pcie);
+- void (*configure)(struct qcom_pcie *pcie);
++ int (*post_init)(struct qcom_pcie *pcie);
+ void (*deinit)(struct qcom_pcie *pcie);
++ void (*ltssm_enable)(struct qcom_pcie *pcie);
+ };
+
+ struct qcom_pcie {
+@@ -163,17 +155,35 @@ static irqreturn_t qcom_pcie_msi_irq_han
+ return dw_handle_msi_irq(pp);
+ }
+
+-static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
++static void qcom_pcie_v0_v1_ltssm_enable(struct qcom_pcie *pcie)
+ {
+ u32 val;
+
+- if (dw_pcie_link_up(&pcie->pp))
+- return 0;
+-
+ /* enable link training */
+ val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL);
+ val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE;
+ writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL);
++}
++
++static void qcom_pcie_v2_ltssm_enable(struct qcom_pcie *pcie)
++{
++ u32 val;
++
++ /* enable link training */
++ val = readl(pcie->parf + PCIE20_PARF_LTSSM);
++ val |= BIT(8);
++ writel(val, pcie->parf + PCIE20_PARF_LTSSM);
++}
++
++static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
++{
++
++ if (dw_pcie_link_up(&pcie->pp))
++ return 0;
++
++ /* Enable Link Training state machine */
++ if (pcie->ops->ltssm_enable)
++ pcie->ops->ltssm_enable(pcie);
+
+ return dw_pcie_wait_for_link(&pcie->pp);
+ }
+@@ -207,14 +217,6 @@ static int qcom_pcie_get_resources_v0(st
+ if (IS_ERR(res->phy_clk))
+ return PTR_ERR(res->phy_clk);
+
+- res->aux_clk = devm_clk_get(dev, "aux");
+- if (IS_ERR(res->aux_clk))
+- return PTR_ERR(res->aux_clk);
+-
+- res->ref_clk = devm_clk_get(dev, "ref");
+- if (IS_ERR(res->ref_clk))
+- return PTR_ERR(res->ref_clk);
+-
+ res->pci_reset = devm_reset_control_get(dev, "pci");
+ if (IS_ERR(res->pci_reset))
+ return PTR_ERR(res->pci_reset);
+@@ -235,14 +237,6 @@ static int qcom_pcie_get_resources_v0(st
+ if (IS_ERR(res->phy_reset))
+ return PTR_ERR(res->phy_reset);
+
+- res->ext_reset = devm_reset_control_get(dev, "ext");
+- if (IS_ERR(res->ext_reset))
+- return PTR_ERR(res->ext_reset);
+-
+- if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset",
+- &res->phy_tx0_term_offset))
+- res->phy_tx0_term_offset = 0;
+-
+ return 0;
+ }
+
+@@ -286,69 +280,15 @@ static void qcom_pcie_deinit_v0(struct q
+ reset_control_assert(res->axi_reset);
+ reset_control_assert(res->ahb_reset);
+ reset_control_assert(res->por_reset);
+- reset_control_assert(res->phy_reset);
+- reset_control_assert(res->ext_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);
+- clk_disable_unprepare(res->aux_clk);
+- clk_disable_unprepare(res->ref_clk);
+ regulator_disable(res->vdda);
+ regulator_disable(res->vdda_phy);
+ regulator_disable(res->vdda_refclk);
+ }
+
+-static void qcom_pcie_prog_viewport_cfg0(struct qcom_pcie *pcie, u32 busdev)
+-{
+- struct pcie_port *pp = &pcie->pp;
+-
+- /*
+- * program and enable address translation region 0 (device config
+- * address space); region type config;
+- * axi config address range to device config address range
+- */
+- writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
+- PCIE20_PLR_IATU_REGION_INDEX(0),
+- pcie->pp.dbi_base + PCIE20_PLR_IATU_VIEWPORT);
+-
+- writel(PCIE20_PLR_IATU_TYPE_CFG0, pcie->pp.dbi_base + PCIE20_PLR_IATU_CTRL1);
+- writel(PCIE20_PLR_IATU_ENABLE, pcie->pp.dbi_base + PCIE20_PLR_IATU_CTRL2);
+- writel(pp->cfg0_base, pcie->pp.dbi_base + PCIE20_PLR_IATU_LBAR);
+- writel((pp->cfg0_base >> 32), pcie->pp.dbi_base + PCIE20_PLR_IATU_UBAR);
+- writel((pp->cfg0_base + pp->cfg0_size - 1),
+- pcie->pp.dbi_base + PCIE20_PLR_IATU_LAR);
+- writel(busdev, pcie->pp.dbi_base + PCIE20_PLR_IATU_LTAR);
+- writel(0, pcie->pp.dbi_base + PCIE20_PLR_IATU_UTAR);
+-}
+-
+-static void qcom_pcie_prog_viewport_mem2_outbound(struct qcom_pcie *pcie)
+-{
+- struct pcie_port *pp = &pcie->pp;
+-
+- /*
+- * program and enable address translation region 2 (device resource
+- * address space); region type memory;
+- * axi device bar address range to device bar address range
+- */
+- writel(PCIE20_PLR_IATU_REGION_OUTBOUND |
+- PCIE20_PLR_IATU_REGION_INDEX(2),
+- pcie->pp.dbi_base + PCIE20_PLR_IATU_VIEWPORT);
+-
+- writel(PCIE20_PLR_IATU_TYPE_MEM, pcie->pp.dbi_base + PCIE20_PLR_IATU_CTRL1);
+- writel(PCIE20_PLR_IATU_ENABLE, pcie->pp.dbi_base + PCIE20_PLR_IATU_CTRL2);
+- writel(pp->mem_base, pcie->pp.dbi_base + PCIE20_PLR_IATU_LBAR);
+- writel((pp->mem_base >> 32), pcie->pp.dbi_base + PCIE20_PLR_IATU_UBAR);
+- writel(pp->mem_base + pp->mem_size - 1,
+- pcie->pp.dbi_base + PCIE20_PLR_IATU_LAR);
+- writel(pp->mem_bus_addr, pcie->pp.dbi_base + PCIE20_PLR_IATU_LTAR);
+- writel(upper_32_bits(pp->mem_bus_addr),
+- pcie->pp.dbi_base + PCIE20_PLR_IATU_UTAR);
+-
+- /* 256B PCIE buffer setting */
+- writel(0x1, pcie->pp.dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0);
+- writel(0x1, pcie->pp.dbi_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1);
+-}
+-
+ static int qcom_pcie_init_v0(struct qcom_pcie *pcie)
+ {
+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
+@@ -377,19 +317,13 @@ static int qcom_pcie_init_v0(struct qcom
+ ret = reset_control_assert(res->ahb_reset);
+ if (ret) {
+ dev_err(dev, "cannot assert ahb reset\n");
+- goto err_assert_reset;
+- }
+-
+- ret = reset_control_deassert(res->ext_reset);
+- if (ret) {
+- dev_err(dev, "cannot deassert ext reset\n");
+- goto err_assert_reset;
++ 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_reset;
++ goto err_assert_ahb;
+ }
+
+ ret = clk_prepare_enable(res->phy_clk);
+@@ -404,53 +338,22 @@ static int qcom_pcie_init_v0(struct qcom
+ goto err_clk_core;
+ }
+
+- ret = clk_prepare_enable(res->aux_clk);
+- if (ret) {
+- dev_err(dev, "cannot prepare/enable aux clock\n");
+- goto err_clk_aux;
+- }
+-
+- ret = clk_prepare_enable(res->ref_clk);
+- if (ret) {
+- dev_err(dev, "cannot prepare/enable ref clock\n");
+- goto err_clk_ref;
+- }
+-
+ ret = reset_control_deassert(res->ahb_reset);
+ if (ret) {
+ dev_err(dev, "cannot deassert ahb reset\n");
+ goto err_deassert_ahb;
+ }
+- udelay(1);
+
+ /* enable PCIe clocks and resets */
+ val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
+ val &= ~BIT(0);
+ writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
+
+- /* 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(res->phy_tx0_term_offset);
+- writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
+-
+- /* PARF programming */
+- writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) |
+- PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) |
+- PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22),
+- pcie->parf + PCIE20_PARF_PCS_DEEMPH);
+- writel(PCS_SWING_TX_SWING_FULL(0x78) |
+- PCS_SWING_TX_SWING_LOW(0x78),
+- pcie->parf + PCIE20_PARF_PCS_SWING);
+- writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS);
+-
+- /* Enable reference clock */
++ /* enable external reference clock */
+ val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK);
+- val &= ~REF_USE_PAD;
+- val |= REF_SSP_EN;
++ val |= BIT(16);
+ writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK);
+
+- /* De-assert PHY, PCIe, POR and AXI resets */
+ ret = reset_control_deassert(res->phy_reset);
+ if (ret) {
+ dev_err(dev, "cannot deassert phy reset\n");
+@@ -481,16 +384,12 @@ static int qcom_pcie_init_v0(struct qcom
+ return 0;
+
+ err_deassert_ahb:
+- clk_disable_unprepare(res->ref_clk);
+-err_clk_ref:
+- clk_disable_unprepare(res->aux_clk);
+-err_clk_aux:
+ 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_reset:
++err_assert_ahb:
+ regulator_disable(res->vdda_phy);
+ err_vdda_phy:
+ regulator_disable(res->vdda_refclk);
+@@ -500,12 +399,6 @@ err_refclk:
+ return ret;
+ }
+
+-static void qcom_pcie_configure_v0(struct qcom_pcie *pcie)
+-{
+- qcom_pcie_prog_viewport_cfg0(pcie, MSM_PCIE_DEV_CFG_ADDR);
+- qcom_pcie_prog_viewport_mem2_outbound(pcie);
+-}
+-
+ static void qcom_pcie_deinit_v1(struct qcom_pcie *pcie)
+ {
+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
+@@ -585,6 +478,429 @@ err_res:
+ return ret;
+ }
+
++static int qcom_pcie_get_resources_v2(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
++ struct device *dev = pcie->pp.dev;
++
++ res->aux_clk = devm_clk_get(dev, "aux");
++ if (IS_ERR(res->aux_clk))
++ return PTR_ERR(res->aux_clk);
++
++ res->cfg_clk = devm_clk_get(dev, "cfg");
++ if (IS_ERR(res->cfg_clk))
++ return PTR_ERR(res->cfg_clk);
++
++ res->master_clk = devm_clk_get(dev, "bus_master");
++ if (IS_ERR(res->master_clk))
++ return PTR_ERR(res->master_clk);
++
++ res->slave_clk = devm_clk_get(dev, "bus_slave");
++ if (IS_ERR(res->slave_clk))
++ return PTR_ERR(res->slave_clk);
++
++ res->pipe_clk = devm_clk_get(dev, "pipe");
++ if (IS_ERR(res->pipe_clk))
++ return PTR_ERR(res->pipe_clk);
++
++ return 0;
++}
++
++static int qcom_pcie_init_v2(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
++ struct device *dev = pcie->pp.dev;
++ u32 val;
++ int ret;
++
++ ret = clk_prepare_enable(res->aux_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable aux clock\n");
++ return ret;
++ }
++
++ ret = clk_prepare_enable(res->cfg_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable cfg clock\n");
++ goto err_cfg_clk;
++ }
++
++ ret = clk_prepare_enable(res->master_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable master clock\n");
++ goto err_master_clk;
++ }
++
++ ret = clk_prepare_enable(res->slave_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable slave clock\n");
++ goto err_slave_clk;
++ }
++
++ /* 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);
++
++ val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2);
++ val |= BIT(31);
++ writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2);
++
++ return 0;
++
++err_slave_clk:
++ clk_disable_unprepare(res->master_clk);
++err_master_clk:
++ clk_disable_unprepare(res->cfg_clk);
++err_cfg_clk:
++ clk_disable_unprepare(res->aux_clk);
++
++ return ret;
++}
++
++static int qcom_pcie_post_init_v2(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
++ struct device *dev = pcie->pp.dev;
++ int ret;
++
++ ret = clk_prepare_enable(res->pipe_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable pipe clock\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static int qcom_pcie_get_resources_v3(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v3 *res = &pcie->res.v3;
++ struct device *dev = pcie->pp.dev;
++
++ res->vdda = devm_regulator_get(dev, "vdda");
++ if (IS_ERR(res->vdda))
++ return PTR_ERR(res->vdda);
++
++ res->vdda_phy = devm_regulator_get(dev, "vdda_phy");
++ if (IS_ERR(res->vdda_phy))
++ return PTR_ERR(res->vdda_phy);
++
++ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk");
++ if (IS_ERR(res->vdda_refclk))
++ return PTR_ERR(res->vdda_refclk);
++
++ res->ahb_clk = devm_clk_get(dev, "ahb");
++ if (IS_ERR(res->ahb_clk))
++ return PTR_ERR(res->ahb_clk);
++
++ res->axi_m_clk = devm_clk_get(dev, "axi_m");
++ if (IS_ERR(res->axi_m_clk))
++ return PTR_ERR(res->axi_m_clk);
++
++ res->axi_s_clk = devm_clk_get(dev, "axi_s");
++ if (IS_ERR(res->axi_s_clk))
++ return PTR_ERR(res->axi_s_clk);
++
++ res->axi_m_reset = devm_reset_control_get(dev, "axi_m");
++ if (IS_ERR(res->axi_m_reset))
++ return PTR_ERR(res->axi_m_reset);
++
++ res->axi_s_reset = devm_reset_control_get(dev, "axi_s");
++ if (IS_ERR(res->axi_s_reset))
++ return PTR_ERR(res->axi_s_reset);
++
++ res->pipe_reset = devm_reset_control_get(dev, "pipe");
++ if (IS_ERR(res->pipe_reset))
++ return PTR_ERR(res->pipe_reset);
++
++ res->axi_m_vmid_reset = devm_reset_control_get(dev, "axi_m_vmid");
++ if (IS_ERR(res->axi_m_vmid_reset))
++ return PTR_ERR(res->axi_m_vmid_reset);
++
++ res->axi_s_xpu_reset = devm_reset_control_get(dev, "axi_s_xpu");
++ if (IS_ERR(res->axi_s_xpu_reset))
++ return PTR_ERR(res->axi_s_xpu_reset);
++
++ res->parf_reset = devm_reset_control_get(dev, "parf");
++ if (IS_ERR(res->parf_reset))
++ return PTR_ERR(res->parf_reset);
++
++ res->phy_reset = devm_reset_control_get(dev, "phy");
++ if (IS_ERR(res->phy_reset))
++ return PTR_ERR(res->phy_reset);
++
++ res->axi_m_sticky_reset = devm_reset_control_get(dev, "axi_m_sticky");
++ if (IS_ERR(res->axi_m_sticky_reset))
++ return PTR_ERR(res->axi_m_sticky_reset);
++
++ res->pipe_sticky_reset = devm_reset_control_get(dev, "pipe_sticky");
++ if (IS_ERR(res->pipe_sticky_reset))
++ return PTR_ERR(res->pipe_sticky_reset);
++
++ res->pwr_reset = devm_reset_control_get(dev, "pwr");
++ if (IS_ERR(res->pwr_reset))
++ return PTR_ERR(res->pwr_reset);
++
++ res->ahb_reset = devm_reset_control_get(dev, "ahb");
++ if (IS_ERR(res->ahb_reset))
++ return PTR_ERR(res->ahb_reset);
++
++ res->phy_ahb_reset = devm_reset_control_get(dev, "phy_ahb");
++ if (IS_ERR(res->phy_ahb_reset))
++ return PTR_ERR(res->phy_ahb_reset);
++
++ return 0;
++}
++
++static void qcom_pcie_deinit_v3(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v3 *res = &pcie->res.v3;
++
++ reset_control_assert(res->axi_m_reset);
++ reset_control_assert(res->axi_s_reset);
++ reset_control_assert(res->pipe_reset);
++ reset_control_assert(res->pipe_sticky_reset);
++ reset_control_assert(res->phy_reset);
++ reset_control_assert(res->phy_ahb_reset);
++ reset_control_assert(res->axi_m_sticky_reset);
++ reset_control_assert(res->pwr_reset);
++ reset_control_assert(res->ahb_reset);
++ clk_disable_unprepare(res->ahb_clk);
++ clk_disable_unprepare(res->axi_m_clk);
++ clk_disable_unprepare(res->axi_s_clk);
++ regulator_disable(res->vdda);
++ regulator_disable(res->vdda_phy);
++ regulator_disable(res->vdda_refclk);
++}
++
++static int qcom_pcie_init_v3(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v3 *res = &pcie->res.v3;
++ struct device *dev = pcie->pp.dev;
++ u32 val;
++ int ret;
++
++ ret = reset_control_assert(res->axi_m_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert axi master reset\n");
++ return ret;
++ }
++
++ ret = reset_control_assert(res->axi_s_reset);
++ if (ret) {
++ dev_err(dev, "cannot asser axi slave reset\n");
++ return ret;
++ }
++
++ usleep_range(10000, 12000);
++
++ ret = reset_control_assert(res->pipe_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert pipe reset\n");
++ return ret;
++ }
++
++ ret = reset_control_assert(res->pipe_sticky_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert pipe sticky reset\n");
++ return ret;
++ }
++
++ ret = reset_control_assert(res->phy_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert phy reset\n");
++ return ret;
++ }
++
++ ret = reset_control_assert(res->phy_ahb_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert phy ahb reset\n");
++ return ret;
++ }
++
++ usleep_range(10000, 12000);
++
++ ret = reset_control_assert(res->axi_m_sticky_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert axi master sticky reset\n");
++ return ret;
++ }
++
++ ret = reset_control_assert(res->pwr_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert power reset\n");
++ return ret;
++ }
++
++ ret = reset_control_assert(res->ahb_reset);
++ if (ret) {
++ dev_err(dev, "cannot assert ahb reset\n");
++ return ret;
++ }
++
++ usleep_range(10000, 12000);
++
++ ret = reset_control_deassert(res->phy_ahb_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert phy ahb reset\n");
++ return ret;
++ }
++
++ ret = reset_control_deassert(res->phy_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert phy reset\n");
++ goto err_rst_phy;
++ }
++
++ ret = reset_control_deassert(res->pipe_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert pipe reset\n");
++ goto err_rst_pipe;
++ }
++
++ ret = reset_control_deassert(res->pipe_sticky_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert pipe sticky reset\n");
++ goto err_rst_pipe_sticky;
++ }
++
++ usleep_range(10000, 12000);
++
++ ret = reset_control_deassert(res->axi_m_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert axi master reset\n");
++ goto err_rst_axi_m;
++ }
++
++ ret = reset_control_deassert(res->axi_m_sticky_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert axi master sticky reset\n");
++ goto err_rst_axi_m_sticky;
++ }
++
++ ret = reset_control_deassert(res->axi_s_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert axi slave reset\n");
++ goto err_rst_axi_s;
++ }
++
++ ret = reset_control_deassert(res->pwr_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert power reset\n");
++ goto err_rst_pwr;
++ }
++
++ ret = reset_control_deassert(res->ahb_reset);
++ if (ret) {
++ dev_err(dev, "cannot deassert ahb reset\n");
++ goto err_rst_ahb;
++ }
++
++ usleep_range(10000, 12000);
++
++ ret = regulator_enable(res->vdda);
++ if (ret) {
++ dev_err(dev, "cannot enable vdda regulator\n");
++ goto err_vdda;
++ }
++
++ ret = regulator_enable(res->vdda_refclk);
++ if (ret) {
++ dev_err(dev, "cannot enable vdda_refclk regulator\n");
++ goto err_refclk;
++ }
++
++ ret = regulator_enable(res->vdda_phy);
++ if (ret) {
++ dev_err(dev, "cannot enable vdda_phy regulator\n");
++ goto err_vdda_phy;
++ }
++
++ ret = clk_prepare_enable(res->ahb_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable iface clock\n");
++ goto err_ahb;
++ }
++
++ ret = clk_prepare_enable(res->axi_m_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable core clock\n");
++ goto err_clk_axi_m;
++ }
++
++ ret = clk_prepare_enable(res->axi_s_clk);
++ if (ret) {
++ dev_err(dev, "cannot prepare/enable phy clock\n");
++ goto err_clk_axi_s;
++ }
++
++ /* 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);
++
++ val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2);
++ val |= BIT(31);
++ writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2);
++
++ return 0;
++
++err_clk_axi_s:
++ clk_disable_unprepare(res->axi_m_clk);
++err_clk_axi_m:
++ clk_disable_unprepare(res->ahb_clk);
++err_ahb:
++ regulator_disable(res->vdda_phy);
++err_vdda_phy:
++ regulator_disable(res->vdda_refclk);
++err_refclk:
++ regulator_disable(res->vdda);
++err_vdda:
++ reset_control_assert(res->ahb_reset);
++err_rst_ahb:
++ reset_control_assert(res->pwr_reset);
++err_rst_pwr:
++ reset_control_assert(res->axi_s_reset);
++err_rst_axi_s:
++ reset_control_assert(res->axi_m_sticky_reset);
++err_rst_axi_m_sticky:
++ reset_control_assert(res->axi_m_reset);
++err_rst_axi_m:
++ reset_control_assert(res->pipe_sticky_reset);
++err_rst_pipe_sticky:
++ reset_control_assert(res->pipe_reset);
++err_rst_pipe:
++ reset_control_assert(res->phy_reset);
++err_rst_phy:
++ reset_control_assert(res->phy_ahb_reset);
++ return ret;
++}
++
+ static int qcom_pcie_link_up(struct pcie_port *pp)
+ {
+ struct qcom_pcie *pcie = to_qcom_pcie(pp);
+@@ -593,6 +909,17 @@ static int qcom_pcie_link_up(struct pcie
+ return !!(val & PCI_EXP_LNKSTA_DLLLA);
+ }
+
++static void qcom_pcie_deinit_v2(struct qcom_pcie *pcie)
++{
++ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
++
++ clk_disable_unprepare(res->pipe_clk);
++ clk_disable_unprepare(res->slave_clk);
++ clk_disable_unprepare(res->master_clk);
++ clk_disable_unprepare(res->cfg_clk);
++ clk_disable_unprepare(res->aux_clk);
++}
++
+ static void qcom_pcie_host_init(struct pcie_port *pp)
+ {
+ struct qcom_pcie *pcie = to_qcom_pcie(pp);
+@@ -608,6 +935,9 @@ static void qcom_pcie_host_init(struct p
+ if (ret)
+ goto err_deinit;
+
++ if (pcie->ops->post_init)
++ pcie->ops->post_init(pcie);
++
+ dw_pcie_setup_rc(pp);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+@@ -619,9 +949,6 @@ static void qcom_pcie_host_init(struct p
+ if (ret)
+ goto err;
+
+- if (pcie->ops->init)
+- pcie->ops->init(pcie);
+-
+ return;
+ err:
+ qcom_ep_reset_assert(pcie);
+@@ -653,14 +980,30 @@ static struct pcie_host_ops qcom_pcie_dw
+ static const struct qcom_pcie_ops ops_v0 = {
+ .get_resources = qcom_pcie_get_resources_v0,
+ .init = qcom_pcie_init_v0,
+- .configure = qcom_pcie_configure_v0,
+ .deinit = qcom_pcie_deinit_v0,
++ .ltssm_enable = qcom_pcie_v0_v1_ltssm_enable,
+ };
+
+ static const struct qcom_pcie_ops ops_v1 = {
+ .get_resources = qcom_pcie_get_resources_v1,
+ .init = qcom_pcie_init_v1,
+ .deinit = qcom_pcie_deinit_v1,
++ .ltssm_enable = qcom_pcie_v0_v1_ltssm_enable,
++};
++
++static const struct qcom_pcie_ops ops_v2 = {
++ .get_resources = qcom_pcie_get_resources_v2,
++ .init = qcom_pcie_init_v2,
++ .post_init = qcom_pcie_post_init_v2,
++ .deinit = qcom_pcie_deinit_v2,
++ .ltssm_enable = qcom_pcie_v2_ltssm_enable,
++};
++
++static const struct qcom_pcie_ops ops_v3 = {
++ .get_resources = qcom_pcie_get_resources_v3,
++ .init = qcom_pcie_init_v3,
++ .deinit = qcom_pcie_deinit_v3,
++ .ltssm_enable = qcom_pcie_v2_ltssm_enable,
+ };
+
+ static int qcom_pcie_probe(struct platform_device *pdev)
+@@ -740,6 +1083,8 @@ static const struct of_device_id qcom_pc
+ { .compatible = "qcom,pcie-ipq8064", .data = &ops_v0 },
+ { .compatible = "qcom,pcie-apq8064", .data = &ops_v0 },
+ { .compatible = "qcom,pcie-apq8084", .data = &ops_v1 },
++ { .compatible = "qcom,pcie-msm8996", .data = &ops_v2 },
++ { .compatible = "qcom,pcie-ipq4019", .data = &ops_v3 },
+ { }
+ };
+