OSDN Git Service

remoteproc: mss: q6v5-mss: Add modem support on SC7280
authorSibi Sankar <sibis@codeaurora.org>
Fri, 17 Sep 2021 13:55:30 +0000 (19:25 +0530)
committerBjorn Andersson <bjorn.andersson@linaro.org>
Mon, 27 Sep 2021 23:37:34 +0000 (18:37 -0500)
Add out of reset sequence support for modem sub-system on SC7280 SoCs.
It requires access to an additional set of qaccept registers, external
power/clk control registers and halt vq6 register to put the modem back
into reset.

Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
Reviewed-by: Stephen Boyd <swboyd@chromium.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Link: https://lore.kernel.org/r/1631886935-14691-6-git-send-email-sibis@codeaurora.org
drivers/remoteproc/qcom_q6v5_mss.c

index 7a1422b..ec07011 100644 (file)
 
 #define HALT_ACK_TIMEOUT_US            100000
 
+/* QACCEPT Register Offsets */
+#define QACCEPT_ACCEPT_REG             0x0
+#define QACCEPT_ACTIVE_REG             0x4
+#define QACCEPT_DENY_REG               0x8
+#define QACCEPT_REQ_REG                        0xC
+
+#define QACCEPT_TIMEOUT_US             50
+
 /* QDSP6SS_RESET */
 #define Q6SS_STOP_CORE                 BIT(0)
 #define Q6SS_CORE_ARES                 BIT(1)
@@ -143,6 +151,9 @@ struct rproc_hexagon_res {
        bool has_alt_reset;
        bool has_mba_logs;
        bool has_spare_reg;
+       bool has_qaccept_regs;
+       bool has_ext_cntl_regs;
+       bool has_vq6;
 };
 
 struct q6v5 {
@@ -158,8 +169,18 @@ struct q6v5 {
        u32 halt_q6;
        u32 halt_modem;
        u32 halt_nc;
+       u32 halt_vq6;
        u32 conn_box;
 
+       u32 qaccept_mdm;
+       u32 qaccept_cx;
+       u32 qaccept_axi;
+
+       u32 axim1_clk_off;
+       u32 crypto_clk_off;
+       u32 force_clk_on;
+       u32 rscc_disable;
+
        struct reset_control *mss_restart;
        struct reset_control *pdc_reset;
 
@@ -201,6 +222,9 @@ struct q6v5 {
        bool has_alt_reset;
        bool has_mba_logs;
        bool has_spare_reg;
+       bool has_qaccept_regs;
+       bool has_ext_cntl_regs;
+       bool has_vq6;
        int mpss_perm;
        int mba_perm;
        const char *hexagon_mdt_image;
@@ -213,6 +237,7 @@ enum {
        MSS_MSM8996,
        MSS_MSM8998,
        MSS_SC7180,
+       MSS_SC7280,
        MSS_SDM845,
 };
 
@@ -473,6 +498,12 @@ static int q6v5_reset_assert(struct q6v5 *qproc)
                regmap_update_bits(qproc->conn_map, qproc->conn_box,
                                   AXI_GATING_VALID_OVERRIDE, 0);
                ret = reset_control_deassert(qproc->mss_restart);
+       } else if (qproc->has_ext_cntl_regs) {
+               regmap_write(qproc->conn_map, qproc->rscc_disable, 0);
+               reset_control_assert(qproc->pdc_reset);
+               reset_control_assert(qproc->mss_restart);
+               reset_control_deassert(qproc->pdc_reset);
+               ret = reset_control_deassert(qproc->mss_restart);
        } else {
                ret = reset_control_assert(qproc->mss_restart);
        }
@@ -490,7 +521,7 @@ static int q6v5_reset_deassert(struct q6v5 *qproc)
                ret = reset_control_reset(qproc->mss_restart);
                writel(0, qproc->rmb_base + RMB_MBA_ALT_RESET);
                reset_control_deassert(qproc->pdc_reset);
-       } else if (qproc->has_spare_reg) {
+       } else if (qproc->has_spare_reg || qproc->has_ext_cntl_regs) {
                ret = reset_control_reset(qproc->mss_restart);
        } else {
                ret = reset_control_deassert(qproc->mss_restart);
@@ -604,7 +635,7 @@ static int q6v5proc_reset(struct q6v5 *qproc)
                }
 
                goto pbl_wait;
-       } else if (qproc->version == MSS_SC7180) {
+       } else if (qproc->version == MSS_SC7180 || qproc->version == MSS_SC7280) {
                val = readl(qproc->reg_base + QDSP6SS_SLEEP);
                val |= Q6SS_CBCR_CLKEN;
                writel(val, qproc->reg_base + QDSP6SS_SLEEP);
@@ -787,6 +818,89 @@ pbl_wait:
        return ret;
 }
 
