On Tue, Jun 09, 2026 at 03:52:57PM +0530, Gaurav Kohli wrote:
> From: Casey Connolly <[email protected]>
> 
> Add a Qualcomm QMI Thermal Mitigation Device (TMD) to support thermal
> cooling devices backed by remote subsystems.
> 
> On several Qualcomm platforms, remote processors (for example modem and
> CDSP) expose thermal mitigation controls through the TMD QMI service.
> Client drivers need a way to discover that service, map DT thermal
> mitigation endpoints to cooling devices, and forward cooling state
> updates to the remote subsystem.
> 
> Co-developed-by: Gaurav Kohli <[email protected]>
> Signed-off-by: Gaurav Kohli <[email protected]>
> Signed-off-by: Casey Connolly <[email protected]>
> Signed-off-by: Daniel Lezcano <[email protected]>

Wrong SoB chain.

> ---
>  MAINTAINERS                      |   6 +
>  drivers/soc/qcom/Kconfig         |  10 +
>  drivers/soc/qcom/Makefile        |   1 +
>  drivers/soc/qcom/qmi_tmd.c       | 604 
> +++++++++++++++++++++++++++++++++++++++
>  include/linux/soc/qcom/qmi.h     |   1 +
>  include/linux/soc/qcom/qmi_tmd.h |  23 ++
>  6 files changed, 645 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 57656ec0e9d5..3d60702a655a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22286,6 +22286,12 @@ F:   
> Documentation/devicetree/bindings/net/qcom,ipq9574-ppe.yaml
>  F:   Documentation/networking/device_drivers/ethernet/qualcomm/ppe/ppe.rst
>  F:   drivers/net/ethernet/qualcomm/ppe/
>  
> +QUALCOMM QMI (REMOTEPROC THERMAL MITIGATION) TMD
> +M:   Gaurav Kohli <[email protected]>
> +L:   [email protected]
> +L:   [email protected]
> +F:   drivers/soc/qcom/qmi_tmd.c
> +
>  QUALCOMM QSEECOM DRIVER
>  M:   Maximilian Luz <[email protected]>
>  L:   [email protected]
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index 2caadbbcf830..a292ce57fd4a 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -128,6 +128,16 @@ config QCOM_QMI_HELPERS
>       tristate
>       depends on NET
>  
> +config QCOM_QMI_TMD
> +     bool "Qualcomm QMI TMD library" if COMPILE_TEST
> +     depends on ARCH_QCOM
> +     select QCOM_QMI_HELPERS
> +     help
> +       This enables the QMI-based Thermal Mitigation Device (TMD) library
> +       for Qualcomm remote subsystems. The library manages TMD messaging and
> +       handles QMI communication with remote processors (modem, CDSP) to
> +       exchange mitigation state and apply thermal mitigation requests.
> +
>  config QCOM_RAMP_CTRL
>       tristate "Qualcomm Ramp Controller driver"
>       depends on ARCH_QCOM || COMPILE_TEST
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index b7f1d2a57367..4544e61c74e7 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_QCOM_PMIC_GLINK)       += pmic_glink.o
>  obj-$(CONFIG_QCOM_PMIC_GLINK)        += pmic_glink_altmode.o
>  obj-$(CONFIG_QCOM_PMIC_PDCHARGER_ULOG)       += pmic_pdcharger_ulog.o
>  CFLAGS_pmic_pdcharger_ulog.o :=  -I$(src)
> +obj-$(CONFIG_QCOM_QMI_TMD) += qmi_tmd.o
>  obj-$(CONFIG_QCOM_QMI_HELPERS)       += qmi_helpers.o
>  qmi_helpers-y        += qmi_encdec.o qmi_interface.o
>  obj-$(CONFIG_QCOM_RAMP_CTRL) += ramp_controller.o
> diff --git a/drivers/soc/qcom/qmi_tmd.c b/drivers/soc/qcom/qmi_tmd.c
> new file mode 100644
> index 000000000000..9d88ae48c864
> --- /dev/null
> +++ b/drivers/soc/qcom/qmi_tmd.c
> @@ -0,0 +1,604 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025, Linaro Limited
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + *
> + * QMI Thermal Mitigation Device (TMD) library.
> + * This library provides cooling device support for remote subsystems
> + * (modem and CDSP) running the TMD service via QMI.

