On Tue, Jul 21, 2026 at 11:59:00AM +0530, Gaurav Kohli wrote:
> Register Thermal Mitigation Devices (TMDs) for PAS-managed remote
> processors to enable thermal throttling through QMI.
> 
> This allows the thermal framework to request mitigation when remote
> subsystems such as modem and CDSP contribute to thermal pressure.
> 
> Signed-off-by: Gaurav Kohli <[email protected]>
> ---
>  drivers/remoteproc/Kconfig         |  1 +
>  drivers/remoteproc/qcom_q6v5_pas.c | 90 
> +++++++++++++++++++++++++++++++++++++-
>  2 files changed, 90 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 65befdbfa5f7..f7c02edf395f 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -229,6 +229,7 @@ config QCOM_Q6V5_PAS
>       select QCOM_PIL_INFO
>       select QCOM_MDT_LOADER
>       select QCOM_Q6V5_COMMON
> +     select QCOM_QMI_TMD

Sashiko warned here about the missing NET dependencies.

>       select QCOM_RPROC_COMMON
>       select QCOM_SCM
>       select QCOM_PAS
> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c 
> b/drivers/remoteproc/qcom_q6v5_pas.c
> index 25599d728208..dc5be2c030cf 100644
> --- a/drivers/remoteproc/qcom_q6v5_pas.c
> +++ b/drivers/remoteproc/qcom_q6v5_pas.c
> @@ -5,6 +5,7 @@
>   * Copyright (C) 2016 Linaro Ltd
>   * Copyright (C) 2014 Sony Mobile Communications AB
>   * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>   */
>  
>  #include <linux/clk.h>
> @@ -26,8 +27,10 @@
>  #include <linux/regulator/consumer.h>
>  #include <linux/remoteproc.h>
>  #include <linux/soc/qcom/mdt_loader.h>
> +#include <linux/soc/qcom/qmi_tmd.h>
>  #include <linux/soc/qcom/smem.h>
>  #include <linux/soc/qcom/smem_state.h>
> +#include <dt-bindings/thermal/qcom,qmi-tmd.h>
>  
>  #include "qcom_common.h"
>  #include "qcom_pil_info.h"
> @@ -38,6 +41,16 @@
>  
>  #define MAX_ASSIGN_COUNT 3
>  
> +/**
> + * struct tmd_name - TMD device name to cooling-device index mapping
> + * @name: TMD device name
> + * @id: Cooling-device index used as #cooling-cells cell 0 in DT
> + */
> +struct tmd_name {
> +     const char *name;
> +     int id;
> +};
> +
>  struct qcom_pas_data {
>       int crash_reason_smem;
>       const char *firmware_name;
> @@ -58,6 +71,10 @@ struct qcom_pas_data {
>       int ssctl_id;
>       unsigned int smem_host_id;
>  
> +     unsigned int tmd_instance_id;
> +     const struct tmd_name *tmd_name;
> +     int num_tmd;
> +
>       int region_assign_idx;
>       int region_assign_count;
>       bool region_assign_shared;
> @@ -122,6 +139,8 @@ struct qcom_pas {
>  
>       struct qcom_pas_context *pas_ctx;
>       struct qcom_pas_context *dtb_pas_ctx;
> +
> +     struct qmi_tmd_client *tmd_inst;
>  };
>  
>  static void qcom_pas_segment_dump(struct rproc *rproc,
> @@ -798,6 +817,64 @@ static void qcom_pas_unassign_memory_region(struct 
> qcom_pas *pas)
>       }
>  }
>  
> +static int qcom_pas_setup_tmd(struct qcom_pas *pas, const struct 
> qcom_pas_data *desc)
> +{
> +     struct qmi_tmd_client *tmd_inst;
> +     const struct tmd_name *tmd;
> +     const char **tmd_names;
> +     int i, ret;
> +
> +     if (!device_property_present(pas->dev, "#cooling-cells"))
> +             return 0;
> +
> +     if (!desc->tmd_name || desc->num_tmd == 0)
> +             return 0;
> +
> +     tmd_names = devm_kcalloc(pas->dev, desc->num_tmd,
> +                              sizeof(*tmd_names), GFP_KERNEL);
> +     if (!tmd_names)
> +             return -ENOMEM;
> +
> +     for (i = 0; i < desc->num_tmd; i++) {
> +             tmd = &desc->tmd_name[i];
> +
> +             if (tmd->id < 0 || tmd->id >= desc->num_tmd) {

The < 0 check is totally redundant.

For the second check, currently the driver will set num_tmd to
ARRAY_SIZE(desc->tmds), which makes second part either redundant or
invalid. I assume that here you want to compare to some maximum TMD
number to be supported rather than a fixed ARRAY_SIZE().

> +                     dev_err(pas->dev, "Invalid TMD id %d for '%s'\n",
> +                             tmd->id, tmd->name);
> +                     return -EINVAL;
> +             }
> +
> +             if (tmd_names[tmd->id]) {
> +                     dev_err(pas->dev, "Duplicate TMD id %d for '%s'\n",
> +                             tmd->id, tmd->name);
> +                     return -EINVAL;
> +             }
> +
> +             tmd_names[tmd->id] = tmd->name;
> +     }
> +
> +     for (i = 0; i < desc->num_tmd; i++) {
> +             if (!tmd_names[i]) {
> +                     dev_err(pas->dev, "Missing TMD mapping for id %d\n", i);
> +                     return -EINVAL;
> +             }
> +     }
> +
> +     tmd_inst = qmi_tmd_init(pas->dev, desc->tmd_instance_id, tmd_names,
> +                             desc->num_tmd);

And this makes it even more strange. Can there be holes in the
tmd_names? Can platform omit some of the TMDs? Assumingly yes, this
is not correct (nor is devm_kcalloc).

> +     if (IS_ERR(tmd_inst)) {
> +             ret = PTR_ERR(tmd_inst);
> +             if (ret == -ENODEV)
> +                     return 0;
> +
> +             return ret;
> +     }
> +
> +     pas->tmd_inst = tmd_inst;
> +
> +     return 0;
> +}
> +
>  static int qcom_pas_probe(struct platform_device *pdev)
>  {
>       const struct qcom_pas_data *desc;
> @@ -925,16 +1002,24 @@ static int qcom_pas_probe(struct platform_device *pdev)
>       if (desc->early_boot)
>               pas->rproc->state = RPROC_DETACHED;
>  
> -     ret = rproc_add(rproc);
> +     ret = qcom_pas_setup_tmd(pas, desc);
>       if (ret)
>               goto remove_ssr_sysmon;
>  
> +     ret = rproc_add(rproc);
> +     if (ret)
> +             goto remove_setup_tmd;
> +
>       node = of_get_compatible_child(pdev->dev.of_node, "qcom,bam-dmux");
>       pas->bam_dmux = of_platform_device_create(node, NULL, &pdev->dev);
>       of_node_put(node);
>  
>       return 0;
>  
> +remove_setup_tmd:
> +     if (pas->tmd_inst)
> +             qmi_tmd_exit(pas->tmd_inst);
> +
>  remove_ssr_sysmon:
>       qcom_remove_ssr_subdev(rproc, &pas->ssr_subdev);
>       qcom_remove_sysmon_subdev(pas->sysmon);
> @@ -960,6 +1045,9 @@ static void qcom_pas_remove(struct platform_device *pdev)
>       if (pas->bam_dmux)
>               of_platform_device_destroy(&pas->bam_dmux->dev, NULL);
>  
> +     if (pas->tmd_inst)
> +             qmi_tmd_exit(pas->tmd_inst);
> +
>       rproc_del(pas->rproc);
>  
>       qcom_q6v5_deinit(&pas->q6v5);
> 
> -- 
> 2.34.1
> 

-- 
With best wishes
Dmitry

Reply via email to