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 | 93 +++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index c78e431b7b2d..cd8cc911e1be 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -228,6 +228,7 @@ config QCOM_Q6V5_PAS select QCOM_PIL_INFO select QCOM_MDT_LOADER select QCOM_Q6V5_COMMON + select QCOM_QMI_TMD select QCOM_RPROC_COMMON select QCOM_SCM help diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index da27d1d3c9da..ab5bcccc91a6 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> @@ -24,8 +25,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/firmware/qcom,qmi-tmd.h> #include "qcom_common.h" #include "qcom_pil_info.h" @@ -36,6 +39,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; @@ -56,6 +69,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; @@ -120,6 +137,8 @@ struct qcom_pas { struct qcom_scm_pas_context *pas_ctx; struct qcom_scm_pas_context *dtb_pas_ctx; + + struct qmi_tmd_client *tmd_inst; }; static void qcom_pas_segment_dump(struct rproc *rproc, @@ -733,6 +752,66 @@ 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; + + /* Check if TMD mappings are defined */ + 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; + + /* Build the name array from the TMD mappings */ + for (i = 0; i < desc->num_tmd; i++) { + tmd = &desc->tmd_name[i]; + + if (tmd->id < 0 || tmd->id >= desc->num_tmd) { + 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); + if (IS_ERR(tmd_inst)) { + ret = PTR_ERR(tmd_inst); + if (ret == -ENODEV) + return 0; + + dev_err(pas->dev, "Failed to initialize TMD: %d\n", ret); + return ret; + } + + pas->tmd_inst = tmd_inst; + + return 0; +} + static int qcom_pas_probe(struct platform_device *pdev) { const struct qcom_pas_data *desc; @@ -855,12 +934,21 @@ static int qcom_pas_probe(struct platform_device *pdev) pas->pas_ctx->use_tzmem = rproc->has_iommu; pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu; - 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; + 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); @@ -883,6 +971,9 @@ static void qcom_pas_remove(struct platform_device *pdev) { struct qcom_pas *pas = platform_get_drvdata(pdev); + if (pas->tmd_inst) + qmi_tmd_exit(pas->tmd_inst); + rproc_del(pas->rproc); qcom_q6v5_deinit(&pas->q6v5); -- 2.34.1

