ipq806x: update ipq pcie driver
authorJohn Crispin <john@phrozen.org>
Fri, 5 May 2017 09:15:56 +0000 (11:15 +0200)
committerJohn Crispin <john@phrozen.org>
Mon, 8 May 2017 08:53:26 +0000 (10:53 +0200)
this also adds support for ipq4019 pcie cores

Signed-off-by: John Crispin <john@phrozen.org>
target/linux/ipq806x/patches-4.9/311-ipq4019-pcie.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.9/312-ipq4019-pcie-dts-nodes.patch [new file with mode: 0644]

diff --git a/target/linux/ipq806x/patches-4.9/311-ipq4019-pcie.patch b/target/linux/ipq806x/patches-4.9/311-ipq4019-pcie.patch
new file mode 100644 (file)
index 0000000..8bedc8b
--- /dev/null
@@ -0,0 +1,895 @@
+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 },
+       { }
+ };
diff --git a/target/linux/ipq806x/patches-4.9/312-ipq4019-pcie-dts-nodes.patch b/target/linux/ipq806x/patches-4.9/312-ipq4019-pcie-dts-nodes.patch
new file mode 100644 (file)
index 0000000..9650291
--- /dev/null
@@ -0,0 +1,77 @@
+Index: linux-4.9.20/arch/arm/boot/dts/qcom-ipq4019.dtsi
+===================================================================
+--- linux-4.9.20.orig/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ linux-4.9.20/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -475,5 +475,72 @@
+                                         "legacy";
+                       status = "disabled";
+               };
++
++              pcie0: pci@40000000 {
++                      compatible = "qcom,pcie-ipq4019";
++                      reg =  <0x40000000 0xf1d
++                              0x40000f20 0xa8
++                              0x80000 0x2000
++                              0x40100000 0x1000>;
++                      reg-names = "dbi", "elbi", "parf", "config";
++                      device_type = "pci";
++                      linux,pci-domain = <0>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <1>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      ranges = <0x81000000 0 0x40200000 0x40200000
++                                0 0x00100000   /* downstream I/O */
++                                0x82000000 0 0x40300000 0x40300000
++                                0 0x100000 /* non-prefetchable memory */
++                                0x82000000 0 0x40400000 0x40400000
++                                0 0x200000>; /* non-prefetchable memory */
++                      interrupts = <0 141 0>;
++                      interrupt-names = "msi";
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 142
++                                       IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                      <0 0 0 2 &intc 0 143
++                                       IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                      <0 0 0 3 &intc 0 144
++                                       IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                      <0 0 0 4 &intc 0 145
++                                       IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++                      clocks = <&gcc GCC_PCIE_AHB_CLK>,
++                               <&gcc GCC_PCIE_AXI_M_CLK>,
++                               <&gcc GCC_PCIE_AXI_S_CLK>;
++                      clock-names = "ahb",
++                                    "axi_m",
++                                    "axi_s";
++
++                      resets = <&gcc PCIE_AXI_M_ARES>,
++                               <&gcc PCIE_AXI_S_ARES>,
++                               <&gcc PCIE_PIPE_ARES>,
++                               <&gcc PCIE_AXI_M_VMIDMT_ARES>,
++                               <&gcc PCIE_AXI_S_XPU_ARES>,
++                               <&gcc PCIE_PARF_XPU_ARES>,
++                               <&gcc PCIE_PHY_ARES>,
++                               <&gcc PCIE_AXI_M_STICKY_ARES>,
++                               <&gcc PCIE_PIPE_STICKY_ARES>,
++                               <&gcc PCIE_PWR_ARES>,
++                               <&gcc PCIE_AHB_ARES>,
++                               <&gcc PCIE_PHY_AHB_ARES>;
++                      reset-names = "axi_m",
++                                    "axi_s",
++                                    "pipe",
++                                    "axi_m_vmid",
++                                    "axi_s_xpu",
++                                    "parf",
++                                    "phy",
++                                    "axi_m_sticky",
++                                    "pipe_sticky",
++                                    "pwr",
++                                    "ahb",
++                                    "phy_ahb";
++                      status = "disabled";
++              };
++
+       };
+ };