Why are you limiting it to these DSPs only? I quickly checked, my X13s
(sc8280xp) also has one on the ADSP.

> + */
> +#include <linux/cleanup.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/module.h>
> +#include <linux/net.h>
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/soc/qcom/qmi.h>
> +#include <linux/soc/qcom/qmi_tmd.h>
> +#include <linux/thermal.h>
> +
> +#define QMI_TMD_INSTANCE_MODEM       0x0
> +#define QMI_TMD_INSTANCE_CDSP        0x43
> +#define QMI_TMD_INSTANCE_CDSP1       0x44

Other instances? Are those numbers fixed?

Should we pass the instance ID from the PAS driver instead?

> +
> +#define QMI_TMD_SERVICE_VERS_V01 0x01
> +
> +#define QMI_TMD_SET_LEVEL_REQ 0x0021
> +#define QMI_TMD_GET_DEV_LIST_REQ 0x0020
> +
> +#define QMI_TMD_DEV_ID_LEN_MAX 32
> +#define QMI_TMD_DEV_LIST_MAX 32
> +#define QMI_TMD_RESP_TIMEOUT msecs_to_jiffies(100)
> +#define TMD_GET_LEVEL_REQ_MAX_LEN 36
> +#define TMD_SET_LEVEL_REQ_MAX_LEN 40
> +
> +#define TMD_GET_DEV_LIST_REQ_MAX_LEN 0
> +#define TMD_GET_DEV_LIST_RESP_MAX_LEN 1099
> +
> +struct tmd_dev_id {
> +     char mitigation_dev_id[QMI_TMD_DEV_ID_LEN_MAX + 1];
> +};
> +
> +static const struct qmi_elem_info tmd_dev_id_ei[] = {
> +     {
> +             .data_type = QMI_STRING,
> +             .elem_len = QMI_TMD_DEV_ID_LEN_MAX + 1,
> +             .elem_size = sizeof(char),
> +             .array_type = NO_ARRAY,
> +             .tlv_type = 0,
> +             .offset = offsetof(struct tmd_dev_id,
> +                                mitigation_dev_id),
> +     },
> +     {
> +             .data_type = QMI_EOTI,
> +             .array_type = NO_ARRAY,
> +             .tlv_type = QMI_COMMON_TLV_TYPE,
> +     },
> +};
> +
> +struct tmd_dev_list {
> +     struct tmd_dev_id mitigation_dev_id;
> +     u8 max_mitigation_level;
> +};
> +
> +static const struct qmi_elem_info tmd_dev_list_ei[] = {
> +     {
> +             .data_type = QMI_STRUCT,
> +             .elem_len = 1,
> +             .elem_size = sizeof(struct tmd_dev_id),
> +             .array_type = NO_ARRAY,
> +             .tlv_type = 0,
> +             .offset = offsetof(struct tmd_dev_list,
> +                                mitigation_dev_id),
> +             .ei_array = tmd_dev_id_ei,
> +     },
> +     {
> +             .data_type = QMI_UNSIGNED_1_BYTE,
> +             .elem_len = 1,
> +             .elem_size = sizeof(uint8_t),
> +             .array_type = NO_ARRAY,
> +             .tlv_type = 0,
> +             .offset = offsetof(struct tmd_dev_list,
> +                                max_mitigation_level),
> +     },
> +     {
> +             .data_type = QMI_EOTI,
> +             .array_type = NO_ARRAY,
> +             .tlv_type = QMI_COMMON_TLV_TYPE,
> +     },
> +};
> +
> +struct tmd_get_dev_list_req {
> +     char placeholder;
> +};
> +
> +static const struct qmi_elem_info tmd_get_dev_list_req_ei[] = {
> +     {
> +             .data_type = QMI_EOTI,
> +             .array_type = NO_ARRAY,
> +             .tlv_type = QMI_COMMON_TLV_TYPE,
> +     },
> +};
> +
> +struct tmd_get_dev_list_resp {
> +     struct qmi_response_type_v01 resp;
> +     u8 mitigation_device_list_valid;
> +     u32 mitigation_device_list_len;
> +     struct tmd_dev_list
> +             mitigation_device_list[QMI_TMD_DEV_LIST_MAX];
> +};
> +
> +static const struct qmi_elem_info tmd_get_dev_list_resp_ei[] = {
> +     {
> +             .data_type = QMI_STRUCT,
> +             .elem_len = 1,
> +             .elem_size = sizeof(struct qmi_response_type_v01),
> +             .array_type = NO_ARRAY,
> +             .tlv_type = 0x02,
> +             .offset = offsetof(struct tmd_get_dev_list_resp,
> +                                resp),
> +             .ei_array = qmi_response_type_v01_ei,
> +     },
> +     {
> +             .data_type = QMI_OPT_FLAG,
> +             .elem_len = 1,
> +             .elem_size = sizeof(uint8_t),
> +             .array_type = NO_ARRAY,
> +             .tlv_type = 0x10,
> +             .offset = offsetof(struct tmd_get_dev_list_resp,
> +                                mitigation_device_list_valid),
> +     },
> +     {
> +             .data_type = QMI_DATA_LEN,
> +             .elem_len = 1,
> +             .elem_size = sizeof(uint8_t),
> +             .array_type = NO_ARRAY,
> +             .tlv_type = 0x10,
> +             .offset = offsetof(struct tmd_get_dev_list_resp,
> +                                mitigation_device_list_len),
> +     },
> +     {
> +             .data_type = QMI_STRUCT,
> +             .elem_len = QMI_TMD_DEV_LIST_MAX,
> +             .elem_size = sizeof(struct tmd_dev_list),
> +             .array_type = VAR_LEN_ARRAY,
> +             .tlv_type = 0x10,
> +             .offset = offsetof(struct tmd_get_dev_list_resp,
> +                                mitigation_device_list),
> +             .ei_array = tmd_dev_list_ei,
> +     },
> +     {
> +             .data_type = QMI_EOTI,
> +             .array_type = NO_ARRAY,
> +             .tlv_type = QMI_COMMON_TLV_TYPE,
> +     },
> +};
> +
> +struct tmd_set_level_req {
> +     struct tmd_dev_id mitigation_dev_id;
> +     u8 mitigation_level;
> +};
> +
> +static const struct qmi_elem_info tmd_set_level_req_ei[] = {
> +     {
> +             .data_type = QMI_STRUCT,
> +             .elem_len = 1,
> +             .elem_size = sizeof(struct tmd_dev_id),
> +             .array_type = NO_ARRAY,
> +             .tlv_type = 0x01,
> +             .offset = offsetof(struct tmd_set_level_req,
> +                                mitigation_dev_id),
> +             .ei_array = tmd_dev_id_ei,
> +     },
> +     {
> +             .data_type = QMI_UNSIGNED_1_BYTE,
> +             .elem_len = 1,
> +             .elem_size = sizeof(uint8_t),
> +             .array_type = NO_ARRAY,
> +             .tlv_type = 0x02,
> +             .offset = offsetof(struct tmd_set_level_req,
> +                                mitigation_level),
> +     },
> +     {
> +             .data_type = QMI_EOTI,
> +             .array_type = NO_ARRAY,
> +             .tlv_type = QMI_COMMON_TLV_TYPE,
> +     },
> +};
> +
> +struct tmd_set_level_resp {
> +     struct qmi_response_type_v01 resp;
> +};
> +
> +static const struct qmi_elem_info tmd_set_level_resp_ei[] = {
> +     {
> +             .data_type = QMI_STRUCT,
> +             .elem_len = 1,
> +             .elem_size = sizeof(struct qmi_response_type_v01),
> +             .array_type = NO_ARRAY,
> +             .tlv_type = 0x02,
> +             .offset = offsetof(struct tmd_set_level_resp, resp),
> +             .ei_array = qmi_response_type_v01_ei,
> +     },
> +     {
> +             .data_type = QMI_EOTI,
> +             .array_type = NO_ARRAY,
> +             .tlv_type = QMI_COMMON_TLV_TYPE,
> +     },
> +};
> +
> +/**
> + * struct qmi_tmd - A TMD cooling device
> + * @name:    The name of this tmd shared by the remote subsystem
> + * @cdev:    Thermal cooling device handle
> + * @cur_state:       The current mitigation state
> + * @max_state:       The maximum state
> + * @qmi_tmd_cli:     Parent QMI TMD client
> + */
> +struct qmi_tmd {
> +     const char *name;
> +     struct thermal_cooling_device *cdev;
> +     unsigned int cur_state;
> +     unsigned int max_state;
> +     struct qmi_tmd_client *qmi_tmd_cli;
> +};
> +
> +/**
> + * struct qmi_tmd_client - QMI TMD client state
> + * @dev:             Device associated with this instance
> + * @handle:          QMI connection handle
> + * @mutex:           Lock to synchronise QMI communication

What is it protecting?

> + * @connection_active:       Whether or not we're connected to the QMI TMD 
> service
> + * @svc_arrive_work: Work item for initialising when the TMD service starts
> + * @num_tmds:                Number of tmds described in the device tree
> + * @tmds:            An array of tmd structures
> + */
> +struct qmi_tmd_client {
> +     struct device *dev;
> +     struct qmi_handle handle;
> +     /* protects QMI transactions and connection_active */
> +     struct mutex mutex;
> +     bool connection_active;
> +     struct work_struct svc_arrive_work;
> +     int num_tmds;
> +     struct qmi_tmd tmds[] __counted_by(num_tmds);
> +};
> +
> +/* Notify the remote subsystem of the requested cooling state */
> +static int qmi_tmd_send_state_request(struct qmi_tmd *tmd, int state)
> +{
> +     struct tmd_set_level_resp resp = { 0 };
> +     struct tmd_set_level_req req = { 0 };
> +     struct qmi_tmd_client *qmi_tmd_cli = tmd->qmi_tmd_cli;
> +     struct qmi_txn txn;
> +     int ret = 0;
> +
> +     guard(mutex)(&qmi_tmd_cli->mutex);
> +
> +     if (!qmi_tmd_cli->connection_active)
> +             return 0;
> +
> +     strscpy(req.mitigation_dev_id.mitigation_dev_id, tmd->name,
> +             QMI_TMD_DEV_ID_LEN_MAX + 1);
> +     req.mitigation_level = state;
> +
> +     ret = qmi_txn_init(&qmi_tmd_cli->handle, &txn,
> +                        tmd_set_level_resp_ei, &resp);
> +     if (ret < 0) {
> +             dev_err(qmi_tmd_cli->dev, "qmi set state %d txn init failed for 
> %s ret %d\n",
> +                     state, tmd->name, ret);
> +             return ret;
> +     }
> +
> +     ret = qmi_send_request(&qmi_tmd_cli->handle, NULL, &txn,
> +                            QMI_TMD_SET_LEVEL_REQ,
> +                            TMD_SET_LEVEL_REQ_MAX_LEN,
> +                            tmd_set_level_req_ei, &req);
> +     if (ret < 0) {
> +             dev_err(qmi_tmd_cli->dev, "qmi set state %d txn send failed for 
> %s ret %d\n",
> +                     state, tmd->name, ret);
> +             qmi_txn_cancel(&txn);
> +             return ret;
> +     }
> +
> +     ret = qmi_txn_wait(&txn, QMI_TMD_RESP_TIMEOUT);
> +     if (ret < 0) {
> +             dev_err(qmi_tmd_cli->dev, "qmi set state %d txn wait failed for 
> %s ret %d\n",
> +                     state, tmd->name, ret);
> +             return ret;
> +     }
> +
> +     if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
> +             dev_err(qmi_tmd_cli->dev,
> +                     "qmi set state %d failed for %s result %#x error %#x\n",
> +                     state, tmd->name,
> +                     resp.resp.result, resp.resp.error);
> +             return -EREMOTEIO;
> +     }
> +
> +     dev_dbg(qmi_tmd_cli->dev, "Requested state %d/%d for %s\n", state,
> +             tmd->max_state, tmd->name);
> +
> +     return 0;
> +}
> +
> +static int qmi_tmd_get_max_state(struct thermal_cooling_device *cdev,
> +                              unsigned long *state)
> +{
> +     struct qmi_tmd *tmd = cdev->devdata;
> +
> +     *state = tmd->max_state;
> +
> +     return 0;
> +}
> +
> +static int qmi_tmd_get_cur_state(struct thermal_cooling_device *cdev,
> +                              unsigned long *state)
> +{
> +     struct qmi_tmd *tmd = cdev->devdata;
> +
> +     *state = tmd->cur_state;

Mutex protection?

> +
> +     return 0;
> +}
> +
> +static int qmi_tmd_set_cur_state(struct thermal_cooling_device *cdev,
> +                              unsigned long state)
> +{
> +     struct qmi_tmd *tmd = cdev->devdata;
> +     int ret;
> +
> +     if (state > tmd->max_state)
> +             return -EINVAL;
> +
> +     if (tmd->cur_state == state)
> +             return 0;

Hmm, again, mutex protection for the cur_state? Or is it provided by the
thermal core?

> +
> +     ret = qmi_tmd_send_state_request(tmd, state);
> +     if (!ret)
> +             tmd->cur_state = state;
> +
> +     return ret;
> +}
> +
> +static const struct thermal_cooling_device_ops qmi_tmd_cooling_ops = {
> +     .get_max_state = qmi_tmd_get_max_state,
> +     .get_cur_state = qmi_tmd_get_cur_state,
> +     .set_cur_state = qmi_tmd_set_cur_state,
> +};
> +
> +static int qmi_tmd_register(struct qmi_tmd_client *qmi_tmd_cli,
> +                         const char *label, u8 max_state)
> +{
> +     struct device *dev = qmi_tmd_cli->dev;
> +     struct qmi_tmd *tmd;
> +     int index;
> +
> +     for (index = 0; index < qmi_tmd_cli->num_tmds; index++) {
> +             tmd = &qmi_tmd_cli->tmds[index];
> +
> +             if (!strncasecmp(tmd->name, label,
> +                              QMI_TMD_DEV_ID_LEN_MAX + 1))
> +                     goto found;
> +     }
> +
> +     dev_dbg(qmi_tmd_cli->dev,
> +             "TMD '%s' available in firmware but not specified in DT\n",
> +             label);

If we can read them from the firmware, why do you need to specify them
in DT?

> +     return 0;
> +
> +found:
> +     tmd->max_state = max_state;
> +
> +     /*
> +      * If the cooling device already exists then the QMI service went away 
> and
> +      * came back. So just make sure the current cooling device state is
> +      * reflected on the remote side and then return.
> +      */
> +     if (tmd->cdev)
> +             return qmi_tmd_send_state_request(tmd, tmd->cur_state);
> +
> +     tmd->cdev = thermal_of_cooling_device_register(dev->of_node, index,
> +                                                    label, tmd, 
> &qmi_tmd_cooling_ops);
> +     if (IS_ERR(tmd->cdev))
> +             return PTR_ERR(tmd->cdev);
> +
> +     return 0;
> +}
> +
> +static void qmi_tmd_unregister(struct qmi_tmd_client *qmi_tmd_cli)
> +{
> +     struct qmi_tmd *tmd;
> +     int index;
> +
> +     for (index = 0; index < qmi_tmd_cli->num_tmds; index++) {
> +             tmd = &qmi_tmd_cli->tmds[index];
> +
> +             if (!tmd->cdev)
> +                     continue;
> +
> +             thermal_cooling_device_unregister(tmd->cdev);
> +             tmd->cdev = NULL;
> +     }
> +}
> +
> +static void qmi_tmd_svc_arrive(struct work_struct *work)
> +{
> +     struct qmi_tmd_client *qmi_tmd_cli =
> +             container_of(work, struct qmi_tmd_client, svc_arrive_work);
> +
> +     struct tmd_get_dev_list_req req = { 0 };
> +     struct tmd_get_dev_list_resp *resp __free(kfree) = NULL;
> +     int ret, i;
> +     struct qmi_txn txn;
> +
> +     resp = kzalloc_obj(*resp, GFP_KERNEL);
> +     if (!resp) {
> +             ret = -ENOMEM;
> +             goto out;
> +     }
> +
> +     scoped_guard(mutex, &qmi_tmd_cli->mutex) {
> +             ret = qmi_txn_init(&qmi_tmd_cli->handle, &txn,
> +                                tmd_get_dev_list_resp_ei, resp);
> +             if (ret < 0)
> +                     goto out;
> +
> +             ret = qmi_send_request(&qmi_tmd_cli->handle, NULL, &txn,
> +                                    QMI_TMD_GET_DEV_LIST_REQ,
> +                             TMD_GET_DEV_LIST_REQ_MAX_LEN,
> +                             tmd_get_dev_list_req_ei, &req);
> +             if (ret < 0) {
> +                     qmi_txn_cancel(&txn);
> +                     goto out;
> +             }
> +
> +             ret = qmi_txn_wait(&txn, QMI_TMD_RESP_TIMEOUT);
> +             if (ret < 0)
> +                     goto out;
> +
> +             if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
> +                     ret = -EPROTO;
> +                     goto out;
> +             }
> +
> +             qmi_tmd_cli->connection_active = true;
> +     }
> +
> +     for (i = 0; i < resp->mitigation_device_list_len; i++) {
> +             struct tmd_dev_list *device =
> +                     &resp->mitigation_device_list[i];
> +
> +             ret = qmi_tmd_register(qmi_tmd_cli,
> +                                    
> device->mitigation_dev_id.mitigation_dev_id,
> +                                    device->max_mitigation_level);
> +             if (ret)
> +                     break;
> +     }
> +
> +out:
> +     if (ret)
> +             dev_err(qmi_tmd_cli->dev, "Failed to initialize TMD service: 
> %d\n", ret);
> +}
> +
> +static void qmi_tmd_del_server(struct qmi_handle *qmi, struct qmi_service 
> *service)
> +{
> +     struct qmi_tmd_client *qmi_tmd_cli =
> +             container_of(qmi, struct qmi_tmd_client, handle);
> +
> +     kernel_sock_shutdown(qmi->sock, SHUT_RDWR);

So, connection is protected by the mutex, but socket shutdown isn't.
Why?

> +
> +     scoped_guard(mutex, &qmi_tmd_cli->mutex)
> +             qmi_tmd_cli->connection_active = false;
> +}
> +
> +static int qmi_tmd_new_server(struct qmi_handle *qmi, struct qmi_service 
> *service)
> +{
> +     struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port };
> +     struct qmi_tmd_client *qmi_tmd_cli;
> +     int ret;
> +
> +     qmi_tmd_cli = container_of(qmi, struct qmi_tmd_client, handle);
> +
> +     scoped_guard(mutex, &qmi_tmd_cli->mutex) {
> +             ret = kernel_connect(qmi->sock, (struct sockaddr_unsized *)&sq,
> +                                  sizeof(sq), 0);
> +     }
> +
> +     if (ret < 0) {
> +             dev_err(qmi_tmd_cli->dev, "QMI connect failed for node %u port 
> %u: %d\n",
> +                     service->node, service->port, ret);
> +             return ret;
> +     }
> +
> +     queue_work(system_highpri_wq, &qmi_tmd_cli->svc_arrive_work);
> +
> +     return 0;
> +}
> +
> +static const struct qmi_ops qmi_tmd_ops = {
> +     .new_server = qmi_tmd_new_server,
> +     .del_server = qmi_tmd_del_server,
> +};
> +
> +static int qmi_tmd_get_instance_id(const char *remoteproc_name)
> +{
> +     if (!strcmp(remoteproc_name, "modem"))
> +             return QMI_TMD_INSTANCE_MODEM;
> +
> +     if (!strcmp(remoteproc_name, "cdsp"))
> +             return QMI_TMD_INSTANCE_CDSP;
> +
> +     if (!strcmp(remoteproc_name, "cdsp1"))
> +             return QMI_TMD_INSTANCE_CDSP1;
> +
> +     return -ENODEV;

Okay, this definitely should be coming from the PAS driver, being a part
of the platform data.

> +}
> +
> +/**
> + * qmi_tmd_init() - Initialize QMI TMD instance
> + * @dev: Device pointer
> + * @remoteproc_name: Remoteproc name (for example modem, cdsp)
> + * @tmd_names: Array of TMD names
> + * @num_tmds: Number of TMD names
> + *
> + * Return: Pointer to qmi_tmd_client on success, ERR_PTR on failure
> + */
> +struct qmi_tmd_client *qmi_tmd_init(struct device *dev,
> +                                 const char *remoteproc_name,
> +                                 const char * const *tmd_names,
> +                                 int num_tmds)
> +{
> +     struct qmi_tmd_client *qmi_tmd_cli;
> +     int ret, i, instance_id;
> +
> +     if (!dev || !remoteproc_name || !tmd_names || num_tmds <= 0)
> +             return ERR_PTR(-EINVAL);
> +
> +     instance_id = qmi_tmd_get_instance_id(remoteproc_name);
> +     if (instance_id < 0) {
> +             dev_err(dev, "Unsupported remoteproc name '%s' for TMD 
> lookup\n",
> +                     remoteproc_name);
> +             return ERR_PTR(instance_id);
> +     }
> +
> +     qmi_tmd_cli = devm_kzalloc(dev, struct_size(qmi_tmd_cli, tmds, 
> num_tmds), GFP_KERNEL);
> +     if (!qmi_tmd_cli)
> +             return ERR_PTR(-ENOMEM);
> +
> +     qmi_tmd_cli->dev = dev;
> +     qmi_tmd_cli->num_tmds = num_tmds;
> +     mutex_init(&qmi_tmd_cli->mutex);
> +     INIT_WORK(&qmi_tmd_cli->svc_arrive_work, qmi_tmd_svc_arrive);
> +
> +     /* Initialize TMD structures */

Is it a useful comment?

> +     for (i = 0; i < num_tmds; i++) {
> +             qmi_tmd_cli->tmds[i].name = tmd_names[i];
> +             qmi_tmd_cli->tmds[i].qmi_tmd_cli = qmi_tmd_cli;
> +     }
> +
> +     ret = qmi_handle_init(&qmi_tmd_cli->handle,
> +                           TMD_GET_DEV_LIST_RESP_MAX_LEN,
> +                           &qmi_tmd_ops, NULL);
> +     if (ret < 0) {
> +             dev_err(dev, "QMI handle init failed: %d\n", ret);
> +             return ERR_PTR(ret);
> +     }
> +
> +     ret = qmi_add_lookup(&qmi_tmd_cli->handle, QMI_SERVICE_ID_TMD,
> +                          QMI_TMD_SERVICE_VERS_V01, instance_id);
> +     if (ret < 0) {
> +             dev_err(dev, "QMI add lookup failed: %d\n", ret);
> +             goto err_release_handle;
> +     }
> +
> +     return qmi_tmd_cli;
> +
> +err_release_handle:
> +     qmi_handle_release(&qmi_tmd_cli->handle);
> +
> +     return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(qmi_tmd_init);
> +
> +/**
> + * qmi_tmd_exit() - Deinitialize QMI TMD instance
> + * @qmi_tmd_cli: QMI TMD client to deinitialize
> + */
> +void qmi_tmd_exit(struct qmi_tmd_client *qmi_tmd_cli)
> +{
> +     if (!qmi_tmd_cli)
> +             return;
> +
> +     cancel_work_sync(&qmi_tmd_cli->svc_arrive_work);
> +     qmi_handle_release(&qmi_tmd_cli->handle);
> +     qmi_tmd_unregister(qmi_tmd_cli);
> +
> +     scoped_guard(mutex, &qmi_tmd_cli->mutex)
> +             qmi_tmd_cli->connection_active = false;
> +}
> +EXPORT_SYMBOL_GPL(qmi_tmd_exit);

-- 
With best wishes
Dmitry

Reply via email to