+static int q6v5proc_enable_qchannel(struct q6v5 *qproc, struct regmap *map, u32 offset)
+{
+       unsigned int val;
+       int ret;
+
+       if (!qproc->has_qaccept_regs)
+               return 0;
+
+       if (qproc->has_ext_cntl_regs) {
+               regmap_write(qproc->conn_map, qproc->rscc_disable, 0);
+               regmap_write(qproc->conn_map, qproc->force_clk_on, 1);
+
+               ret = regmap_read_poll_timeout(qproc->halt_map, qproc->axim1_clk_off, val,
+                                              !val, 1, Q6SS_CBCR_TIMEOUT_US);
+               if (ret) {
+                       dev_err(qproc->dev, "failed to enable axim1 clock\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
+       regmap_write(map, offset + QACCEPT_REQ_REG, 1);
+
+       /* Wait for accept */
+       ret = regmap_read_poll_timeout(map, offset + QACCEPT_ACCEPT_REG, val, val, 5,
+                                      QACCEPT_TIMEOUT_US);
+       if (ret) {
+               dev_err(qproc->dev, "qchannel enable failed\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static void q6v5proc_disable_qchannel(struct q6v5 *qproc, struct regmap *map, u32 offset)
+{
+       int ret;
+       unsigned int val, retry;
+       unsigned int nretry = 10;
+       bool takedown_complete = false;
+
+       if (!qproc->has_qaccept_regs)
+               return;
+
+       while (!takedown_complete && nretry) {
+               nretry--;
+
+               /* Wait for active transactions to complete */
+               regmap_read_poll_timeout(map, offset + QACCEPT_ACTIVE_REG, val, !val, 5,
+                                        QACCEPT_TIMEOUT_US);
+
+               /* Request Q-channel transaction takedown */
+               regmap_write(map, offset + QACCEPT_REQ_REG, 0);
+
+               /*
+                * If the request is denied, reset the Q-channel takedown request,
+                * wait for active transactions to complete and retry takedown.
+                */
+               retry = 10;
+               while (retry) {
+                       usleep_range(5, 10);
+                       retry--;
+                       ret = regmap_read(map, offset + QACCEPT_DENY_REG, &val);
+                       if (!ret && val) {
+                               regmap_write(map, offset + QACCEPT_REQ_REG, 1);
+                               break;
+                       }
+
+                       ret = regmap_read(map, offset + QACCEPT_ACCEPT_REG, &val);
+                       if (!ret && !val) {
+                               takedown_complete = true;
+                               break;
+                       }
+               }
+
+               if (!retry)
+                       break;
+       }
+
+       /* Rely on mss_restart to clear out pending transactions on takedown failure */
+       if (!takedown_complete)
+               dev_err(qproc->dev, "qchannel takedown failed\n");
+}
+
 static void q6v5proc_halt_axi_port(struct q6v5 *qproc,
                                   struct regmap *halt_map,
                                   u32 offset)
@@ -950,6 +1064,12 @@ static int q6v5_mba_load(struct q6v5 *qproc)
                goto assert_reset;
        }
 
+       ret = q6v5proc_enable_qchannel(qproc, qproc->halt_map, qproc->qaccept_axi);
+       if (ret) {
+               dev_err(qproc->dev, "failed to enable axi bridge\n");
+               goto disable_active_clks;
+       }
+
        /*
         * Some versions of the MBA firmware will upon boot wipe the MPSS region as well, so provide
         * the Q6 access to this region.
@@ -996,8 +1116,13 @@ static int q6v5_mba_load(struct q6v5 *qproc)
 
 halt_axi_ports:
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
+       if (qproc->has_vq6)
+               q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_vq6);
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
+       q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_mdm);
+       q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_cx);
+       q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_axi);
        mba_load_err = true;
 reclaim_mba:
        xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true,
@@ -1047,6 +1172,8 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
        qproc->dp_size = 0;
 
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
+       if (qproc->has_vq6)
+               q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_vq6);
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
        q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
        if (qproc->version == MSS_MSM8996) {
@@ -1059,6 +1186,24 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc)
                writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
        }
 
+       if (qproc->has_ext_cntl_regs) {
+               regmap_write(qproc->conn_map, qproc->rscc_disable, 1);
+
+               ret = regmap_read_poll_timeout(qproc->halt_map, qproc->axim1_clk_off, val,
+                                              !val, 1, Q6SS_CBCR_TIMEOUT_US);
+               if (ret)
+                       dev_err(qproc->dev, "failed to enable axim1 clock\n");
+
+               ret = regmap_read_poll_timeout(qproc->halt_map, qproc->crypto_clk_off, val,
+                                              !val, 1, Q6SS_CBCR_TIMEOUT_US);
+               if (ret)
+                       dev_err(qproc->dev, "failed to enable crypto clock\n");
+       }
+
+       q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_mdm);
+       q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_cx);
+       q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_axi);
+
        q6v5_reset_assert(qproc);
 
        q6v5_clk_disable(qproc->dev, qproc->reset_clks,
@@ -1471,6 +1616,7 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
 {
        struct of_phandle_args args;
        struct resource *res;
+       int halt_cell_cnt = 3;
        int ret;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
@@ -1483,8 +1629,11 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
        if (IS_ERR(qproc->rmb_base))
                return PTR_ERR(qproc->rmb_base);
 
+       if (qproc->has_vq6)
+               halt_cell_cnt++;
+
        ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
-                                              "qcom,halt-regs", 3, 0, &args);
+                                              "qcom,halt-regs", halt_cell_cnt, 0, &args);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
                return -EINVAL;
@@ -1499,6 +1648,52 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
        qproc->halt_modem = args.args[1];
        qproc->halt_nc = args.args[2];
 
+       if (qproc->has_vq6)
+               qproc->halt_vq6 = args.args[3];
+
+       if (qproc->has_qaccept_regs) {
+               ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+                                                      "qcom,qaccept-regs",
+                                                      3, 0, &args);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to parse qaccept-regs\n");
+                       return -EINVAL;
+               }
+
+               qproc->qaccept_mdm = args.args[0];
+               qproc->qaccept_cx = args.args[1];
+               qproc->qaccept_axi = args.args[2];
+       }
+
+       if (qproc->has_ext_cntl_regs) {
+               ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+                                                      "qcom,ext-regs",
+                                                      2, 0, &args);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to parse ext-regs index 0\n");
+                       return -EINVAL;
+               }
+
+               qproc->conn_map = syscon_node_to_regmap(args.np);
+               of_node_put(args.np);
+               if (IS_ERR(qproc->conn_map))
+                       return PTR_ERR(qproc->conn_map);
+
+               qproc->force_clk_on = args.args[0];
+               qproc->rscc_disable = args.args[1];
+
+               ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+                                                      "qcom,ext-regs",
+                                                      2, 1, &args);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to parse ext-regs index 1\n");
+                       return -EINVAL;
+               }
+
+               qproc->axim1_clk_off = args.args[0];
+               qproc->crypto_clk_off = args.args[1];
+       }
+
        if (qproc->has_spare_reg) {
                ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
                                                       "qcom,spare-regs",
@@ -1590,7 +1785,7 @@ static int q6v5_init_reset(struct q6v5 *qproc)
                return PTR_ERR(qproc->mss_restart);
        }
 
-       if (qproc->has_alt_reset || qproc->has_spare_reg) {
+       if (qproc->has_alt_reset || qproc->has_spare_reg || qproc->has_ext_cntl_regs) {
                qproc->pdc_reset = devm_reset_control_get_exclusive(qproc->dev,
                                                                    "pdc_reset");
                if (IS_ERR(qproc->pdc_reset)) {
@@ -1697,6 +1892,9 @@ static int q6v5_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, qproc);
 
+       qproc->has_qaccept_regs = desc->has_qaccept_regs;
+       qproc->has_ext_cntl_regs = desc->has_ext_cntl_regs;
+       qproc->has_vq6 = desc->has_vq6;
        qproc->has_spare_reg = desc->has_spare_reg;
        ret = q6v5_init_mem(qproc, pdev);
        if (ret)
@@ -1857,9 +2055,40 @@ static const struct rproc_hexagon_res sc7180_mss = {
        .has_alt_reset = false,
        .has_mba_logs = true,
        .has_spare_reg = true,
+       .has_qaccept_regs = false,
+       .has_ext_cntl_regs = false,
+       .has_vq6 = false,
        .version = MSS_SC7180,
 };
 
+static const struct rproc_hexagon_res sc7280_mss = {
+       .hexagon_mba_image = "mba.mbn",
+       .proxy_clk_names = (char*[]){
+               "xo",
+               "pka",
+               NULL
+       },
+       .active_clk_names = (char*[]){
+               "iface",
+               "offline",
+               "snoc_axi",
+               NULL
+       },
+       .proxy_pd_names = (char*[]){
+               "cx",
+               "mss",
+               NULL
+       },
+       .need_mem_protection = true,
+       .has_alt_reset = false,
+       .has_mba_logs = true,
+       .has_spare_reg = false,
+       .has_qaccept_regs = true,
+       .has_ext_cntl_regs = true,
+       .has_vq6 = true,
+       .version = MSS_SC7280,
+};
+
 static const struct rproc_hexagon_res sdm845_mss = {
        .hexagon_mba_image = "mba.mbn",
        .proxy_clk_names = (char*[]){
@@ -1889,6 +2118,9 @@ static const struct rproc_hexagon_res sdm845_mss = {
        .has_alt_reset = true,
        .has_mba_logs = false,
        .has_spare_reg = false,
+       .has_qaccept_regs = false,
+       .has_ext_cntl_regs = false,
+       .has_vq6 = false,
        .version = MSS_SDM845,
 };
 
@@ -1917,6 +2149,9 @@ static const struct rproc_hexagon_res msm8998_mss = {
        .has_alt_reset = false,
        .has_mba_logs = false,
        .has_spare_reg = false,
+       .has_qaccept_regs = false,
+       .has_ext_cntl_regs = false,
+       .has_vq6 = false,
        .version = MSS_MSM8998,
 };
 
@@ -1948,6 +2183,9 @@ static const struct rproc_hexagon_res msm8996_mss = {
        .has_alt_reset = false,
        .has_mba_logs = false,
        .has_spare_reg = false,
+       .has_qaccept_regs = false,
+       .has_ext_cntl_regs = false,
+       .has_vq6 = false,
        .version = MSS_MSM8996,
 };
 
@@ -1990,6 +2228,9 @@ static const struct rproc_hexagon_res msm8916_mss = {
        .has_alt_reset = false,
        .has_mba_logs = false,
        .has_spare_reg = false,
+       .has_qaccept_regs = false,
+       .has_ext_cntl_regs = false,
+       .has_vq6 = false,
        .version = MSS_MSM8916,
 };
 
@@ -2040,6 +2281,9 @@ static const struct rproc_hexagon_res msm8974_mss = {
        .has_alt_reset = false,
        .has_mba_logs = false,
        .has_spare_reg = false,
+       .has_qaccept_regs = false,
+       .has_ext_cntl_regs = false,
+       .has_vq6 = false,
        .version = MSS_MSM8974,
 };
 
@@ -2050,6 +2294,7 @@ static const struct of_device_id q6v5_of_match[] = {
        { .compatible = "qcom,msm8996-mss-pil", .data = &msm8996_mss},
        { .compatible = "qcom,msm8998-mss-pil", .data = &msm8998_mss},
        { .compatible = "qcom,sc7180-mss-pil", .data = &sc7180_mss},
+       { .compatible = "qcom,sc7280-mss-pil", .data = &sc7280_mss},
        { .compatible = "qcom,sdm845-mss-pil", .data = &sdm845_mss},
        { },
 };