summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-14 15:04:46 +0100
committerMark Brown <broonie@kernel.org>2026-10-14 15:04:46 +0100
commit6eb7ecbce95aa3f3bdcd096b9ac978a53dad8a5b (patch)
treeb4e857ba707ce044ae303d058bcd7e38ccb713f2
parent20aa4e06adbcfd7944c324db284c781519d659b4 (diff)
parenta101347c14c48a6ea60f9af11df8ad3b2f4a602e (diff)
downloadlinux-next-6eb7ecbce95aa3f3bdcd096b9ac978a53dad8a5b.tar.gz
linux-next-6eb7ecbce95aa3f3bdcd096b9ac978a53dad8a5b.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux.git
-rw-r--r--Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml22
-rw-r--r--Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml9
-rw-r--r--Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml31
-rw-r--r--Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml1
-rw-r--r--Documentation/staging/rpmsg.rst17
-rw-r--r--MAINTAINERS8
-rw-r--r--arch/arm64/boot/dts/qcom/sdx75.dtsi3
-rw-r--r--drivers/remoteproc/Kconfig11
-rw-r--r--drivers/remoteproc/imx_dsp_rproc.c7
-rw-r--r--drivers/remoteproc/imx_rproc.c51
-rw-r--r--drivers/remoteproc/omap_remoteproc.c12
-rw-r--r--drivers/remoteproc/qcom_q6v5_adsp.c2
-rw-r--r--drivers/remoteproc/qcom_q6v5_pas.c166
-rw-r--r--drivers/remoteproc/remoteproc_core.c69
-rw-r--r--drivers/remoteproc/remoteproc_debugfs.c6
-rw-r--r--drivers/remoteproc/remoteproc_elf_loader.c23
-rw-r--r--drivers/remoteproc/remoteproc_sysfs.c7
-rw-r--r--drivers/remoteproc/remoteproc_virtio.c61
-rw-r--r--drivers/remoteproc/stm32_rproc.c2
-rw-r--r--drivers/remoteproc/ti_k3_common.c10
-rw-r--r--drivers/remoteproc/ti_k3_common.h4
-rw-r--r--drivers/remoteproc/ti_k3_dsp_remoteproc.c7
-rw-r--r--drivers/remoteproc/ti_k3_m4_remoteproc.c4
-rw-r--r--drivers/remoteproc/ti_k3_r5_remoteproc.c9
-rw-r--r--drivers/remoteproc/xlnx_r5_remoteproc.c23
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c156
-rw-r--r--drivers/soc/qcom/Kconfig12
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/qmi_tmd.c595
-rw-r--r--include/dt-bindings/thermal/qcom,pas.h20
-rw-r--r--include/linux/omap-mailbox.h5
-rw-r--r--include/linux/remoteproc.h4
-rw-r--r--include/linux/rpmsg/virtio_rpmsg.h41
-rw-r--r--include/linux/soc/qcom/qmi.h1
-rw-r--r--include/linux/soc/qcom/qmi_tmd.h36
-rw-r--r--include/uapi/linux/rpmsg.h15
-rw-r--r--samples/rpmsg/rpmsg_client_sample.c20
37 files changed, 1221 insertions, 250 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml b/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml
index bdbb12118da4..2f0a28c7a24c 100644
--- a/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/mtk,scp.yaml
@@ -28,11 +28,11 @@ properties:
description:
Should contain the address ranges for memory regions SRAM, CFG, and,
on some platforms, L1TCM.
- minItems: 2
+ minItems: 1
maxItems: 3
reg-names:
- minItems: 2
+ minItems: 1
maxItems: 3
clocks:
@@ -171,6 +171,7 @@ allOf:
then:
properties:
reg:
+ minItems: 2
maxItems: 2
reg-names:
items:
@@ -185,6 +186,7 @@ allOf:
then:
properties:
reg:
+ minItems: 3
maxItems: 3
reg-names:
items:
@@ -195,17 +197,31 @@ allOf:
properties:
compatible:
enum:
- - mediatek,mt8188-scp-dual
- mediatek,mt8195-scp-dual
then:
properties:
reg:
+ minItems: 2
maxItems: 2
reg-names:
items:
- const: cfg
- const: l1tcm
+ - if:
+ properties:
+ compatible:
+ enum:
+ - mediatek,mt8188-scp-dual
+ then:
+ properties:
+ reg:
+ minItems: 1
+ maxItems: 1
+ reg-names:
+ items:
+ - const: cfg
+
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml
index 11faf655f530..f75cab6b4ed9 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml
@@ -77,6 +77,15 @@ properties:
channels and devices related to the ADSP.
unevaluatedProperties: false
+ '#cooling-cells':
+ description: |
+ Cooling device with three cells:
+ Cell 0: Cooling device id as defined in
+ include/dt-bindings/thermal/qcom,pas.h
+ Cell 1: Minimum cooling state
+ Cell 2: Maximum cooling state
+ const: 3
+
glink-edge:
$ref: /schemas/remoteproc/qcom,glink-edge.yaml#
description:
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml
index c58e8a6c7fe1..99e9aeeb4f62 100644
--- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml
@@ -18,7 +18,7 @@ properties:
oneOf:
- enum:
- qcom,eliza-cdsp-pas
- - qcom,sdx75-mpss-pas
+ - qcom,hawi-cdsp-pas
- qcom,sm8550-adsp-pas
- qcom,sm8550-cdsp-pas
- qcom,sm8550-mpss-pas
@@ -30,6 +30,10 @@ properties:
- qcom,x1e80100-cdsp-pas
- items:
- enum:
+ - qcom,maili-cdsp-pas
+ - const: qcom,hawi-cdsp-pas
+ - items:
+ - enum:
- qcom,eliza-adsp-pas
- qcom,glymur-adsp-pas
- qcom,hawi-adsp-pas
@@ -40,13 +44,17 @@ properties:
- items:
- enum:
- qcom,glymur-cdsp-pas
- - qcom,hawi-cdsp-pas
- qcom,kaanapali-cdsp-pas
- - qcom,maili-cdsp-pas
- const: qcom,sm8550-cdsp-pas
- items:
- const: qcom,sm8750-cdsp-pas
- const: qcom,sm8650-cdsp-pas
+ - items:
+ - enum:
+ - qcom,hawi-mpss-pas
+ - qcom,maili-mpss-pas
+ - qcom,sdx75-mpss-pas
+ - const: qcom,sm8650-mpss-pas
reg:
maxItems: 1
@@ -203,9 +211,9 @@ allOf:
- if:
properties:
compatible:
- enum:
- - qcom,sdx75-mpss-pas
- - qcom,sm8650-mpss-pas
+ contains:
+ enum:
+ - qcom,sm8650-mpss-pas
then:
properties:
interrupts:
@@ -253,11 +261,11 @@ allOf:
- if:
properties:
compatible:
- enum:
- - qcom,sdx75-mpss-pas
- - qcom,sm8550-mpss-pas
- - qcom,sm8650-mpss-pas
- - qcom,sm8750-mpss-pas
+ contains:
+ enum:
+ - qcom,sm8550-mpss-pas
+ - qcom,sm8650-mpss-pas
+ - qcom,sm8750-mpss-pas
then:
properties:
power-domains:
@@ -273,6 +281,7 @@ allOf:
compatible:
contains:
enum:
+ - qcom,hawi-cdsp-pas
- qcom,sm8550-cdsp-pas
- qcom,sm8650-cdsp-pas
- qcom,x1e80100-cdsp-pas
diff --git a/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml b/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml
index 9e8a03acdec4..997be06ceeb1 100644
--- a/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/ti,am3352-wkup-m3.yaml
@@ -62,7 +62,6 @@ required:
dependencies:
resets: [reset-names]
- reset-names: [resets]
additionalProperties: false
diff --git a/Documentation/staging/rpmsg.rst b/Documentation/staging/rpmsg.rst
index 63612b7ee120..017867383fd9 100644
--- a/Documentation/staging/rpmsg.rst
+++ b/Documentation/staging/rpmsg.rst
@@ -299,3 +299,20 @@ by the bus, and can then start sending messages to the remote service.
The plan is also to add static creation of rpmsg channels via the virtio
config space, but it's not implemented yet.
+
+Configurable buffer sizes
+=========================
+
+By default each rpmsg buffer is 512 bytes, and the same size is used for both
+the receive and transmit directions. Remote processors that need a different
+size (for example a larger MTU, or different RX and TX queue sizes) can
+advertise it through the virtio device config space by setting the
+VIRTIO_RPMSG_F_BUFSZ feature bit.
+
+When this feature is negotiated, the driver reads struct virtio_rpmsg_config
+from the config space. This structure provides the size of a single buffer for
+each direction (TX and RX). The device should advertise TX/RX buffer sizes
+that already satisfy the alignment constraints of its buffers.
+
+If the feature is not negotiated, the default 512-byte buffers are used for
+both directions.
diff --git a/MAINTAINERS b/MAINTAINERS
index d2b34264f223..caba7a90864c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3525,6 +3525,7 @@ F: drivers/watchdog/gunyah_wdt.c
F: include/dt-bindings/arm/qcom,ids.h
F: include/dt-bindings/firmware/qcom,scm.h
F: include/dt-bindings/soc/qcom*
+F: include/dt-bindings/thermal/qcom,pas.h
F: include/linux/firmware/qcom
F: include/linux/soc/qcom/
F: include/soc/qcom/
@@ -23039,6 +23040,13 @@ 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 <gaurav.kohli@oss.qualcomm.com>
+L: linux-arm-msm@vger.kernel.org
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: drivers/soc/qcom/qmi_tmd.c
+
QUALCOMM QSEECOM DRIVER
M: Maximilian Luz <luzmaximilian@gmail.com>
L: linux-arm-msm@vger.kernel.org
diff --git a/arch/arm64/boot/dts/qcom/sdx75.dtsi b/arch/arm64/boot/dts/qcom/sdx75.dtsi
index c66f7bd9bf9a..7e63e4c3570e 100644
--- a/arch/arm64/boot/dts/qcom/sdx75.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdx75.dtsi
@@ -925,7 +925,8 @@
};
remoteproc_mpss: remoteproc@4080000 {
- compatible = "qcom,sdx75-mpss-pas";
+ compatible = "qcom,sdx75-mpss-pas",
+ "qcom,sm8650-mpss-pas";
reg = <0 0x04080000 0 0x10000>;
interrupts-extended = <&intc GIC_SPI 250 IRQ_TYPE_EDGE_RISING>,
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..23f8539a99bd 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++;
@@ -1659,49 +1662,21 @@ disable_iommu:
}
/*
- * take a firmware and boot it up.
- *
- * Note: this function is called asynchronously upon registration of the
- * remote processor (so we must wait until it completes before we try
- * to unregister the device. one other option is just to use kref here,
- * that might be cleaner).
+ * Boot or attach the remote processor in the background: rproc_add()
+ * runs in probe context and must not block while the firmware image is
+ * read from storage. rproc_boot() dispatches on the processor state,
+ * so this covers both a firmware boot and an attach to a processor
+ * started by another entity.
+ *
+ * Note: rproc_del() waits for this work to complete with
+ * cancel_work_sync(), so the rproc instance remains valid for the
+ * entire lifetime of this function.
*/
-static void rproc_auto_boot_callback(const struct firmware *fw, void *context)
+static void rproc_boot_work(struct work_struct *work)
{
- struct rproc *rproc = context;
+ struct rproc *rproc = container_of(work, struct rproc, boot_work);
rproc_boot(rproc);
-
- release_firmware(fw);
-}
-
-static void rproc_attach_work(struct work_struct *work)
-{
- struct rproc *rproc = container_of(work, struct rproc, attach_work);
-
- rproc_boot(rproc);
-}
-
-static int rproc_trigger_auto_boot(struct rproc *rproc)
-{
- int ret;
-
- if (rproc->state == RPROC_DETACHED) {
- schedule_work(&rproc->attach_work);
- return 0;
- }
-
- /*
- * We're initiating an asynchronous firmware loading, so we can
- * be built-in kernel code, without hanging the boot process.
- */
- ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
- rproc->firmware, &rproc->dev, GFP_KERNEL,
- rproc, rproc_auto_boot_callback);
- if (ret < 0)
- dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret);
-
- return ret;
}
static int rproc_stop(struct rproc *rproc, bool crashed)
@@ -2345,11 +2320,8 @@ int rproc_add(struct rproc *rproc)
rproc_create_debug_dir(rproc);
/* if rproc is marked always-on, request it to boot */
- if (rproc->auto_boot) {
- ret = rproc_trigger_auto_boot(rproc);
- if (ret < 0)
- goto rproc_remove_dev;
- }
+ if (rproc->auto_boot)
+ schedule_work(&rproc->boot_work);
/* expose to rproc_get_by_phandle users */
mutex_lock(&rproc_list_mutex);
@@ -2358,10 +2330,6 @@ int rproc_add(struct rproc *rproc)
return 0;
-rproc_remove_dev:
- cancel_work_sync(&rproc->crash_handler);
- rproc_delete_debug_dir(rproc);
- device_del(dev);
rproc_remove_cdev:
rproc_char_device_remove(rproc);
return ret;
@@ -2555,7 +2523,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
INIT_LIST_HEAD(&rproc->subdevs);
INIT_LIST_HEAD(&rproc->dump_segments);
- INIT_WORK(&rproc->attach_work, rproc_attach_work);
+ INIT_WORK(&rproc->boot_work, rproc_boot_work);
INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
spin_lock_init(&rproc->crash_handler_lock);
@@ -2639,6 +2607,9 @@ int rproc_del(struct rproc *rproc)
if (cancel_work_sync(&rproc->crash_handler))
pm_relax(rproc->dev.parent);
+ /* auto-boot may still be fetching firmware: wait for it here */
+ cancel_work_sync(&rproc->boot_work);
+
__rproc_shutdown(rproc, true);
rproc_delete_debug_dir(rproc);
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/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
index 7fefb4bd7adc..12f55a81cfa0 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -38,11 +38,12 @@ static int copy_dma_range_map(struct device *to, struct device *from)
for (r = map; r->size; r++)
num_ranges++;
- new_map = kmemdup(map, array_size(num_ranges + 1, sizeof(*map)),
- GFP_KERNEL);
+ new_map = kmemdup_array(map, num_ranges + 1, sizeof(*map), GFP_KERNEL);
if (!new_map)
return -ENOMEM;
+
to->dma_range_map = new_map;
+
return 0;
}
@@ -55,13 +56,23 @@ static struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev)
return platform_get_drvdata(pdev);
}
-static struct rproc *vdev_to_rproc(struct virtio_device *vdev)
+static struct rproc *vdev_to_rproc(struct virtio_device *vdev)
{
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
return rvdev->rproc;
}
+static struct fw_rsc_vdev *vdev_to_rsc(struct virtio_device *vdev)
+{
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+ struct fw_rsc_vdev *rsc;
+
+ rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+
+ return rsc;
+}
+
/* kick the remote processor, and let it know which virtqueue to poke at */
static bool rproc_virtio_notify(struct virtqueue *vq)
{
@@ -116,10 +127,10 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
{
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
struct rproc *rproc = vdev_to_rproc(vdev);
+ struct fw_rsc_vdev *rsc = vdev_to_rsc(vdev);
struct device *dev = &rproc->dev;
struct rproc_mem_entry *mem;
struct rproc_vring *rvring;
- struct fw_rsc_vdev *rsc;
struct virtqueue *vq;
void *addr;
int num, size;
@@ -166,7 +177,6 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
vq->priv = rvring;
/* Update vring in resource table */
- rsc = (void *)rproc->table_ptr + rvdev->rsc_offset;
rsc->vring[id].da = mem->da;
return vq;
@@ -227,20 +237,14 @@ error:
static u8 rproc_virtio_get_status(struct virtio_device *vdev)
{
- struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
- struct fw_rsc_vdev *rsc;
-
- rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+ struct fw_rsc_vdev *rsc = vdev_to_rsc(vdev);
return rsc->status;
}
static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
{
- struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
- struct fw_rsc_vdev *rsc;
-
- rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+ struct fw_rsc_vdev *rsc = vdev_to_rsc(vdev);
rsc->status = status;
dev_dbg(&vdev->dev, "status: %d\n", status);
@@ -248,10 +252,7 @@ static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
static void rproc_virtio_reset(struct virtio_device *vdev)
{
- struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
- struct fw_rsc_vdev *rsc;
-
- rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+ struct fw_rsc_vdev *rsc = vdev_to_rsc(vdev);
rsc->status = 0;
dev_dbg(&vdev->dev, "reset !\n");
@@ -267,10 +268,7 @@ static void rproc_virtio_synchronize_cbs(struct virtio_device *vdev)
/* provide the vdev features as retrieved from the firmware */
static u64 rproc_virtio_get_features(struct virtio_device *vdev)
{
- struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
- struct fw_rsc_vdev *rsc;
-
- rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+ struct fw_rsc_vdev *rsc = vdev_to_rsc(vdev);
return rsc->dfeatures;
}
@@ -287,10 +285,7 @@ static void rproc_transport_features(struct virtio_device *vdev)
static int rproc_virtio_finalize_features(struct virtio_device *vdev)
{
- struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
- struct fw_rsc_vdev *rsc;
-
- rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+ struct fw_rsc_vdev *rsc = vdev_to_rsc(vdev);
/* Give virtio_ring a chance to accept features */
vring_transport_features(vdev);
@@ -313,12 +308,8 @@ static int rproc_virtio_finalize_features(struct virtio_device *vdev)
static void rproc_virtio_get(struct virtio_device *vdev, unsigned int offset,
void *buf, unsigned int len)
{
- struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
- struct fw_rsc_vdev *rsc;
- void *cfg;
-
- rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
- cfg = &rsc->vring[rsc->num_of_vrings];
+ struct fw_rsc_vdev *rsc = vdev_to_rsc(vdev);
+ void *cfg = &rsc->vring[rsc->num_of_vrings];
if (offset + len > rsc->config_len || offset + len < len) {
dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n");
@@ -331,12 +322,8 @@ static void rproc_virtio_get(struct virtio_device *vdev, unsigned int offset,
static void rproc_virtio_set(struct virtio_device *vdev, unsigned int offset,
const void *buf, unsigned int len)
{
- struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
- struct fw_rsc_vdev *rsc;
- void *cfg;
-
- rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
- cfg = &rsc->vring[rsc->num_of_vrings];
+ struct fw_rsc_vdev *rsc = vdev_to_rsc(vdev);
+ void *cfg = &rsc->vring[rsc->num_of_vrings];
if (offset + len > rsc->config_len || offset + len < len) {
dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n");
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");
diff --git a/include/dt-bindings/thermal/qcom,pas.h b/include/dt-bindings/thermal/qcom,pas.h
new file mode 100644
index 000000000000..f603bddef9a8
--- /dev/null
+++ b/include/dt-bindings/thermal/qcom,pas.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * Qualcomm PAS remoteproc cooling device indices
+ *
+ * These indices are used in device tree cooling-maps to reference
+ * specific TMD devices provided by PAS-managed remote processors via QMI.
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+#ifndef _DT_BINDINGS_THERMAL_QCOM_PAS_H
+#define _DT_BINDINGS_THERMAL_QCOM_PAS_H
+
+/* CDSP thermal mitigation device id */
+#define QCOM_TMD_CDSP_SW 0
+
+/* Modem thermal mitigation device id */
+#define QCOM_TMD_PA 0
+#define QCOM_TMD_MODEM 1
+
+#endif /* _DT_BINDINGS_THERMAL_QCOM_PAS_H */
diff --git a/include/linux/omap-mailbox.h b/include/linux/omap-mailbox.h
index 3cc5c4ed7f5a..e45f8c3841c2 100644
--- a/include/linux/omap-mailbox.h
+++ b/include/linux/omap-mailbox.h
@@ -6,8 +6,9 @@
#ifndef OMAP_MAILBOX_H
#define OMAP_MAILBOX_H
-typedef uintptr_t mbox_msg_t;
+typedef u32 mbox_msg_t;
-#define omap_mbox_message(data) (u32)(mbox_msg_t)(data)
+#define omap_mbox_to_message(data) ((void *)(uintptr_t)(data))
+#define omap_mbox_from_message(data) ((mbox_msg_t)(uintptr_t)(data))
#endif /* OMAP_MAILBOX_H */
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index ff0ad1e12c01..00019b1895e6 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -233,7 +233,7 @@ enum rproc_features {
* @subdevs: list of subdevices, to following the running state
* @notifyids: idr for dynamically assigning rproc-wide unique notify ids
* @index: index of this rproc device
- * @attach_work: workqueue for attaching rproc
+ * @boot_work: workqueue for booting rproc
* @crash_handler: workqueue for handling a crash
* @crash_handler_lock: serializes crash handler queueing and deletion
* @deleting: remoteproc deletion has begun
@@ -280,7 +280,7 @@ struct rproc {
struct list_head subdevs;
struct idr notifyids;
int index;
- struct work_struct attach_work;
+ struct work_struct boot_work;
struct work_struct crash_handler;
spinlock_t crash_handler_lock;
bool deleting;
diff --git a/include/linux/rpmsg/virtio_rpmsg.h b/include/linux/rpmsg/virtio_rpmsg.h
new file mode 100644
index 000000000000..0872cbca4593
--- /dev/null
+++ b/include/linux/rpmsg/virtio_rpmsg.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Pinecone Inc. 2019
+ * Copyright (C) Xiang Xiao <xiaoxiang@pinecone.net>
+ * Copyright (C) Advanced Micro Devices, Inc. 2026
+ */
+
+#ifndef _LINUX_VIRTIO_RPMSG_H
+#define _LINUX_VIRTIO_RPMSG_H
+
+#include <linux/types.h>
+#include <linux/virtio_types.h>
+
+/* The feature bitmap for virtio rpmsg */
+#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */
+#define VIRTIO_RPMSG_F_BUFSZ 1 /* RP advertises RX/TX buffer sizes from config space */
+
+/* Version of struct virtio_rpmsg_config understood by this driver */
+#define RPMSG_VDEV_CONFIG_V1 1
+
+/**
+ * struct virtio_rpmsg_config - config space for rpmsg virtio device
+ *
+ * @version: version of this structure, currently %RPMSG_VDEV_CONFIG_V1.
+ * @size: size of this structure in bytes.
+ * @txbuf_size: Tx buf size from remote's view. For Linux this is rx buf size.
+ * @rxbuf_size: Rx buf size from remote's view. For Linux this is tx buf size.
+ *
+ * This is the configuration structure shared by the device and the driver,
+ * read when %VIRTIO_RPMSG_F_BUFSZ is negotiated. The fields are laid out so
+ * the structure is naturally 32-bit aligned.
+ */
+struct virtio_rpmsg_config {
+ u8 version;
+ __virtio16 size;
+ /* The tx/rx individual buffer size (if VIRTIO_RPMSG_F_BUFSZ) */
+ __virtio32 txbuf_size;
+ __virtio32 rxbuf_size;
+} __packed;
+
+#endif /* _LINUX_VIRTIO_RPMSG_H */
diff --git a/include/linux/soc/qcom/qmi.h b/include/linux/soc/qcom/qmi.h
index b9dcb437a0be..683d27cee413 100644
--- a/include/linux/soc/qcom/qmi.h
+++ b/include/linux/soc/qcom/qmi.h
@@ -96,6 +96,7 @@ struct qmi_elem_info {
* Enumerate the IDs of the QMI services
*/
#define QMI_SERVICE_ID_TEST 0x0f /* 15 */
+#define QMI_SERVICE_ID_TMD 0x18 /* 24 */
#define QMI_SERVICE_ID_SSCTL 0x2b /* 43 */
#define QMI_SERVICE_ID_IPA 0x31 /* 49 */
#define QMI_SERVICE_ID_SERVREG_LOC 0x40 /* 64 */
diff --git a/include/linux/soc/qcom/qmi_tmd.h b/include/linux/soc/qcom/qmi_tmd.h
new file mode 100644
index 000000000000..452f51f23731
--- /dev/null
+++ b/include/linux/soc/qcom/qmi_tmd.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025, Linaro Limited
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * QMI Thermal Mitigation Device (TMD) library header.
+ */
+
+#ifndef __QMI_TMD_H__
+#define __QMI_TMD_H__
+
+struct device;
+struct qmi_tmd_client;
+
+#if IS_ENABLED(CONFIG_QCOM_QMI_TMD)
+struct qmi_tmd_client *qmi_tmd_init(struct device *dev,
+ unsigned int instance_id,
+ const char * const *tmd_names,
+ int num_tmds);
+
+void qmi_tmd_exit(struct qmi_tmd_client *tmd_cli);
+#else
+static inline struct qmi_tmd_client *qmi_tmd_init(struct device *dev,
+ unsigned int instance_id,
+ const char * const *tmd_names,
+ int num_tmds)
+{
+ return NULL;
+}
+
+static inline void qmi_tmd_exit(struct qmi_tmd_client *tmd_cli)
+{
+}
+#endif
+
+#endif /* __QMI_TMD_H__ */
diff --git a/include/uapi/linux/rpmsg.h b/include/uapi/linux/rpmsg.h
index f0c8da2b185b..d553ebac3181 100644
--- a/include/uapi/linux/rpmsg.h
+++ b/include/uapi/linux/rpmsg.h
@@ -24,32 +24,35 @@ struct rpmsg_endpoint_info {
};
/**
- * Instantiate a new rmpsg char device endpoint.
+ * RPMSG_CREATE_EPT_IOCTL - Instantiate a new rmpsg char device endpoint.
*/
#define RPMSG_CREATE_EPT_IOCTL _IOW(0xb5, 0x1, struct rpmsg_endpoint_info)
/**
- * Destroy a rpmsg char device endpoint created by the RPMSG_CREATE_EPT_IOCTL.
+ * RPMSG_DESTROY_EPT_IOCTL - Destroy a rpmsg char device endpoint created
+ * by the RPMSG_CREATE_EPT_IOCTL.
*/
#define RPMSG_DESTROY_EPT_IOCTL _IO(0xb5, 0x2)
/**
- * Instantiate a new local rpmsg service device.
+ * RPMSG_CREATE_DEV_IOCTL - Instantiate a new local rpmsg service device.
*/
#define RPMSG_CREATE_DEV_IOCTL _IOW(0xb5, 0x3, struct rpmsg_endpoint_info)
/**
- * Release a local rpmsg device.
+ * RPMSG_RELEASE_DEV_IOCTL - Release a local rpmsg device.
*/
#define RPMSG_RELEASE_DEV_IOCTL _IOW(0xb5, 0x4, struct rpmsg_endpoint_info)
/**
- * Get the flow control state of the remote rpmsg char device.
+ * RPMSG_GET_OUTGOING_FLOWCONTROL - Get the flow control state of the
+ * remote rpmsg char device.
*/
#define RPMSG_GET_OUTGOING_FLOWCONTROL _IOR(0xb5, 0x5, int)
/**
- * Set the flow control state of the local rpmsg char device.
+ * RPMSG_SET_INCOMING_FLOWCONTROL - Set the flow control state of the
+ * local rpmsg char device.
*/
#define RPMSG_SET_INCOMING_FLOWCONTROL _IOR(0xb5, 0x6, int)
diff --git a/samples/rpmsg/rpmsg_client_sample.c b/samples/rpmsg/rpmsg_client_sample.c
index ae5081662283..8c629065c64c 100644
--- a/samples/rpmsg/rpmsg_client_sample.c
+++ b/samples/rpmsg/rpmsg_client_sample.c
@@ -52,6 +52,7 @@ static int rpmsg_sample_probe(struct rpmsg_device *rpdev)
{
int ret;
struct instance_data *idata;
+ ssize_t mtu, msg_len;
dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
rpdev->src, rpdev->dst);
@@ -62,8 +63,25 @@ static int rpmsg_sample_probe(struct rpmsg_device *rpdev)
dev_set_drvdata(&rpdev->dev, idata);
+ mtu = rpmsg_get_mtu(rpdev->ept);
+ if (mtu < 0) {
+ dev_warn(&rpdev->dev, "invalid rpmsg MTU size = %zd\n", mtu);
+ return mtu;
+ }
+
+ dev_info(&rpdev->dev, "rpmsg MTU size = %zd\n", mtu);
+
+ msg_len = strlen(MSG);
+ /* make sure our message fits in a single rpmsg buffer */
+ if (msg_len > mtu) {
+ dev_err(&rpdev->dev,
+ "message size %zd exceeds rpmsg MTU size %zd\n",
+ msg_len, mtu);
+ return -EMSGSIZE;
+ }
+
/* send a message to our remote processor */
- ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG));
+ ret = rpmsg_send(rpdev->ept, MSG, msg_len);
if (ret) {
dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
return ret;