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.lehtimaki@xxxxxxxxx> > Signed-off-by: Matti Lehtimäki <matti.lehtimaki@xxxxxxxxx> > Signed-off-by: Luca Weiss <luca@xxxxxxxxxxxx> > --- > 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