From bbde72dea11689b21cc34f61f064d0e35b660fec Mon Sep 17 00:00:00 2001 From: Manikanta Mylavarapu Date: Fri, 31 Dec 2021 17:13:59 +0530 Subject: [PATCH 1009/1011] ipq6018: rproc: Add non secure Q6 bringup sequence This patch adds Q6 bring up sequence support. Change-Id: I28eee991168034cc240d863e736ed9c766ec4f33 Signed-off-by: Manikanta Mylavarapu --- arch/arm64/boot/dts/qcom/ipq6018.dtsi | 20 ++- drivers/remoteproc/qcom_q6v5_wcss.c | 223 ++++++++++++++++++++++++-- 2 files changed, 227 insertions(+), 16 deletions(-) --- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi @@ -663,8 +663,24 @@ "wcss_reset", "wcss_q6_reset"; - clocks = <&gcc GCC_PRNG_AHB_CLK>; - clock-names = "prng"; + clocks = <&gcc GCC_PRNG_AHB_CLK>, + <&gcc GCC_SYS_NOC_WCSS_AHB_CLK>, + <&gcc GCC_Q6SS_ATBM_CLK>, + <&gcc GCC_Q6SS_PCLKDBG_CLK>, + <&gcc GCC_Q6_TSCTR_1TO2_CLK>; + clock-names = "prng", + "gcc_sys_noc_wcss_ahb_clk", + "gcc_q6ss_atbm_clk", + "gcc_q6ss_pclkdbg_clk", + "gcc_q6_tsctr_1to2_clk"; + assigned-clocks = <&gcc GCC_SYS_NOC_WCSS_AHB_CLK>, + <&gcc GCC_Q6SS_PCLKDBG_CLK>, + <&gcc GCC_Q6_TSCTR_1TO2_CLK>, + <&gcc GCC_Q6SS_ATBM_CLK>; + assigned-clock-rates = <133333333>, + <600000000>, + <600000000>, + <240000000>; qcom,halt-regs = <&tcsr 0x18000 0x1b000 0xe000>; --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -27,6 +27,7 @@ /* Q6SS Register Offsets */ #define Q6SS_RESET_REG 0x014 +#define Q6SS_DBG_CFG 0x018 #define Q6SS_GFMUX_CTL_REG 0x020 #define Q6SS_PWR_CTL_REG 0x030 #define Q6SS_MEM_PWR_CTL 0x0B0 @@ -68,6 +69,7 @@ #define HALT_CHECK_MAX_LOOPS 200 #define Q6SS_XO_CBCR GENMASK(5, 3) #define Q6SS_SLEEP_CBCR GENMASK(5, 2) +#define Q6SS_TIMEOUT_US 1000 /* Q6SS config/status registers */ #define TCSR_GLOBAL_CFG0 0x0 @@ -78,6 +80,7 @@ #define Q6SS_RST_EVB 0x10 #define BHS_EN_REST_ACK BIT(0) +#define WCSS_HM_RET BIT(1) #define SSCAON_ENABLE BIT(13) #define SSCAON_BUS_EN BIT(15) #define SSCAON_BUS_MUX_MASK GENMASK(18, 16) @@ -120,6 +123,11 @@ struct q6v5_wcss { struct clk *qdsp6ss_core_gfmux; struct clk *lcc_bcr_sleep; struct clk *prng_clk; + struct clk *gcc_sys_noc_wcss_ahb_clk; + struct clk *gcc_q6ss_atbm_clk; + struct clk *gcc_q6ss_pclkdbg_clk; + struct clk *gcc_q6_tsctr_1to2_clk; + struct regulator *cx_supply; struct qcom_sysmon *sysmon; @@ -164,12 +172,77 @@ struct wcss_data { bool need_auto_boot; }; +static const struct wcss_data wcss_ipq6018_res_init; + +static int ipq6018_clks_prepare_enable(struct q6v5_wcss *wcss) +{ + int ret; + + ret = clk_prepare_enable(wcss->gcc_sys_noc_wcss_ahb_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(wcss->gcc_q6ss_atbm_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(wcss->gcc_q6ss_pclkdbg_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(wcss->gcc_q6_tsctr_1to2_clk); + if (ret) + return ret; + + return 0; +} + +static void ipq6018_clks_prepare_disable(struct q6v5_wcss *wcss) +{ + clk_disable_unprepare(wcss->gcc_sys_noc_wcss_ahb_clk); + clk_disable_unprepare(wcss->gcc_q6ss_atbm_clk); + clk_disable_unprepare(wcss->gcc_q6ss_pclkdbg_clk); + clk_disable_unprepare(wcss->gcc_q6_tsctr_1to2_clk); +} + static int q6v5_wcss_reset(struct q6v5_wcss *wcss) { + const struct wcss_data *desc; int ret; u32 val; int i; + desc = device_get_match_data(wcss->dev); + if (desc == &wcss_ipq6018_res_init) { + if (desc->aon_reset_required) { + /* Deassert wcss aon reset */ + ret = reset_control_deassert(wcss->wcss_aon_reset); + if (ret) { + dev_err(wcss->dev, "wcss_aon_reset failed\n"); + return ret; + } + mdelay(1); + } + + ret = ipq6018_clks_prepare_enable(wcss); + if (ret) { + dev_err(wcss->dev, "failed to enable clock\n"); + return ret; + } + } + + val = readl(wcss->rmb_base + SSCAON_CONFIG); + val |= BIT(0); + writel(val, wcss->rmb_base + SSCAON_CONFIG); + mdelay(1); + + /*set CFG[18:15]=1* and clear CFG[1]=0*/ + val = readl(wcss->rmb_base + SSCAON_CONFIG); + val &= ~(SSCAON_BUS_MUX_MASK | WCSS_HM_RET); + val |= SSCAON_BUS_EN; + writel(val, wcss->rmb_base + SSCAON_CONFIG); + mdelay(1); + /* Assert resets, stop core */ val = readl(wcss->reg_base + Q6SS_RESET_REG); val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE; @@ -195,7 +268,19 @@ static int q6v5_wcss_reset(struct q6v5_w writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); udelay(1); + if (desc == &wcss_ipq6018_res_init) { + /* 10 - Wait till BHS Reset is done */ + ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS, + val, (val & BHS_EN_REST_ACK), 1000, + Q6SS_TIMEOUT_US * 50); + if (ret) { + dev_err(wcss->dev, "BHS_STATUS not ON (rc:%d) val:0x%X\n", ret, val); + return ret; + } + } + /* Put LDO in bypass mode */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); val |= Q6SS_LDO_BYP; writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); @@ -205,6 +290,7 @@ static int q6v5_wcss_reset(struct q6v5_w writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); /* Deassert memory peripheral sleep and L2 memory standby */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N; writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); @@ -219,7 +305,10 @@ static int q6v5_wcss_reset(struct q6v5_w * array to turn on. */ val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL); - udelay(1); + if (desc == &wcss_ipq6018_res_init) + mdelay(10); + else + udelay(1); } /* Remove word line clamp */ val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); @@ -227,6 +316,7 @@ static int q6v5_wcss_reset(struct q6v5_w writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); /* Remove IO clamp */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); val &= ~Q6SS_CLAMP_IO; writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); @@ -245,6 +335,16 @@ static int q6v5_wcss_reset(struct q6v5_w val &= ~Q6SS_STOP_CORE; writel(val, wcss->reg_base + Q6SS_RESET_REG); + /* Wait for SSCAON_STATUS */ + val = readl(wcss->rmb_base + SSCAON_STATUS); + ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS, + val, (val & 0xffff) == 0x10, 1000, + Q6SS_TIMEOUT_US * 1000); + if (ret) { + dev_err(wcss->dev, " Boot Error, SSCAON=0x%08X\n", val); + return ret; + } + return 0; } @@ -376,7 +476,7 @@ static int q6v5_wcss_qcs404_power_on(str /* Read CLKOFF bit to go low indicating CLK is enabled */ ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR, val, !(val & BIT(31)), 1, - HALT_CHECK_MAX_LOOPS); + Q6SS_TIMEOUT_US); if (ret) { dev_err(wcss->dev, "xo cbcr enabling timed out (rc:%d)\n", ret); @@ -529,13 +629,18 @@ static void q6v5_wcss_halt_axi_port(stru unsigned long timeout; unsigned int val; int ret; + const struct wcss_data *desc = device_get_match_data(wcss->dev); - /* Check if we're already idle */ - ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); - if (!ret && val) - return; + if (desc != &wcss_ipq6018_res_init) { + /* Check if we're already idle */ + ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); + if (!ret && val) + return; + } /* Assert halt request */ + regmap_read(halt_map, offset + AXI_HALTREQ_REG, &val); + val |= BIT(0); regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1); /* Wait for halt */ @@ -548,12 +653,14 @@ static void q6v5_wcss_halt_axi_port(stru msleep(1); } - ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); - if (ret || !val) - dev_err(wcss->dev, "port failed halt\n"); + if (desc != &wcss_ipq6018_res_init) { + ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); + if (ret || !val) + dev_err(wcss->dev, "port failed halt\n"); - /* Clear halt request (port will remain halted until reset) */ - regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0); + /* Clear halt request (port will remain halted until reset) */ + regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0); + } } static int q6v5_qcs404_wcss_shutdown(struct q6v5_wcss *wcss) @@ -622,6 +729,7 @@ static int q6v5_qcs404_wcss_shutdown(str static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss) { + const struct wcss_data *desc = device_get_match_data(wcss->dev); int ret; u32 val; @@ -639,21 +747,26 @@ static int q6v5_wcss_powerdown(struct q6 writel(val, wcss->rmb_base + SSCAON_CONFIG); /* 4 - SSCAON_CONFIG 1 */ + val = readl(wcss->rmb_base + SSCAON_CONFIG); val |= BIT(1); writel(val, wcss->rmb_base + SSCAON_CONFIG); /* 5 - wait for SSCAON_STATUS */ ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS, val, (val & 0xffff) == 0x400, 1000, - HALT_CHECK_MAX_LOOPS); + Q6SS_TIMEOUT_US * 10); if (ret) { dev_err(wcss->dev, "can't get SSCAON_STATUS rc:%d)\n", ret); return ret; } + mdelay(2); + /* 6 - De-assert WCSS_AON reset */ reset_control_assert(wcss->wcss_aon_reset); + if (desc == &wcss_ipq6018_res_init) + mdelay(1); /* 7 - Disable WCSSAON_CONFIG 13 */ val = readl(wcss->rmb_base + SSCAON_CONFIG); @@ -663,6 +776,13 @@ static int q6v5_wcss_powerdown(struct q6 /* 8 - De-assert WCSS/Q6 HALTREQ */ reset_control_assert(wcss->wcss_reset); + if (desc == &wcss_ipq6018_res_init) { + /* Clear halt request (port will remain halted until reset) */ + regmap_read(wcss->halt_map, wcss->halt_wcss + AXI_HALTREQ_REG, &val); + val &= ~0x1; + regmap_write(wcss->halt_map, wcss->halt_wcss + AXI_HALTREQ_REG, val); + } + return 0; } @@ -671,6 +791,12 @@ static int q6v5_q6_powerdown(struct q6v5 int ret; u32 val; int i; + const struct wcss_data *desc = device_get_match_data(wcss->dev); + + if (desc == &wcss_ipq6018_res_init) { + /* To retain power domain after q6 powerdown */ + writel(0x1, wcss->reg_base + Q6SS_DBG_CFG); + } /* 1 - Halt Q6 bus interface */ q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6); @@ -686,14 +812,17 @@ static int q6v5_q6_powerdown(struct q6v5 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); /* 4 - Clamp WL */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); val |= QDSS_BHS_ON; writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); /* 5 - Clear Erase standby */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); val &= ~Q6SS_L2DATA_STBY_N; writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); /* 6 - Clear Sleep RTN */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); val &= ~Q6SS_SLP_RET_N; writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); @@ -711,6 +840,7 @@ static int q6v5_q6_powerdown(struct q6v5 writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); /* 9 - Turn off BHS */ + val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); val &= ~Q6SS_BHS_ON; writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); udelay(1); @@ -718,7 +848,7 @@ static int q6v5_q6_powerdown(struct q6v5 /* 10 - Wait till BHS Reset is done */ ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS, val, !(val & BHS_EN_REST_ACK), 1000, - HALT_CHECK_MAX_LOOPS); + Q6SS_TIMEOUT_US * 10); if (ret) { dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret); return ret; @@ -726,9 +856,23 @@ static int q6v5_q6_powerdown(struct q6v5 /* 11 - Assert WCSS reset */ reset_control_assert(wcss->wcss_reset); + if (desc == &wcss_ipq6018_res_init) + mdelay(1); /* 12 - Assert Q6 reset */ reset_control_assert(wcss->wcss_q6_reset); + if (desc == &wcss_ipq6018_res_init) { + mdelay(2); + + /* Clear halt request (port will remain halted until reset) */ + regmap_read(wcss->halt_map, wcss->halt_q6 + AXI_HALTREQ_REG, &val); + val &= ~0x1; + regmap_write(wcss->halt_map, wcss->halt_q6 + AXI_HALTREQ_REG, val); + mdelay(1); + + /* Disable clocks*/ + ipq6018_clks_prepare_disable(wcss); + } return 0; } @@ -970,6 +1114,57 @@ static int q6v5_alloc_memory_region(stru return 0; } +static int ipq6018_init_clock(struct q6v5_wcss *wcss) +{ + int ret; + + wcss->prng_clk = devm_clk_get(wcss->dev, "prng"); + if (IS_ERR(wcss->prng_clk)) { + ret = PTR_ERR(wcss->prng_clk); + if (ret != -EPROBE_DEFER) + dev_err(wcss->dev, "Failed to get prng clock\n"); + return ret; + } + + wcss->gcc_sys_noc_wcss_ahb_clk = + devm_clk_get(wcss->dev, "gcc_sys_noc_wcss_ahb_clk"); + if (IS_ERR(wcss->gcc_sys_noc_wcss_ahb_clk)) { + ret = PTR_ERR(wcss->gcc_sys_noc_wcss_ahb_clk); + if (ret != -EPROBE_DEFER) + dev_err(wcss->dev, "Failed to get sys_noc_wcss_ahb clock\n"); + return ret; + } + + wcss->gcc_q6ss_atbm_clk = + devm_clk_get(wcss->dev, "gcc_q6ss_atbm_clk"); + if (IS_ERR(wcss->gcc_q6ss_atbm_clk)) { + ret = PTR_ERR(wcss->gcc_q6ss_atbm_clk); + if (ret != -EPROBE_DEFER) + dev_err(wcss->dev, "Failed to get q6ss_atbm clock\n"); + return ret; + } + + wcss->gcc_q6ss_pclkdbg_clk = + devm_clk_get(wcss->dev, "gcc_q6ss_pclkdbg_clk"); + if (IS_ERR(wcss->gcc_q6ss_pclkdbg_clk)) { + ret = PTR_ERR(wcss->gcc_q6ss_pclkdbg_clk); + if (ret != -EPROBE_DEFER) + dev_err(wcss->dev, "Failed to get q6ss_pclkdbg clock\n"); + return ret; + } + + wcss->gcc_q6_tsctr_1to2_clk = + devm_clk_get(wcss->dev, "gcc_q6_tsctr_1to2_clk"); + if (IS_ERR(wcss->gcc_q6_tsctr_1to2_clk)) { + ret = PTR_ERR(wcss->gcc_q6_tsctr_1to2_clk); + if (ret != -EPROBE_DEFER) + dev_err(wcss->dev, "Failed to get q6_tsctr_1to2 clock\n"); + return ret; + } + + return 0; +} + static int ipq8074_init_clock(struct q6v5_wcss *wcss) { int ret; @@ -1178,7 +1373,7 @@ static int q6v5_wcss_remove(struct platf } static const struct wcss_data wcss_ipq6018_res_init = { - .init_clock = ipq8074_init_clock, + .init_clock = ipq6018_init_clock, .q6_firmware_name = "IPQ6018/q6_fw.mdt", .m3_firmware_name = "IPQ6018/m3_fw.mdt", .crash_reason_smem = WCSS_CRASH_REASON,