From af81e7775f99b82933101f9bc5b6d83decc37fa4 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 5 May 2017 11:15:56 +0200 Subject: [PATCH] ipq806x: update ipq pcie driver this also adds support for ipq4019 pcie cores Signed-off-by: John Crispin --- .../patches-4.9/311-ipq4019-pcie.patch | 895 ++++++++++++++++++ .../312-ipq4019-pcie-dts-nodes.patch | 77 ++ 2 files changed, 972 insertions(+) create mode 100644 target/linux/ipq806x/patches-4.9/311-ipq4019-pcie.patch create mode 100644 target/linux/ipq806x/patches-4.9/312-ipq4019-pcie-dts-nodes.patch 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 index 000000000000..8bedc8b44bdf --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/311-ipq4019-pcie.patch @@ -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 index 000000000000..9650291338ed --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/312-ipq4019-pcie-dts-nodes.patch @@ -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"; ++ }; ++ + }; + }; -- 2.30.2