On Mon, Jan 27, 2025 at 11:45:37PM +0100, Luca Weiss wrote:
> Add support for the external power block headswitch register needed by
> MSM8226 and some other qcom platforms.
> 
> Co-developed-by: Matti Lehtimäki <matti.lehtim...@gmail.com>
> Signed-off-by: Matti Lehtimäki <matti.lehtim...@gmail.com>
> Signed-off-by: Luca Weiss <l...@lucaweiss.eu>
> ---
>  drivers/remoteproc/qcom_q6v5_mss.c | 113 
> +++++++++++++++++++++++++++++++++++++
>  1 file changed, 113 insertions(+)
> 
> diff --git a/drivers/remoteproc/qcom_q6v5_mss.c 
> b/drivers/remoteproc/qcom_q6v5_mss.c
> index 
> 0e1b0934ceedd13d5790b798afc95d68a8314c75..32f35fe89416f167fe49be7ca02a24af842e0073
>  100644
> --- a/drivers/remoteproc/qcom_q6v5_mss.c
> +++ b/drivers/remoteproc/qcom_q6v5_mss.c
> @@ -134,6 +134,11 @@
>  #define BOOT_FSM_TIMEOUT                10000
>  #define BHS_CHECK_MAX_LOOPS             200
>  
> +/* External power block headswitch */
> +#define EXTERNAL_BHS_ON                      BIT(0)
> +#define EXTERNAL_BHS_STATUS          BIT(4)
> +#define EXTERNAL_BHS_TIMEOUT_US              50
> +
>  struct reg_info {
>       struct regulator *reg;
>       int uV;
> @@ -161,6 +166,7 @@ struct rproc_hexagon_res {
>       bool has_mba_logs;
>       bool has_spare_reg;
>       bool has_qaccept_regs;
> +     bool has_ext_bhs_reg;
>       bool has_ext_cntl_regs;
>       bool has_vq6;
>  };
> @@ -180,6 +186,7 @@ struct q6v5 {
>       u32 halt_nc;
>       u32 halt_vq6;
>       u32 conn_box;
> +     u32 ext_bhs;
>  
>       u32 qaccept_mdm;
>       u32 qaccept_cx;
> @@ -237,6 +244,7 @@ struct q6v5 {
>       bool has_mba_logs;
>       bool has_spare_reg;
>       bool has_qaccept_regs;
> +     bool has_ext_bhs_reg;
>       bool has_ext_cntl_regs;
>       bool has_vq6;
>       u64 mpss_perm;
> @@ -246,6 +254,7 @@ struct q6v5 {
>  };
>  
>  enum {
> +     MSS_MSM8226,
>       MSS_MSM8909,
>       MSS_MSM8916,
>       MSS_MSM8953,
> @@ -1750,6 +1759,23 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct 
> platform_device *pdev)
>               qproc->qaccept_axi = args.args[2];
>       }
>  
> +     if (qproc->has_ext_bhs_reg) {
> +             ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
> +                                                    "qcom,ext-bhs-reg",
> +                                                    1, 0, &args);
> +             if (ret < 0) {
> +                     dev_err(&pdev->dev, "failed to parse ext-bhs-reg 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->ext_bhs = args.args[0];
> +     }
> +
>       if (qproc->has_ext_cntl_regs) {
>               ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
>                                                      "qcom,ext-regs",
> @@ -1871,6 +1897,34 @@ static void q6v5_pds_detach(struct q6v5 *qproc, struct 
> device **pds,
>               dev_pm_domain_detach(pds[i], false);
>  }
>  
> +static int q6v5_external_bhs_enable(struct q6v5 *qproc)
> +{
> +     u32 val;
> +     int ret = 0;
> +
> +     /*
> +      * Enable external power block headswitch and wait for it to
> +      * stabilize
> +      */
> +     regmap_set_bits(qproc->conn_map, qproc->ext_bhs, EXTERNAL_BHS_ON);
> +
> +     ret = regmap_read_poll_timeout(qproc->conn_map, qproc->ext_bhs,
> +                                    val, val & EXTERNAL_BHS_STATUS,
> +                                    1, EXTERNAL_BHS_TIMEOUT_US);
> +
> +     if (ret) {
> +             dev_err(qproc->dev, "External BHS timed out\n");
> +             ret = -ETIMEDOUT;
> +     }
> +
> +     return ret;
> +}
> +
> +static void q6v5_external_bhs_disable(struct q6v5 *qproc)
> +{
> +     regmap_clear_bits(qproc->conn_map, qproc->ext_bhs, EXTERNAL_BHS_ON);
> +}
> +
>  static int q6v5_init_reset(struct q6v5 *qproc)
>  {
>       qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev,
> @@ -2021,6 +2075,7 @@ static int q6v5_probe(struct platform_device *pdev)
>       platform_set_drvdata(pdev, qproc);
>  
>       qproc->has_qaccept_regs = desc->has_qaccept_regs;
> +     qproc->has_ext_bhs_reg = desc->has_ext_bhs_reg;
>       qproc->has_ext_cntl_regs = desc->has_ext_cntl_regs;
>       qproc->has_vq6 = desc->has_vq6;
>       qproc->has_spare_reg = desc->has_spare_reg;
> @@ -2079,6 +2134,12 @@ static int q6v5_probe(struct platform_device *pdev)
>               qproc->proxy_pd_count = ret;
>       }
>  
> +     if (qproc->has_ext_bhs_reg) {
> +             ret = q6v5_external_bhs_enable(qproc);
> +             if (ret < 0)
> +                     goto detach_proxy_pds;
> +     }
> +
>       qproc->has_alt_reset = desc->has_alt_reset;
>       ret = q6v5_init_reset(qproc);
>       if (ret)
> @@ -2143,6 +2204,9 @@ static void q6v5_remove(struct platform_device *pdev)
>       qcom_remove_smd_subdev(rproc, &qproc->smd_subdev);
>       qcom_remove_glink_subdev(rproc, &qproc->glink_subdev);
>  
> +     if (qproc->has_ext_bhs_reg)
> +             q6v5_external_bhs_disable(qproc);

Sorry, just seeing this now: I think this is like the "active_supply"
you just added for MSM8926. It should get enabled when the modem is
started, and disabled when it gets stopped.

The calls to q6v5_external_bhs_enable() / q6v5_external_bhs_disable()
should be next to

        ret = q6v5_regulator_enable(qproc, qproc->active_regs,
                                    qproc->active_reg_count);

and

        q6v5_regulator_disable(qproc, qproc->active_regs,
                               qproc->active_reg_count);

, instead of being called in the driver probe()/remove() function.

Downstream also has this next to the regulator_enable()/disable() [1]
(drv->vreg = mss-supply).

Thanks,
Stephan

[1]: 
https://git.codelinaro.org/clo/la/kernel/msm-3.10/-/blob/LA.BF.1.1.3-02310-8x26.0/arch/arm/mach-msm/pil-msa.c#L71-109

Reply via email to