diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-03 00:55:29 +0200 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-03 00:55:29 +0200 |
| commit | 15423fdbbc0ebe35300958679580fadee60972c8 (patch) | |
| tree | 0ad162394671088ca9f89eebdc060d408a84da14 /drivers | |
| parent | dff88f4aab307adbe6ff3d02f0ef8c3247f10368 (diff) | |
| parent | 5f7775e2a346257aa13128feb2f640cb5a2d6e5b (diff) | |
| download | linux-next-15423fdbbc0ebe35300958679580fadee60972c8.tar.gz linux-next-15423fdbbc0ebe35300958679580fadee60972c8.zip | |
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux.git
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/remoteproc/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/remoteproc/imx_dsp_rproc.c | 7 | ||||
| -rw-r--r-- | drivers/remoteproc/imx_rproc.c | 51 | ||||
| -rw-r--r-- | drivers/remoteproc/omap_remoteproc.c | 12 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_q6v5_adsp.c | 2 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_q6v5_pas.c | 166 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 3 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_debugfs.c | 6 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_elf_loader.c | 23 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_sysfs.c | 7 | ||||
| -rw-r--r-- | drivers/remoteproc/stm32_rproc.c | 2 | ||||
| -rw-r--r-- | drivers/remoteproc/ti_k3_common.c | 10 | ||||
| -rw-r--r-- | drivers/remoteproc/ti_k3_common.h | 4 | ||||
| -rw-r--r-- | drivers/remoteproc/ti_k3_dsp_remoteproc.c | 7 | ||||
| -rw-r--r-- | drivers/remoteproc/ti_k3_m4_remoteproc.c | 4 | ||||
| -rw-r--r-- | drivers/remoteproc/ti_k3_r5_remoteproc.c | 9 | ||||
| -rw-r--r-- | drivers/remoteproc/xlnx_r5_remoteproc.c | 23 | ||||
| -rw-r--r-- | drivers/rpmsg/virtio_rpmsg_bus.c | 156 | ||||
| -rw-r--r-- | drivers/soc/qcom/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/soc/qcom/Makefile | 1 | ||||
| -rw-r--r-- | drivers/soc/qcom/qmi_tmd.c | 595 |
21 files changed, 974 insertions, 137 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 5b56b2dcc725..70dec8c3fe4e 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -83,9 +83,7 @@ config OMAP_REMOTEPROC select OMAP2PLUS_MBOX help Say y here to support OMAP's remote processors (dual M3 - and DSP on OMAP4) via the remote processor framework. - - Currently only supported on OMAP4. + and DSP) via the remote processor framework. Usually you want to say Y here, in order to enable multimedia use-cases to run on your platform (multimedia codecs are @@ -108,7 +106,7 @@ config OMAP_REMOTEPROC_WATCHDOG config WKUP_M3_RPROC tristate "AMx3xx Wakeup M3 remoteproc support" - depends on SOC_AM33XX || SOC_AM43XX + depends on SOC_AM33XX || SOC_AM43XX || COMPILE_TEST help Say y here to support Wakeup M3 remote processor on TI AM33xx and AM43xx family of SoCs. @@ -120,7 +118,7 @@ config WKUP_M3_RPROC config DA8XX_REMOTEPROC tristate "DA8xx/OMAP-L13x remoteproc support" - depends on ARCH_DAVINCI_DA8XX + depends on ARCH_DAVINCI_DA8XX || COMPILE_TEST depends on DMA_CMA help Say y here to support DA8xx/OMAP-L13x remote processors via the @@ -141,7 +139,7 @@ config DA8XX_REMOTEPROC config KEYSTONE_REMOTEPROC tristate "Keystone Remoteproc support" - depends on ARCH_KEYSTONE + depends on ARCH_KEYSTONE || COMPILE_TEST help Say Y here here to support Keystone remote processors (DSP) via the remote processor framework. @@ -238,6 +236,7 @@ config QCOM_Q6V5_PAS select QCOM_PIL_INFO select QCOM_MDT_LOADER select QCOM_Q6V5_COMMON + select QCOM_QMI_TMD if NET select QCOM_RPROC_COMMON select QCOM_SCM select QCOM_PAS diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index fd60c67ba8a9..96d619daf3f5 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -726,14 +726,17 @@ static int imx_dsp_rproc_prepare(struct rproc *rproc) struct device *dev = rproc->dev.parent; int ret; + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + ret = imx_dsp_rproc_add_carveout(priv); if (ret) { dev_err(dev, "failed on imx_dsp_rproc_add_carveout\n"); + pm_runtime_put_sync(dev); return ret; } - pm_runtime_get_sync(dev); - return 0; } diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 745ce52cd822..581bb07f5893 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -24,7 +24,6 @@ #include <linux/regmap.h> #include <linux/remoteproc.h> #include <linux/scmi_imx_protocol.h> -#include <linux/workqueue.h> #include "imx_rproc.h" #include "remoteproc_internal.h" @@ -115,8 +114,6 @@ struct imx_rproc { struct mbox_client cl; struct mbox_chan *tx_ch; struct mbox_chan *rx_ch; - struct work_struct rproc_work; - struct workqueue_struct *workqueue; void __iomem *rsc_table; struct imx_sc_ipc *ipc_handle; struct notifier_block rproc_nb; @@ -540,6 +537,7 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, /* parse address translation table */ for (i = 0; i < dcfg->att_size; i++) { const struct imx_rproc_att *att = &dcfg->att[i]; + u64 offset; /* * Ignore entries not belong to current core: @@ -552,9 +550,11 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, continue; } - if (da >= att->da && da + len < att->da + att->size) { - unsigned int offset = da - att->da; + if (da < att->da) + continue; + offset = da - att->da; + if (offset <= att->size && len <= att->size - offset) { *sys = att->sa + offset; if (is_iomem) *is_iomem = att->flags & ATT_IOMEM; @@ -585,9 +585,14 @@ static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *i return NULL; for (i = 0; i < IMX_RPROC_MEM_MAX; i++) { - if (sys >= priv->mem[i].sys_addr && sys + len < - priv->mem[i].sys_addr + priv->mem[i].size) { - unsigned int offset = sys - priv->mem[i].sys_addr; + u64 offset; + + if (sys < priv->mem[i].sys_addr) + continue; + + offset = sys - priv->mem[i].sys_addr; + if (offset <= priv->mem[i].size && + len <= priv->mem[i].size - offset) { /* __force to make sparse happy with type conversion */ va = (__force void *)(priv->mem[i].cpu_addr + offset); break; @@ -860,21 +865,11 @@ static int imx_rproc_notified_idr_cb(int id, void *ptr, void *data) return 0; } -static void imx_rproc_vq_work(struct work_struct *work) -{ - struct imx_rproc *priv = container_of(work, struct imx_rproc, - rproc_work); - struct rproc *rproc = priv->rproc; - - idr_for_each(&rproc->notifyids, imx_rproc_notified_idr_cb, rproc); -} - static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg) { struct rproc *rproc = dev_get_drvdata(cl->dev); - struct imx_rproc *priv = rproc->priv; - queue_work(priv->workqueue, &priv->rproc_work); + idr_for_each(&rproc->notifyids, imx_rproc_notified_idr_cb, rproc); } static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block) @@ -1239,13 +1234,6 @@ static int imx_rproc_sys_off_handler(struct sys_off_data *data) return NOTIFY_DONE; } -static void imx_rproc_destroy_workqueue(void *data) -{ - struct workqueue_struct *workqueue = data; - - destroy_workqueue(workqueue); -} - static int imx_rproc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1273,17 +1261,6 @@ static int imx_rproc_probe(struct platform_device *pdev) priv->ops = dcfg->ops; dev_set_drvdata(dev, rproc); - priv->workqueue = create_workqueue(dev_name(dev)); - if (!priv->workqueue) { - dev_err(dev, "cannot create workqueue\n"); - return -ENOMEM; - } - - ret = devm_add_action_or_reset(dev, imx_rproc_destroy_workqueue, priv->workqueue); - if (ret) - return dev_err_probe(dev, ret, "Failed to add devm destroy workqueue action\n"); - - INIT_WORK(&priv->rproc_work, imx_rproc_vq_work); ret = imx_rproc_xtr_mbox_init(rproc, true); if (ret) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 6ed0f28edac9..1e96506ce7ca 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -499,7 +499,7 @@ static void omap_rproc_mbox_callback(struct mbox_client *client, void *data) client); struct device *dev = oproc->rproc->dev.parent; const char *name = oproc->rproc->name; - u32 msg = (u32)data; + mbox_msg_t msg = omap_mbox_from_message(data); dev_dbg(dev, "mbox msg: 0x%x\n", msg); @@ -550,7 +550,7 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid) } /* send the index of the triggered virtqueue in the mailbox payload */ - ret = mbox_send_message(oproc->mbox, (void *)vqid); + ret = mbox_send_message(oproc->mbox, omap_mbox_to_message(vqid)); if (ret < 0) dev_err(dev, "failed to send mailbox message, status = %d\n", ret); @@ -628,7 +628,7 @@ static int omap_rproc_start(struct rproc *rproc) * Note that the reply will _not_ arrive immediately: this message * will wait in the mailbox fifo until the remote processor is booted. */ - ret = mbox_send_message(oproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); + ret = mbox_send_message(oproc->mbox, omap_mbox_to_message(RP_MBOX_ECHO_REQUEST)); if (ret < 0) { dev_err(dev, "mbox_send_message failed: %d\n", ret); goto put_mbox; @@ -777,13 +777,13 @@ static int _omap_rproc_suspend(struct rproc *rproc, bool auto_suspend) struct omap_rproc *oproc = rproc->priv; unsigned long to = msecs_to_jiffies(DEF_SUSPEND_TIMEOUT); unsigned long ta = jiffies + to; - u32 suspend_msg = auto_suspend ? + mbox_msg_t suspend_msg = auto_suspend ? RP_MBOX_SUSPEND_AUTO : RP_MBOX_SUSPEND_SYSTEM; int ret; reinit_completion(&oproc->pm_comp); oproc->suspend_acked = false; - ret = mbox_send_message(oproc->mbox, (void *)suspend_msg); + ret = mbox_send_message(oproc->mbox, omap_mbox_to_message(suspend_msg)); if (ret < 0) { dev_err(dev, "PM mbox_send_message failed: %d\n", ret); return ret; @@ -1208,7 +1208,7 @@ static int omap_rproc_of_get_internal_memories(struct platform_device *pdev, oproc->mem[i].dev_addr = data->mems[i].dev_addr; oproc->mem[i].size = resource_size(res); - dev_dbg(dev, "memory %8s: bus addr %pa size 0x%x va %p da 0x%x\n", + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %p da 0x%x\n", data->mems[i].name, &oproc->mem[i].bus_addr, oproc->mem[i].size, oproc->mem[i].cpu_addr, oproc->mem[i].dev_addr); diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 2e5fb5954fa9..46d9169a37f6 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -430,7 +430,7 @@ static int adsp_start(struct rproc *rproc) goto disable_adsp_clks; } - ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5 * HZ)); + ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000)); if (ret == -ETIMEDOUT) { dev_err(adsp->dev, "start timed out\n"); goto disable_adsp_clks; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index a005546c265d..2e1e39826ffa 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,11 @@ #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/power/qcom,rpmhpd.h> +#include <dt-bindings/thermal/qcom,pas.h> #include "qcom_common.h" #include "qcom_pil_info.h" @@ -38,6 +42,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; @@ -51,6 +65,8 @@ struct qcom_pas_data { bool decrypt_shutdown; char **proxy_pd_names; + const unsigned int *proxy_pd_performance_states; + unsigned int num_proxy_pd_performance_states; const char *load_state; const char *ssr_name; @@ -58,6 +74,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; @@ -79,6 +99,7 @@ struct qcom_pas { struct regulator *px_supply; struct device *proxy_pds[3]; + const unsigned int *proxy_pd_performance_states; int proxy_pd_count; @@ -123,6 +144,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, @@ -167,7 +190,12 @@ static int qcom_pas_pds_enable(struct qcom_pas *pas, struct device **pds, int i; for (i = 0; i < pd_count; i++) { - dev_pm_genpd_set_performance_state(pds[i], INT_MAX); + unsigned int state = INT_MAX; + + if (pas->proxy_pd_performance_states) + state = pas->proxy_pd_performance_states[i]; + + dev_pm_genpd_set_performance_state(pds[i], state); ret = pm_runtime_get_sync(pds[i]); if (ret < 0) { pm_runtime_put_noidle(pds[i]); @@ -818,6 +846,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 >= 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; + + return ret; + } + + pas->tmd_inst = tmd_inst; + + return 0; +} + static int qcom_pas_probe(struct platform_device *pdev) { const struct qcom_pas_data *desc; @@ -873,6 +959,7 @@ static int qcom_pas_probe(struct platform_device *pdev) pas->info_name = desc->sysmon_name; pas->smem_host_id = desc->smem_host_id; pas->decrypt_shutdown = desc->decrypt_shutdown; + pas->proxy_pd_performance_states = desc->proxy_pd_performance_states; pas->region_assign_idx = desc->region_assign_idx; pas->region_assign_count = min_t(int, MAX_ASSIGN_COUNT, desc->region_assign_count); pas->region_assign_vmid = desc->region_assign_vmid; @@ -908,6 +995,15 @@ static int qcom_pas_probe(struct platform_device *pdev) goto unassign_mem; pas->proxy_pd_count = ret; + if (desc->proxy_pd_performance_states && + desc->num_proxy_pd_performance_states != pas->proxy_pd_count) { + dev_err(&pdev->dev, + "proxy_pd_performance_states count %u != pd count %d\n", + desc->num_proxy_pd_performance_states, pas->proxy_pd_count); + ret = -EINVAL; + goto detach_proxy_pds; + } + ret = qcom_q6v5_init(&pas->q6v5, pdev, rproc, desc->crash_reason_smem, desc->load_state, qcom_pas_handover); if (ret) @@ -931,16 +1027,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); @@ -968,6 +1072,9 @@ static void qcom_pas_remove(struct platform_device *pdev) rproc_del(pas->rproc); + if (pas->tmd_inst) + qmi_tmd_exit(pas->tmd_inst); + qcom_q6v5_deinit(&pas->q6v5); qcom_pas_unassign_memory_region(pas); qcom_remove_glink_subdev(pas->rproc, &pas->glink_subdev); @@ -979,6 +1086,15 @@ static void qcom_pas_remove(struct platform_device *pdev) device_init_wakeup(pas->dev, false); } +static const struct tmd_name cdsp_tmd_name[] = { + { .name = "cdsp_sw", .id = QCOM_TMD_CDSP_SW }, +}; + +static const struct tmd_name modem_tmd_name[] = { + { .name = "pa", .id = QCOM_TMD_PA }, + { .name = "modem", .id = QCOM_TMD_MODEM }, +}; + static const struct qcom_pas_data adsp_resource_init = { .crash_reason_smem = 423, .firmware_name = "adsp.mdt", @@ -1136,6 +1252,9 @@ static const struct qcom_pas_data sa8775p_cdsp0_resource = { .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, + .tmd_instance_id = 0x43, + .tmd_name = cdsp_tmd_name, + .num_tmd = ARRAY_SIZE(cdsp_tmd_name), }; static const struct qcom_pas_data sa8775p_cdsp1_resource = { @@ -1154,6 +1273,9 @@ static const struct qcom_pas_data sa8775p_cdsp1_resource = { .ssr_name = "cdsp1", .sysmon_name = "cdsp1", .ssctl_id = 0x20, + .tmd_instance_id = 0x44, + .tmd_name = cdsp_tmd_name, + .num_tmd = ARRAY_SIZE(cdsp_tmd_name), }; static const struct qcom_pas_data sdm845_cdsp_resource_init = { @@ -1181,6 +1303,9 @@ static const struct qcom_pas_data sm6350_cdsp_resource = { .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, + .tmd_instance_id = 0x43, + .tmd_name = cdsp_tmd_name, + .num_tmd = ARRAY_SIZE(cdsp_tmd_name), }; static const struct qcom_pas_data sm8150_cdsp_resource = { @@ -1196,6 +1321,9 @@ static const struct qcom_pas_data sm8150_cdsp_resource = { .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, + .tmd_instance_id = 0x43, + .tmd_name = cdsp_tmd_name, + .num_tmd = ARRAY_SIZE(cdsp_tmd_name), }; static const struct qcom_pas_data sm8250_cdsp_resource = { @@ -1280,6 +1408,9 @@ static const struct qcom_pas_data x1e80100_cdsp_resource = { .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, + .tmd_instance_id = 0x43, + .tmd_name = cdsp_tmd_name, + .num_tmd = ARRAY_SIZE(cdsp_tmd_name), }; static const struct qcom_pas_data sm8350_cdsp_resource = { @@ -1348,6 +1479,9 @@ static const struct qcom_pas_data mpss_resource_init = { .ssr_name = "mpss", .sysmon_name = "modem", .ssctl_id = 0x12, + .tmd_instance_id = 0x0, + .tmd_name = modem_tmd_name, + .num_tmd = ARRAY_SIZE(modem_tmd_name), }; static const struct qcom_pas_data sc8180x_mpss_resource = { @@ -1798,6 +1932,33 @@ static const struct qcom_pas_data glymur_soccp_resource = { .needs_tzmem = true, }; +static const struct qcom_pas_data hawi_cdsp_resource = { + .crash_reason_smem = 601, + .firmware_name = "cdsp.mdt", + .dtb_firmware_name = "cdsp_dtb.mdt", + .pas_id = 18, + .dtb_pas_id = 0x25, + .minidump_id = 7, + .auto_boot = true, + .proxy_pd_names = (char*[]){ + "cx", + "mxc", + "nsp", + NULL + }, + .proxy_pd_performance_states = (const unsigned int[]){ + RPMH_REGULATOR_LEVEL_TURBO, + RPMH_REGULATOR_LEVEL_TURBO, + RPMH_REGULATOR_LEVEL_NOM, + }, + .num_proxy_pd_performance_states = 3, + .load_state = "cdsp", + .ssr_name = "cdsp", + .sysmon_name = "cdsp", + .ssctl_id = 0x17, + .smem_host_id = 5, +}; + static const struct qcom_pas_data eliza_cdsp_resource = { .crash_reason_smem = 601, .firmware_name = "cdsp.mbn", @@ -1827,6 +1988,7 @@ static const struct of_device_id qcom_pas_of_match[] = { { .compatible = "qcom,eliza-adsp-pas", .data = &sm8550_adsp_resource }, { .compatible = "qcom,eliza-cdsp-pas", .data = &eliza_cdsp_resource }, { .compatible = "qcom,glymur-soccp-pas", .data = &glymur_soccp_resource }, + { .compatible = "qcom,hawi-cdsp-pas", .data = &hawi_cdsp_resource }, { .compatible = "qcom,kaanapali-soccp-pas", .data = &kaanapali_soccp_resource }, { .compatible = "qcom,milos-adsp-pas", .data = &sm8550_adsp_resource }, { .compatible = "qcom,milos-cdsp-pas", .data = &milos_cdsp_resource }, diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index d131b82af320..4bf69003b7da 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -574,6 +574,9 @@ static int rproc_handle_trace(struct rproc *rproc, void *ptr, /* create the debugfs entry */ trace->tfile = rproc_create_trace_file(name, rproc, trace); + /* keep the name for the diagnostic in rproc_trace_read() */ + strscpy(trace->trace_mem.name, name, sizeof(trace->trace_mem.name)); + list_add_tail(&trace->node, &rproc->traces); rproc->num_traces++; diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index b86c1d09c70c..e8e7b6a3769b 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -347,8 +347,8 @@ static int rproc_rsc_table_show(struct seq_file *seq, void *p) } break; default: - seq_printf(seq, "Unknown resource type found: %d [hdr: %pK]\n", - hdr->type, hdr); + seq_printf(seq, "Unknown resource type found: %d\n", + hdr->type); break; } } @@ -367,7 +367,7 @@ static int rproc_carveouts_show(struct seq_file *seq, void *p) list_for_each_entry(carveout, &rproc->carveouts, node) { seq_puts(seq, "Carveout memory entry:\n"); seq_printf(seq, "\tName: %s\n", carveout->name); - seq_printf(seq, "\tVirtual address: %pK\n", carveout->va); + seq_printf(seq, "\tVirtual address: %p\n", carveout->va); seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma); seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da); seq_printf(seq, "\tLength: 0x%zx Bytes\n\n", carveout->len); diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c index 94177e416047..da3cddbe7d4c 100644 --- a/drivers/remoteproc/remoteproc_elf_loader.c +++ b/drivers/remoteproc/remoteproc_elf_loader.c @@ -46,8 +46,9 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw) struct elf32_hdr *ehdr; u32 elf_shdr_get_size; u64 phoff, shoff; + size_t shend; char class; - u16 phnum; + u16 phnum, shnum, shstrndx; if (!fw) { dev_err(dev, "failed to load %s\n", name); @@ -90,9 +91,27 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw) phoff = elf_hdr_get_e_phoff(class, fw->data); shoff = elf_hdr_get_e_shoff(class, fw->data); phnum = elf_hdr_get_e_phnum(class, fw->data); + shnum = elf_hdr_get_e_shnum(class, fw->data); + shstrndx = elf_hdr_get_e_shstrndx(class, fw->data); elf_shdr_get_size = elf_size_of_shdr(class); - if (fw->size < shoff + elf_shdr_get_size) { + /* keeps shoff in size_t range for the two bounds below */ + if (shoff > fw->size) { + dev_err(dev, "Section header table is out of bounds\n"); + return -EINVAL; + } + + if (shnum) { + shend = size_add(size_mul(elf_shdr_get_size, shnum), shoff); + if (shend > fw->size) { + dev_err(dev, "Section headers are out of bounds\n"); + return -EINVAL; + } + } + + /* find_table() reads the header at shstrndx even with no sections */ + shend = size_add(size_mul(elf_shdr_get_size, (size_t)shstrndx + 1), shoff); + if (shend > fw->size) { dev_err(dev, "Image is too small\n"); return -EINVAL; } diff --git a/drivers/remoteproc/remoteproc_sysfs.c b/drivers/remoteproc/remoteproc_sysfs.c index 925b0cdbe577..c37736ff4acc 100644 --- a/drivers/remoteproc/remoteproc_sysfs.c +++ b/drivers/remoteproc/remoteproc_sysfs.c @@ -76,7 +76,7 @@ static const char * const rproc_coredump_str[] = { [RPROC_COREDUMP_INLINE] = "inline", }; -/* Expose the current coredump configuration via debugfs */ +/* Expose the current coredump configuration via sysfs */ static ssize_t coredump_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -87,14 +87,15 @@ static ssize_t coredump_show(struct device *dev, /* * By writing to the 'coredump' sysfs entry, we control the behavior of the - * coredump mechanism dynamically. The default value of this entry is "default". + * coredump mechanism dynamically. The default value of this entry is + * "disabled". * * The 'coredump' sysfs entry supports these commands: * * disabled: This is the default coredump mechanism. Recovery will proceed * without collecting any dump. * - * default: When the remoteproc crashes the entire coredump will be + * enabled: When the remoteproc crashes the entire coredump will be * copied to a separate buffer and exposed to userspace. * * inline: The coredump will not be copied to a separate buffer and the diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 1fe4cdc0a13a..a47e040c6db8 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -675,7 +675,7 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev, int err, irq; irq = platform_get_irq_optional(pdev, 0); - if (irq == -EPROBE_DEFER) + if (irq < 0 && irq != -ENXIO) return irq; if (irq > 0) { diff --git a/drivers/remoteproc/ti_k3_common.c b/drivers/remoteproc/ti_k3_common.c index 3cb8ae5d72f6..4b6da3363f6c 100644 --- a/drivers/remoteproc/ti_k3_common.c +++ b/drivers/remoteproc/ti_k3_common.c @@ -54,7 +54,7 @@ void k3_rproc_mbox_callback(struct mbox_client *client, void *data) struct k3_rproc *kproc = container_of(client, struct k3_rproc, client); struct device *dev = kproc->rproc->dev.parent; struct rproc *rproc = kproc->rproc; - u32 msg = (u32)(uintptr_t)(data); + mbox_msg_t msg = omap_mbox_from_message(data); dev_dbg(dev, "mbox msg: 0x%x\n", msg); @@ -94,15 +94,9 @@ void k3_rproc_kick(struct rproc *rproc, int vqid) { struct k3_rproc *kproc = rproc->priv; struct device *dev = kproc->dev; - u32 msg = (u32)vqid; int ret; - /* - * Send the index of the triggered virtqueue in the mailbox payload. - * NOTE: msg is cast to uintptr_t to prevent compiler warnings when - * void* is 64bit. It is safely cast back to u32 in the mailbox driver. - */ - ret = mbox_send_message(kproc->mbox, (void *)(uintptr_t)msg); + ret = mbox_send_message(kproc->mbox, omap_mbox_to_message(vqid)); if (ret < 0) dev_err(dev, "failed to send mailbox message, status = %d\n", ret); diff --git a/drivers/remoteproc/ti_k3_common.h b/drivers/remoteproc/ti_k3_common.h index aee3c28dbe51..c8430e42225f 100644 --- a/drivers/remoteproc/ti_k3_common.h +++ b/drivers/remoteproc/ti_k3_common.h @@ -21,6 +21,10 @@ #ifndef REMOTEPROC_TI_K3_COMMON_H #define REMOTEPROC_TI_K3_COMMON_H +#include <linux/mailbox_client.h> +#include <linux/platform_device.h> +#include <linux/types.h> + #define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK (SZ_16M - 1) /** diff --git a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c index d6ceea6dc920..6582c7404e54 100644 --- a/drivers/remoteproc/ti_k3_dsp_remoteproc.c +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c @@ -7,11 +7,8 @@ */ #include <linux/io.h> -#include <linux/mailbox_client.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_reserved_mem.h> -#include <linux/omap-mailbox.h> #include <linux/platform_device.h> #include <linux/remoteproc.h> #include <linux/reset.h> @@ -154,6 +151,10 @@ static int k3_dsp_rproc_probe(struct platform_device *pdev) dev_info(dev, "configured DSP for remoteproc mode\n"); } + ret = dma_coerce_mask_and_coherent(&rproc->dev, DMA_BIT_MASK(48)); + if (ret) + dev_warn(dev, "Failed to set DMA mask (%d)\n", ret); + ret = devm_rproc_add(dev, rproc); if (ret) return dev_err_probe(dev, ret, "failed to add register device with remoteproc core\n"); diff --git a/drivers/remoteproc/ti_k3_m4_remoteproc.c b/drivers/remoteproc/ti_k3_m4_remoteproc.c index 3a11fd24eb52..4c46f4a256a9 100644 --- a/drivers/remoteproc/ti_k3_m4_remoteproc.c +++ b/drivers/remoteproc/ti_k3_m4_remoteproc.c @@ -7,10 +7,8 @@ */ #include <linux/io.h> -#include <linux/mailbox_client.h> #include <linux/module.h> -#include <linux/of_address.h> -#include <linux/of_reserved_mem.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/remoteproc.h> #include <linux/reset.h> diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index b1d04d082e44..d7d9756afed0 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -6,17 +6,12 @@ * Suman Anna <s-anna@ti.com> */ -#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/mailbox_client.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> -#include <linux/of_reserved_mem.h> #include <linux/of_platform.h> -#include <linux/omap-mailbox.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/remoteproc.h> @@ -1047,6 +1042,10 @@ static int k3_r5_cluster_rproc_init(struct platform_device *pdev) goto out; } + ret = dma_coerce_mask_and_coherent(&rproc->dev, DMA_BIT_MASK(48)); + if (ret) + dev_warn(dev, "Failed to set DMA mask (%d)\n", ret); + /* K3 R5s have a Region Address Translator (RAT) but no MMU */ rproc->has_iommu = false; /* error recovery is not supported at present */ diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index c685bb5fa62c..a9873a37dd8f 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -318,6 +318,8 @@ static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) if (!ipi) return NULL; + INIT_WORK(&ipi->mbox_work, handle_event_notified); + mbox_cl = &ipi->mbox_cl; mbox_cl->rx_callback = zynqmp_r5_mb_rx_cb; mbox_cl->tx_block = false; @@ -329,6 +331,7 @@ static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) ipi->tx_chan = mbox_request_channel_byname(mbox_cl, "tx"); if (IS_ERR(ipi->tx_chan)) { ipi->tx_chan = NULL; + cancel_work_sync(&ipi->mbox_work); kfree(ipi); dev_warn(cdev, "mbox tx channel request failed\n"); return NULL; @@ -339,13 +342,12 @@ static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) mbox_free_channel(ipi->tx_chan); ipi->rx_chan = NULL; ipi->tx_chan = NULL; + cancel_work_sync(&ipi->mbox_work); kfree(ipi); dev_warn(cdev, "mbox rx channel request failed\n"); return NULL; } - INIT_WORK(&ipi->mbox_work, handle_event_notified); - return ipi; } @@ -364,6 +366,8 @@ static void zynqmp_r5_free_mbox(struct mbox_info *ipi) ipi->rx_chan = NULL; } + cancel_work_sync(&ipi->mbox_work); + kfree(ipi); } @@ -381,7 +385,10 @@ static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid) int ret; ipi = r5_core->ipi; - if (!ipi) + if (!ipi || !ipi->tx_chan) + return; + + if (mbox_chan_tx_slots_available(ipi->tx_chan) == 0) return; mb_msg = (struct zynqmp_ipi_message *)ipi->tx_mc_buf; @@ -568,7 +575,7 @@ static int tcm_mem_map(struct rproc *rproc, return -ENOMEM; /* Update memory entry va */ - mem->va = (void *)va; + mem->va = (__force void *)va; /* clear TCMs */ memset_io(va, 0, mem->len); @@ -746,7 +753,7 @@ static struct resource_table *zynqmp_r5_get_loaded_rsc_table(struct rproc *rproc *size = r5_core->rsc_tbl_size; - return (struct resource_table *)r5_core->rsc_tbl_va; + return (__force struct resource_table *)r5_core->rsc_tbl_va; } static int zynqmp_r5_get_rsc_table_va(struct zynqmp_r5_core *r5_core) @@ -770,7 +777,7 @@ static int zynqmp_r5_get_rsc_table_va(struct zynqmp_r5_core *r5_core) return -EINVAL; } - rsc_data_va = (struct rsc_tbl_data *)ioremap_wc(res_mem.start, + rsc_data_va = (__force struct rsc_tbl_data *)ioremap_wc(res_mem.start, sizeof(struct rsc_tbl_data)); if (!rsc_data_va) { dev_err(dev, "failed to map resource table data address\n"); @@ -794,7 +801,7 @@ static int zynqmp_r5_get_rsc_table_va(struct zynqmp_r5_core *r5_core) return -EINVAL; } - rsc_tbl_addr = (struct resource_table *)r5_core->rsc_tbl_va; + rsc_tbl_addr = (__force struct resource_table *)r5_core->rsc_tbl_va; /* * As of now resource table version 1 is expected. Don't fail to attach @@ -834,7 +841,7 @@ static int zynqmp_r5_handle_rsc(struct rproc *rproc, u32 rsc_type, void *rsc, int offset, int avail) { struct zynqmp_r5_core *r5_core = rproc->priv; - void *rsc_offset = (r5_core->rsc_tbl_va + offset); + void *rsc_offset = (__force void *)(r5_core->rsc_tbl_va + offset); if (rsc_type != XLNX_RPROC_FW_CRASH_REPORT) return RSC_IGNORED; diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 1b8bb05924af..40ec93fedde9 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -20,6 +20,7 @@ #include <linux/rpmsg.h> #include <linux/rpmsg/byteorder.h> #include <linux/rpmsg/ns.h> +#include <linux/rpmsg/virtio_rpmsg.h> #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/sched.h> @@ -35,13 +36,15 @@ * @vdev: the virtio device * @rvq: rx virtqueue * @svq: tx virtqueue - * @rbufs: kernel address of rx buffers - * @sbufs: kernel address of tx buffers - * @num_bufs: total number of buffers for rx and tx - * @buf_size: size of one rx or tx buffer - * @last_sbuf: index of last tx buffer used + * @rx_bufs: kernel address of rx buffers + * @tx_bufs: kernel address of tx buffers + * @num_rx_buf: total number of rx buffers + * @num_tx_buf: total number of tx buffers + * @rx_buf_size: size of one rx buffer + * @tx_buf_size: size of one tx buffer + * @last_tx_buf: index of last tx buffer used * @bufs_dma: dma base addr of the buffers - * @tx_lock: protects svq and sbufs, to allow concurrent senders. + * @tx_lock: protects svq and tx_bufs, to allow concurrent senders. * sending a message might require waking up a dozing remote * processor, which involves sleeping, hence the mutex. * @endpoints: idr of local endpoints, allows fast retrieval @@ -55,10 +58,12 @@ struct virtproc_info { struct virtio_device *vdev; struct virtqueue *rvq, *svq; - void *rbufs, *sbufs; - unsigned int num_bufs; - unsigned int buf_size; - int last_sbuf; + void *rx_bufs, *tx_bufs; + unsigned int num_rx_buf; + unsigned int num_tx_buf; + unsigned int rx_buf_size; + unsigned int tx_buf_size; + int last_tx_buf; dma_addr_t bufs_dma; struct mutex tx_lock; struct idr endpoints; @@ -66,9 +71,6 @@ struct virtproc_info { wait_queue_head_t sendq; }; -/* The feature bitmap for virtio rpmsg */ -#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ - /** * struct rpmsg_hdr - common header for all rpmsg messages * @src: source address @@ -110,7 +112,7 @@ struct virtio_rpmsg_channel { /* * We're allocating buffers of 512 bytes each for communications. The * number of buffers will be computed from the number of buffers supported - * by the vring, upto a maximum of 512 buffers (256 in each direction). + * by the vring, up to a maximum of 256 in each direction. * * Each buffer will have 16 bytes for the msg header and 496 bytes for * the payload. @@ -125,8 +127,8 @@ struct virtio_rpmsg_channel { * can change this without changing anything in the firmware of the remote * processor. */ -#define MAX_RPMSG_NUM_BUFS (512) -#define MAX_RPMSG_BUF_SIZE (512) +#define MAX_RPMSG_NUM_BUFS (256) +#define DEFAULT_RPMSG_BUF_SIZE (512) /* * Local addresses are dynamically allocated on-demand. @@ -439,12 +441,9 @@ static void *get_a_tx_buf(struct virtproc_info *vrp) mutex_lock(&vrp->tx_lock); - /* - * either pick the next unused tx buffer - * (half of our buffers are used for sending messages) - */ - if (vrp->last_sbuf < vrp->num_bufs / 2) - ret = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++; + /* either pick the next unused tx buffer */ + if (vrp->last_tx_buf < vrp->num_tx_buf) + ret = vrp->tx_bufs + vrp->tx_buf_size * vrp->last_tx_buf++; /* or recycle a used one */ else ret = virtqueue_get_buf(vrp->svq, &len); @@ -514,7 +513,7 @@ static int rpmsg_send_offchannel_raw(struct rpmsg_device *rpdev, * messaging), or to improve the buffer allocator, to support * variable-length buffer sizes. */ - if (len > vrp->buf_size - sizeof(struct rpmsg_hdr)) { + if (len > vrp->tx_buf_size - sizeof(struct rpmsg_hdr)) { dev_err(dev, "message is too big (%d)\n", len); return -EMSGSIZE; } @@ -630,11 +629,10 @@ static __poll_t virtio_rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, /* * check for a free buffer, either: - * - we haven't used all of the available transmit buffers (half of the - * allocated buffers are used for transmit, hence num_bufs / 2), or, + * - we haven't used all of the available transmit buffers or, * - we ask the virtqueue if there's a buffer available */ - if (vrp->last_sbuf < vrp->num_bufs / 2 || + if (vrp->last_tx_buf < vrp->num_tx_buf || !virtqueue_enable_cb(vrp->svq)) mask |= EPOLLOUT; @@ -648,7 +646,7 @@ static ssize_t virtio_rpmsg_get_mtu(struct rpmsg_endpoint *ept) struct rpmsg_device *rpdev = ept->rpdev; struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); - return vch->vrp->buf_size - sizeof(struct rpmsg_hdr); + return vch->vrp->tx_buf_size - sizeof(struct rpmsg_hdr); } static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, @@ -674,7 +672,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, * We currently use fixed-sized buffers, so trivially sanitize * the reported payload length. */ - if (len > vrp->buf_size || + if (len > vrp->rx_buf_size || msg_len > (len - sizeof(struct rpmsg_hdr))) { dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg_len); return -EINVAL; @@ -707,7 +705,7 @@ static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, dev_warn_ratelimited(dev, "msg received with no recipient\n"); /* publish the real size of the buffer */ - rpmsg_sg_init(&sg, msg, vrp->buf_size); + rpmsg_sg_init(&sg, msg, vrp->rx_buf_size); /* add the buffer back to the remote processor's virtqueue */ err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, msg, GFP_KERNEL); @@ -811,6 +809,65 @@ static void rpmsg_virtio_del_ctrl_dev(struct rpmsg_device *rpdev_ctrl) device_unregister(&rpdev_ctrl->dev); } +static int rpmsg_virtio_get_buf_size(struct virtproc_info *vrp) +{ + struct virtio_device *vdev; + u8 version; + u16 size; + + vdev = vrp->vdev; + + /* + * If VIRTIO_RPMSG_F_BUFSZ feature is supported, then configure buf + * size from virtio device config space from the resource table. + * If the feature is not supported, then assign default buf size. + */ + if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_BUFSZ)) { + virtio_cread(vdev, struct virtio_rpmsg_config, + version, &version); + + /* for now we support only v1 */ + if (version != RPMSG_VDEV_CONFIG_V1) { + dev_err(&vdev->dev, + "unsupported vdev config version %u\n", version); + return -EINVAL; + } + + /* size of the config space must match */ + virtio_cread(vdev, struct virtio_rpmsg_config, + size, &size); + if (size != sizeof(struct virtio_rpmsg_config)) { + dev_err(&vdev->dev, "invalid size of vdev config %u\n", + size); + return -EINVAL; + } + + /* note: tx and rx are defined from remote view */ + virtio_cread(vdev, struct virtio_rpmsg_config, + txbuf_size, &vrp->rx_buf_size); + virtio_cread(vdev, struct virtio_rpmsg_config, + rxbuf_size, &vrp->tx_buf_size); + + /* The buffers must hold at least the rpmsg header */ + if (vrp->rx_buf_size < sizeof(struct rpmsg_hdr) || + vrp->tx_buf_size < sizeof(struct rpmsg_hdr)) { + dev_err(&vdev->dev, + "bad vdev config: rx buf sz = %u, tx buf sz = %u\n", + vrp->rx_buf_size, vrp->tx_buf_size); + return -EINVAL; + } + + dev_dbg(&vdev->dev, + "vdev config: ver=%u, rx sz = 0x%x, tx sz = 0x%x\n", + version, vrp->rx_buf_size, vrp->tx_buf_size); + } else { + vrp->rx_buf_size = DEFAULT_RPMSG_BUF_SIZE; + vrp->tx_buf_size = DEFAULT_RPMSG_BUF_SIZE; + } + + return 0; +} + static int rpmsg_probe(struct virtio_device *vdev) { struct virtqueue_info vqs_info[] = { @@ -845,19 +902,23 @@ static int rpmsg_probe(struct virtio_device *vdev) vrp->rvq = vqs[0]; vrp->svq = vqs[1]; - /* we expect symmetric tx/rx vrings */ - WARN_ON(virtqueue_get_vring_size(vrp->rvq) != - virtqueue_get_vring_size(vrp->svq)); - /* we need less buffers if vrings are small */ - if (virtqueue_get_vring_size(vrp->rvq) < MAX_RPMSG_NUM_BUFS / 2) - vrp->num_bufs = virtqueue_get_vring_size(vrp->rvq) * 2; + if (virtqueue_get_vring_size(vrp->rvq) < MAX_RPMSG_NUM_BUFS) + vrp->num_rx_buf = virtqueue_get_vring_size(vrp->rvq); + else + vrp->num_rx_buf = MAX_RPMSG_NUM_BUFS; + + if (virtqueue_get_vring_size(vrp->svq) < MAX_RPMSG_NUM_BUFS) + vrp->num_tx_buf = virtqueue_get_vring_size(vrp->svq); else - vrp->num_bufs = MAX_RPMSG_NUM_BUFS; + vrp->num_tx_buf = MAX_RPMSG_NUM_BUFS; - vrp->buf_size = MAX_RPMSG_BUF_SIZE; + err = rpmsg_virtio_get_buf_size(vrp); + if (err) + goto vqs_del; - total_buf_space = vrp->num_bufs * vrp->buf_size; + total_buf_space = (vrp->num_rx_buf * vrp->rx_buf_size) + + (vrp->num_tx_buf * vrp->tx_buf_size); /* allocate coherent memory for the buffers */ bufs_va = dma_alloc_coherent(vdev->dev.parent, @@ -871,18 +932,17 @@ static int rpmsg_probe(struct virtio_device *vdev) dev_dbg(&vdev->dev, "buffers: va %p, dma %pad\n", bufs_va, &vrp->bufs_dma); - /* half of the buffers is dedicated for RX */ - vrp->rbufs = bufs_va; + /* first part of the buffers is dedicated for RX */ + vrp->rx_bufs = bufs_va; - /* and half is dedicated for TX */ - vrp->sbufs = bufs_va + total_buf_space / 2; + vrp->tx_bufs = bufs_va + (vrp->num_rx_buf * vrp->rx_buf_size); /* set up the receive buffers */ - for (i = 0; i < vrp->num_bufs / 2; i++) { + for (i = 0; i < vrp->num_rx_buf; i++) { struct scatterlist sg; - void *cpu_addr = vrp->rbufs + i * vrp->buf_size; + void *cpu_addr = vrp->rx_bufs + i * vrp->rx_buf_size; - rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size); + rpmsg_sg_init(&sg, cpu_addr, vrp->rx_buf_size); err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr, GFP_KERNEL); @@ -965,7 +1025,8 @@ static int rpmsg_remove_device(struct device *dev, void *data) static void rpmsg_remove(struct virtio_device *vdev) { struct virtproc_info *vrp = vdev->priv; - size_t total_buf_space = vrp->num_bufs * vrp->buf_size; + size_t total_buf_space = (vrp->num_rx_buf * vrp->rx_buf_size) + + (vrp->num_tx_buf * vrp->tx_buf_size); int ret; virtio_reset_device(vdev); @@ -979,7 +1040,7 @@ static void rpmsg_remove(struct virtio_device *vdev) vdev->config->del_vqs(vrp->vdev); dma_free_coherent(vdev->dev.parent, total_buf_space, - vrp->rbufs, vrp->bufs_dma); + vrp->rx_bufs, vrp->bufs_dma); kfree(vrp); } @@ -991,6 +1052,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_RPMSG_F_NS, + VIRTIO_RPMSG_F_BUFSZ, }; static struct virtio_driver virtio_ipc_driver = { diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 9f703261d7ac..535c8619197b 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -318,6 +318,18 @@ config QCOM_QMI_HELPERS tristate depends on NET +config QCOM_QMI_TMD + tristate "Qualcomm remote subsystem TMD" if COMPILE_TEST + depends on NET + depends on ARCH_QCOM || COMPILE_TEST + select QRTR + select QCOM_QMI_HELPERS + help + This enables Qualcomm Messaging Interface (QMI) based Thermal Mitigation + Device (TMD) support for Qualcomm remote subsystems. It manages + TMD messaging and handles QMI communication with remote processors + to exchange mitigation state and apply thermal mitigation requests. + config QCOM_UBWC_CONFIG tristate depends on QCOM_SMEM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 798643be3590..bddd6f0a5836 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..54e5ce8dc462 --- /dev/null +++ b/drivers/soc/qcom/qmi_tmd.c @@ -0,0 +1,595 @@ +// 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). + * Provides cooling device support for remote subsystems + * running the TMD service via QMI. + */ +#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_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: Serializes QMI request/response sequences (qmi_txn_init, + * qmi_send_request) during DSP subsystem restart and + * protects @connection_active and @exiting + * @connection_active: Whether or not we're connected to the QMI TMD service + * @exiting: Whether or not teardown has started + * @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, connection_active and exiting */ + struct mutex mutex; + bool connection_active; + bool exiting; + 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; + + if (!qmi_tmd_cli->connection_active) + return -ENOTCONN; + + 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); + + tmd->cur_state = state; + + 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; + + /* cur_state is protected by thermal core's cdev->lock */ + *state = tmd->cur_state; + + return 0; +} + +static int qmi_tmd_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct qmi_tmd *tmd = cdev->devdata; + + if (state > tmd->max_state) + return -EINVAL; + + /* cur_state is protected by thermal core's cdev->lock */ + if (tmd->cur_state == state) + return 0; + + guard(mutex)(&tmd->qmi_tmd_cli->mutex); + + return qmi_tmd_send_state_request(tmd, state); +} + +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); + 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) { + guard(mutex)(&qmi_tmd_cli->mutex); + + 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)) { + int ret = PTR_ERR(tmd->cdev); + + tmd->cdev = NULL; + return ret; + } + + 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) { + if (qmi_tmd_cli->exiting) + return; + + 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); + + 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) { + if (qmi_tmd_cli->exiting) + return 0; + + 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, +}; + +/** + * qmi_tmd_init() - Initialize QMI TMD instance + * @dev: Device pointer + * @instance_id: QMI service instance ID for the remote subsystem + * @tmd_names: Array of TMD names + * @num_tmds: Number of TMD names + * + * Context: Must be called from probe context. + * + * Return: Pointer to qmi_tmd_client on success, ERR_PTR on failure + */ +struct qmi_tmd_client *qmi_tmd_init(struct device *dev, + unsigned int instance_id, + const char * const *tmd_names, + int num_tmds) +{ + struct qmi_tmd_client *qmi_tmd_cli; + int ret, i; + + if (!dev || !tmd_names || num_tmds <= 0) + return ERR_PTR(-EINVAL); + + 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); + + 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) + return ERR_PTR(dev_err_probe(dev, ret, "QMI handle init failed\n")); + + ret = qmi_add_lookup(&qmi_tmd_cli->handle, QMI_SERVICE_ID_TMD, + QMI_TMD_SERVICE_VERS_V01, instance_id); + if (ret < 0) { + dev_err_probe(dev, ret, "QMI add lookup failed\n"); + 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; + + scoped_guard(mutex, &qmi_tmd_cli->mutex) { + qmi_tmd_cli->exiting = true; + qmi_tmd_cli->connection_active = false; + } + + qmi_handle_release(&qmi_tmd_cli->handle); + cancel_work_sync(&qmi_tmd_cli->svc_arrive_work); + qmi_tmd_unregister(qmi_tmd_cli); +} +EXPORT_SYMBOL_GPL(qmi_tmd_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm QMI Thermal Mitigation support"); |
