From e30d8b2730a33e5e8789371e947c3529789a6070 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Thu, 14 May 2026 17:39:51 +0100 Subject: [PATCH 01/23] mailbox: mpfs: fix check for syscon presence in mpfs_mbox_inbox_isr() mpfs_mbox_inbox_isr() writes to the sysreg scb syscon, not the control scb syscon, but checks for the presence of the latter. Ultimately this makes little difference because if one syscon is present, both will be. Fixes: a4123ffab9ece ("mailbox: mpfs: support new, syscon based, devicetree configuration") Signed-off-by: Conor Dooley Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox-mpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mailbox/mailbox-mpfs.c b/drivers/mailbox/mailbox-mpfs.c index d5d9effece97..ef40fe2be30d 100644 --- a/drivers/mailbox/mailbox-mpfs.c +++ b/drivers/mailbox/mailbox-mpfs.c @@ -201,7 +201,7 @@ static irqreturn_t mpfs_mbox_inbox_isr(int irq, void *data) struct mbox_chan *chan = data; struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; - if (mbox->control_scb) + if (mbox->sysreg_scb) regmap_write(mbox->sysreg_scb, MESSAGE_INT_OFFSET, 0); else writel_relaxed(0, mbox->int_reg); From 388f16c9372d15585b594998f34542ed00fddebf Mon Sep 17 00:00:00 2001 From: Deepti Jaggi Date: Mon, 27 Apr 2026 08:52:35 +0800 Subject: [PATCH 02/23] dt-bindings: mailbox: qcom: Document Nord CPUCP mailbox controller Document CPUSS Control Processor (CPUCP) mailbox controller for Qualcomm Nord SoC, which is compatible with X1E80100 CPUCP, even though it supports more IPC channels. Signed-off-by: Deepti Jaggi Signed-off-by: Shawn Guo Reviewed-by: Krzysztof Kozlowski Signed-off-by: Jassi Brar --- Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml index 90bfde66cc4a..c8107d58f3d5 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml @@ -20,6 +20,7 @@ properties: - enum: - qcom,glymur-cpucp-mbox - qcom,kaanapali-cpucp-mbox + - qcom,nord-cpucp-mbox - qcom,sm8750-cpucp-mbox - const: qcom,x1e80100-cpucp-mbox - enum: From 0edda639ba13d9da37447586b5480582a2581e2b Mon Sep 17 00:00:00 2001 From: Deepti Jaggi Date: Mon, 27 Apr 2026 08:52:36 +0800 Subject: [PATCH 03/23] mailbox: qcom-cpucp: Add support for Nord CPUCP mailbox controller The Nord SoC CPUCP mailbox supports 16 IPC channels, compared to 3 on x1e80100. The existing driver hardcodes the channel count via a compile-time constant (APSS_CPUCP_IPC_CHAN_SUPPORTED), making it impossible to support hardware with a different number of channels. Introduce a qcom_cpucp_mbox_data per-hardware configuration struct that carries the channel count, and retrieve it via of_device_get_match_data() at probe time. Switch the channel array from a fixed-size member to a dynamically allocated buffer sized from the hardware data. Update the x1e80100 entry to supply its own data struct, and add a new Nord entry with num_chans = 16. Signed-off-by: Deepti Jaggi Signed-off-by: Shawn Guo Reviewed-by: Konrad Dybcio Signed-off-by: Jassi Brar --- drivers/mailbox/qcom-cpucp-mbox.c | 35 ++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/mailbox/qcom-cpucp-mbox.c b/drivers/mailbox/qcom-cpucp-mbox.c index 44f4ed15f818..862e45e8fbd5 100644 --- a/drivers/mailbox/qcom-cpucp-mbox.c +++ b/drivers/mailbox/qcom-cpucp-mbox.c @@ -12,7 +12,6 @@ #include #include -#define APSS_CPUCP_IPC_CHAN_SUPPORTED 3 #define APSS_CPUCP_MBOX_CMD_OFF 0x4 /* Tx Registers */ @@ -26,6 +25,14 @@ #define APSS_CPUCP_RX_MBOX_EN 0x4c00 #define APSS_CPUCP_RX_MBOX_CMD_MASK GENMASK_ULL(63, 0) +/** + * struct qcom_cpucp_mbox_data - Per-hardware mailbox configuration data + * @num_chans: Number of IPC channels supported by this hardware + */ +struct qcom_cpucp_mbox_data { + int num_chans; +}; + /** * struct qcom_cpucp_mbox - Holder for the mailbox driver * @chans: The mailbox channel @@ -34,7 +41,7 @@ * @rx_base: Base address of the CPUCP rx registers */ struct qcom_cpucp_mbox { - struct mbox_chan chans[APSS_CPUCP_IPC_CHAN_SUPPORTED]; + struct mbox_chan *chans; struct mbox_controller mbox; void __iomem *tx_base; void __iomem *rx_base; @@ -53,7 +60,7 @@ static irqreturn_t qcom_cpucp_mbox_irq_fn(int irq, void *data) status = readq(cpucp->rx_base + APSS_CPUCP_RX_MBOX_STAT); - for_each_set_bit(i, (unsigned long *)&status, APSS_CPUCP_IPC_CHAN_SUPPORTED) { + for_each_set_bit(i, (unsigned long *)&status, cpucp->mbox.num_chans) { u32 val = readl(cpucp->rx_base + APSS_CPUCP_RX_MBOX_CMD(i) + APSS_CPUCP_MBOX_CMD_OFF); struct mbox_chan *chan = &cpucp->chans[i]; unsigned long flags; @@ -112,15 +119,24 @@ static const struct mbox_chan_ops qcom_cpucp_mbox_chan_ops = { static int qcom_cpucp_mbox_probe(struct platform_device *pdev) { + const struct qcom_cpucp_mbox_data *data; struct device *dev = &pdev->dev; struct qcom_cpucp_mbox *cpucp; struct mbox_controller *mbox; int irq, ret; + data = of_device_get_match_data(dev); + if (!data) + return dev_err_probe(dev, -EINVAL, "No match data found\n"); + cpucp = devm_kzalloc(dev, sizeof(*cpucp), GFP_KERNEL); if (!cpucp) return -ENOMEM; + cpucp->chans = devm_kcalloc(dev, data->num_chans, sizeof(*cpucp->chans), GFP_KERNEL); + if (!cpucp->chans) + return -ENOMEM; + cpucp->rx_base = devm_of_iomap(dev, dev->of_node, 0, NULL); if (IS_ERR(cpucp->rx_base)) return PTR_ERR(cpucp->rx_base); @@ -146,7 +162,7 @@ static int qcom_cpucp_mbox_probe(struct platform_device *pdev) mbox = &cpucp->mbox; mbox->dev = dev; - mbox->num_chans = APSS_CPUCP_IPC_CHAN_SUPPORTED; + mbox->num_chans = data->num_chans; mbox->chans = cpucp->chans; mbox->ops = &qcom_cpucp_mbox_chan_ops; @@ -157,8 +173,17 @@ static int qcom_cpucp_mbox_probe(struct platform_device *pdev) return 0; } +static const struct qcom_cpucp_mbox_data qcom_x1e80100_mbox_data = { + .num_chans = 3, +}; + +static const struct qcom_cpucp_mbox_data qcom_nord_mbox_data = { + .num_chans = 16, +}; + static const struct of_device_id qcom_cpucp_mbox_of_match[] = { - { .compatible = "qcom,x1e80100-cpucp-mbox" }, + { .compatible = "qcom,nord-cpucp-mbox", .data = &qcom_nord_mbox_data }, + { .compatible = "qcom,x1e80100-cpucp-mbox", .data = &qcom_x1e80100_mbox_data }, {} }; MODULE_DEVICE_TABLE(of, qcom_cpucp_mbox_of_match); From 3931aaac040921931e6d97c3c1a836e864386642 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Fri, 8 May 2026 16:10:48 +0530 Subject: [PATCH 04/23] dt-bindings: mailbox: qcom: Add Shikra APCS compatible Add compatible for the Qualcomm Shikra APCS block. Signed-off-by: Komal Bajaj Signed-off-by: Sneh Mankad Acked-by: Rob Herring (Arm) Signed-off-by: Jassi Brar --- .../devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml index f40dc9048327..1b4ef0688ca7 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml @@ -49,6 +49,7 @@ properties: - qcom,qcs615-apss-shared - qcom,sc7180-apss-shared - qcom,sc8180x-apss-shared + - qcom,shikra-apss-shared - qcom,sm7150-apss-shared - qcom,sm8150-apss-shared - const: qcom,sdm845-apss-shared From c19bb923b5bcb077aebcb06ce164f6f4c6b3072c Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 27 Apr 2026 23:54:12 +0530 Subject: [PATCH 05/23] dt-bindings: mailbox: qcom,cpucp-mbox: Add Hawi compatible Document CPU Control Processor (CPUCP) mailbox controller for Qualcomm Hawi SoCs. It is software compatible with X1E80100 CPUCP mailbox controller hence fallback to it. Signed-off-by: Mukesh Ojha Acked-by: Krzysztof Kozlowski Signed-off-by: Jassi Brar --- Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml index c8107d58f3d5..03359479d926 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml @@ -19,6 +19,7 @@ properties: - items: - enum: - qcom,glymur-cpucp-mbox + - qcom,hawi-cpucp-mbox - qcom,kaanapali-cpucp-mbox - qcom,nord-cpucp-mbox - qcom,sm8750-cpucp-mbox From cfba87b3875dd0d78ec6ca75d2446412030133fa Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Wed, 1 Apr 2026 18:21:26 +0530 Subject: [PATCH 06/23] dt-bindings: mailbox: qcom: Add IPCC support for Hawi Platform Document the Inter-Processor Communication Controller on the Qualcomm Hawi Platform, which will be used to route interrupts across various subsystems found on the SoC. Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Reviewed-by: Manivannan Sadhasivam Signed-off-by: Jassi Brar --- Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml index f5c584cf2146..fae20a6615f6 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml @@ -26,6 +26,7 @@ properties: - enum: - qcom,eliza-ipcc - qcom,glymur-ipcc + - qcom,hawi-ipcc - qcom,kaanapali-ipcc - qcom,milos-ipcc - qcom,qcs8300-ipcc From 7caa16023b14dee4aa791f1a298474db02a791fd Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Thu, 30 Apr 2026 11:12:59 +0000 Subject: [PATCH 07/23] mailbox: exynos: Drop unused register definitions Leaving these dead definitions in place hides which registers are actually being used by the hardware, making the driver harder to read and maintain. Remove them to clean up the file. Signed-off-by: Tudor Ambarus Reviewed-by: Alexey Klimov Signed-off-by: Jassi Brar --- drivers/mailbox/exynos-mailbox.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/mailbox/exynos-mailbox.c b/drivers/mailbox/exynos-mailbox.c index d2355b128ba4..fa02f18948cf 100644 --- a/drivers/mailbox/exynos-mailbox.c +++ b/drivers/mailbox/exynos-mailbox.c @@ -16,15 +16,8 @@ #include #include -#define EXYNOS_MBOX_MCUCTRL 0x0 /* Mailbox Control Register */ -#define EXYNOS_MBOX_INTCR0 0x24 /* Interrupt Clear Register 0 */ #define EXYNOS_MBOX_INTMR0 0x28 /* Interrupt Mask Register 0 */ -#define EXYNOS_MBOX_INTSR0 0x2c /* Interrupt Status Register 0 */ -#define EXYNOS_MBOX_INTMSR0 0x30 /* Interrupt Mask Status Register 0 */ #define EXYNOS_MBOX_INTGR1 0x40 /* Interrupt Generation Register 1 */ -#define EXYNOS_MBOX_INTMR1 0x48 /* Interrupt Mask Register 1 */ -#define EXYNOS_MBOX_INTSR1 0x4c /* Interrupt Status Register 1 */ -#define EXYNOS_MBOX_INTMSR1 0x50 /* Interrupt Mask Status Register 1 */ #define EXYNOS_MBOX_INTMR0_MASK GENMASK(15, 0) #define EXYNOS_MBOX_INTGR1_MASK GENMASK(15, 0) From 7bcfb7e65457f784b9495b10743f2c9db409b5b7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2026 09:01:14 +0200 Subject: [PATCH 08/23] mailbox: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Jassi Brar --- drivers/mailbox/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 5bf4155b090a..3062ee352f78 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -341,7 +341,7 @@ config SPRD_MBOX you want to build the Spreatrum mailbox controller driver. config QCOM_CPUCP_MBOX - tristate "Qualcomm Technologies, Inc. CPUCP mailbox driver" + tristate "Qualcomm CPUCP mailbox driver" depends on (ARCH_QCOM || COMPILE_TEST) && 64BIT help Qualcomm Technologies, Inc. CPUSS Control Processor (CPUCP) mailbox @@ -349,7 +349,7 @@ config QCOM_CPUCP_MBOX Y here if you want to build this driver. config QCOM_IPCC - tristate "Qualcomm Technologies, Inc. IPCC driver" + tristate "Qualcomm IPCC driver" depends on ARCH_QCOM || COMPILE_TEST help Qualcomm Technologies, Inc. Inter-Processor Communication Controller From b57d1a40bc43258372fa1f4d39305e093947a262 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Tue, 28 Apr 2026 11:55:44 +0900 Subject: [PATCH 09/23] mailbox: mtk-adsp: fix UAF during device teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the SOF audio driver fails to initialize (e.g. firmware boot timeout), its devres unwind frees the snd_sof_dev object that the mailbox client (mtk-adsp-ipc) reaches via chan->cl->rx_callback. The mtk-adsp-mailbox shutdown clears the mailbox command registers but leaves the IRQ line unmasked, so a late interrupt can still queue a threaded handler after mbox_free_channel() had cleared chan->cl, and mbox_chan_received_data() would then trigger UAF: BUG: KASAN: slab-use-after-free in sof_ipc3_validate_fw_version sof_ipc3_validate_fw_version sof_ipc3_do_rx_work sof_ipc3_rx_msg mt8196_dsp_handle_request mtk_adsp_ipc_recv mbox_chan_received_data mtk_adsp_mbox_isr irq_thread_fn Freed by task ...: kfree devres_release_all really_probe ... (sof-audio-of-mt8196 probe failure) The crash was observed roughly three seconds after the failed probe. disable_irq() in shutdown and enable_irq() in startup. disable_irq() also waits for any in-flight interrupts, so by the time mbox_free_channel() proceeds to clear chan->cl no rx_callback can run. In addition, request the IRQ with IRQF_NO_AUTOEN so it stays masked between probe and the first client bind — otherwise an early interrupt can crash on chan->cl == NULL in mbox_chan_received_data(). Fixes: af2dfa96c52d ("mailbox: mediatek: add support for adsp mailbox controller") Signed-off-by: Sergey Senozhatsky Reviewed-by: Tzung-Bi Shih Signed-off-by: Jassi Brar --- drivers/mailbox/mtk-adsp-mailbox.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/mtk-adsp-mailbox.c b/drivers/mailbox/mtk-adsp-mailbox.c index 91487aa4d7da..8bcecddee0eb 100644 --- a/drivers/mailbox/mtk-adsp-mailbox.c +++ b/drivers/mailbox/mtk-adsp-mailbox.c @@ -19,6 +19,7 @@ struct mtk_adsp_mbox_priv { struct mbox_controller mbox; void __iomem *va_mboxreg; const struct mtk_adsp_mbox_cfg *cfg; + int irq; }; struct mtk_adsp_mbox_cfg { @@ -67,6 +68,8 @@ static int mtk_adsp_mbox_startup(struct mbox_chan *chan) writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in); writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out); + enable_irq(priv->irq); + return 0; } @@ -74,6 +77,8 @@ static void mtk_adsp_mbox_shutdown(struct mbox_chan *chan) { struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox); + disable_irq(priv->irq); + /* Clear ADSP mbox command */ writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in); writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out); @@ -139,8 +144,10 @@ static int mtk_adsp_mbox_probe(struct platform_device *pdev) if (irq < 0) return irq; + priv->irq = irq; ret = devm_request_threaded_irq(dev, irq, mtk_adsp_mbox_irq, - mtk_adsp_mbox_isr, IRQF_TRIGGER_NONE, + mtk_adsp_mbox_isr, + IRQF_TRIGGER_NONE | IRQF_NO_AUTOEN, dev_name(dev), mbox->chans); if (ret < 0) return ret; From c96c8a7404ef8ce434ffd0f07b00e1a493fff42d Mon Sep 17 00:00:00 2001 From: Joonwon Kang Date: Tue, 21 Apr 2026 10:46:51 +0000 Subject: [PATCH 10/23] mailbox: Clarify multi-thread is not supported in blocking mode Unlike in non-blocking mode, multi-thread has not been supported in blocking mode. This commit is to prevent clients from having wrong assumption by explicitly specifying this fact to the API doc. Signed-off-by: Joonwon Kang Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index bbc9fd75a95f..b00f7a32e866 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -258,6 +258,10 @@ EXPORT_SYMBOL_GPL(mbox_chan_tx_slots_available); * over the chan, i.e, tx_done() is made. * This function could be called from atomic context as it simply * queues the data and returns a token against the request. + * In blocking mode, it is caller's responsibility to serialize threads' + * access to a channel if multi-threads are to send messages through the + * same channel, i.e. caller should not call this function until any + * previous call returns. * * Return: Non-negative integer for successful submission (non-blocking mode) * or transmission over chan (blocking mode). From 96a3d2f3167f5644b30e60171898e67123c3c2c6 Mon Sep 17 00:00:00 2001 From: Joonwon Kang Date: Sun, 10 May 2026 05:41:11 +0000 Subject: [PATCH 11/23] mailbox: Make mbox_send_message() return error code when tx fails When the mailbox controller failed transmitting message, the error code was only passed to the client's tx done handler and not to mbox_send_message() in blocking mode. For this reason, the function could return a false success. This commit resolves the issue by introducing the tx status and checking it before mbox_send_message() returns. This commit works with the premise that the multi-threads' access to a channel in blocking mode is serialized by clients, not by the mailbox APIs, since the current mbox_send_message() in blocking mode does not support multi-threads. Signed-off-by: Joonwon Kang Reviewed-by: Sudeep Holla Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox.c | 6 +++++- include/linux/mailbox_controller.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index b00f7a32e866..066702e5a46f 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -98,8 +98,10 @@ static void tx_tick(struct mbox_chan *chan, int r) if (chan->cl->tx_done) chan->cl->tx_done(chan->cl, mssg, r); - if (r != -ETIME && chan->cl->tx_block) + if (r != -ETIME && chan->cl->tx_block) { + chan->tx_status = r; complete(&chan->tx_complete); + } } static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer) @@ -295,6 +297,8 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg) if (ret == 0) { t = -ETIME; tx_tick(chan, t); + } else if (chan->tx_status < 0) { + t = chan->tx_status; } } diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h index dc93287a2a01..26a238a6f941 100644 --- a/include/linux/mailbox_controller.h +++ b/include/linux/mailbox_controller.h @@ -120,6 +120,7 @@ struct mbox_controller { * @txdone_method: Way to detect TXDone chosen by the API * @cl: Pointer to the current owner of this channel * @tx_complete: Transmission completion + * @tx_status: Transmission status * @active_req: Currently active request hook * @msg_count: No. of mssg currently queued * @msg_free: Index of next available mssg slot @@ -132,6 +133,7 @@ struct mbox_chan { unsigned txdone_method; struct mbox_client *cl; struct completion tx_complete; + int tx_status; void *active_req; unsigned msg_count, msg_free; void *msg_data[MBOX_TX_QUEUE_LEN]; From 4f176444dcc977d1888fd9220c357a4d32338ee0 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 6 May 2026 09:09:47 +0200 Subject: [PATCH 12/23] mailbox: don't free the channel if the startup callback failed If the optional startup() callbacks fails, we need to clear some states. Currently, this is done by freeing the channel. This does, however, more than needed which creates problems. Namely, it is calling the shutdown() callback. This is totally not intuitive. No user expects that shutdown() is called when startup() fails, similar to remove() not being called when probe() fails. Currently, quite some mailbox users register the IRQ in startup() and free them in shutdown(). These drivers will get a WARN about freeing an already free IRQ. Other subtle issues could arise from this unexpected behaviour. To solve this problem, introduce a helper which does the minimal cleanup and use it in both, in free_channel() and after startup() failed. Link: https://sashiko.dev/#/patchset/20260402112709.13002-1-wsa%2Brenesas%40sang-engineering.com # second issue Fixes: 2b6d83e2b8b7 ("mailbox: Introduce framework for mailbox") Signed-off-by: Wolfram Sang Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index 066702e5a46f..ef88474501b9 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -335,6 +335,19 @@ int mbox_flush(struct mbox_chan *chan, unsigned long timeout) } EXPORT_SYMBOL_GPL(mbox_flush); +static void mbox_clean_and_put_channel(struct mbox_chan *chan) +{ + /* The queued TX requests are simply aborted, no callbacks are made */ + scoped_guard(spinlock_irqsave, &chan->lock) { + chan->cl = NULL; + chan->active_req = MBOX_NO_MSG; + if (chan->txdone_method == MBOX_TXDONE_BY_ACK) + chan->txdone_method = MBOX_TXDONE_BY_POLL; + } + + module_put(chan->mbox->dev->driver->owner); +} + static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl) { struct device *dev = cl->dev; @@ -358,10 +371,9 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl) if (chan->mbox->ops->startup) { ret = chan->mbox->ops->startup(chan); - if (ret) { dev_err(dev, "Unable to startup the chan (%d)\n", ret); - mbox_free_channel(chan); + mbox_clean_and_put_channel(chan); return ret; } } @@ -503,15 +515,7 @@ void mbox_free_channel(struct mbox_chan *chan) if (chan->mbox->ops->shutdown) chan->mbox->ops->shutdown(chan); - /* The queued TX requests are simply aborted, no callbacks are made */ - scoped_guard(spinlock_irqsave, &chan->lock) { - chan->cl = NULL; - chan->active_req = MBOX_NO_MSG; - if (chan->txdone_method == MBOX_TXDONE_BY_ACK) - chan->txdone_method = MBOX_TXDONE_BY_POLL; - } - - module_put(chan->mbox->dev->driver->owner); + mbox_clean_and_put_channel(chan); } EXPORT_SYMBOL_GPL(mbox_free_channel); From 5e4907c4908bff6570f48aff86fef424e74c051f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 6 May 2026 09:16:06 +0200 Subject: [PATCH 13/23] mailbox: add list of used channels to debugfs During development, it is useful to see which mailboxes are currently obtained. Use a seq-file in debugfs to list the currently registered controllers and their used channels. Example output from a Renesas R-Car X5H based system: # cat /sys/kernel/debug/mailbox/mailbox_summary 189e0000.system-controller: 0: c1000000.mailbox_test_send_to_recv 1: c1000100.mailbox_test_recv_to_send 128: c1000100.mailbox_test_recv_to_send 129: c1000000.mailbox_test_send_to_recv 189e1000.system-controller: 4: scmi_dev.1 5: scmi_dev.2 Signed-off-by: Wolfram Sang Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox.c | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index ef88474501b9..efacd24a085d 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include static LIST_HEAD(mbox_cons); @@ -644,3 +646,66 @@ int devm_mbox_controller_register(struct device *dev, return 0; } EXPORT_SYMBOL_GPL(devm_mbox_controller_register); + +#ifdef CONFIG_DEBUG_FS +static void *mbox_seq_start(struct seq_file *s, loff_t *pos) +{ + mutex_lock(&con_mutex); + return seq_list_start(&mbox_cons, *pos); +} + +static void *mbox_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return seq_list_next(v, &mbox_cons, pos); +} + +static void mbox_seq_stop(struct seq_file *s, void *v) +{ + mutex_unlock(&con_mutex); +} + +static int mbox_seq_show(struct seq_file *seq, void *v) +{ + const struct mbox_controller *mbox = list_entry(v, struct mbox_controller, node); + + seq_printf(seq, "%s:\n", dev_name(mbox->dev)); + + for (unsigned int i = 0; i < mbox->num_chans; i++) { + struct mbox_chan *chan = &mbox->chans[i]; + + scoped_guard(spinlock_irqsave, &chan->lock) { + if (chan->cl) { + struct device *cl_dev = chan->cl->dev; + + seq_printf(seq, " %3u: %s\n", i, + cl_dev ? dev_name(cl_dev) : "NULL device"); + } + } + } + + return 0; +} + +static const struct seq_operations mbox_sops = { + .start = mbox_seq_start, + .next = mbox_seq_next, + .stop = mbox_seq_stop, + .show = mbox_seq_show, +}; +DEFINE_SEQ_ATTRIBUTE(mbox); + +/* + * subsys_initcall() is used here but controllers may already have been + * registered earlier or will be later. The rationale is that debugfs is + * accessed only late, i.e. from userspace. So, files created here must make no + * assumptions about initcall ordering. + */ +static int __init mbox_init(void) +{ + struct dentry *mbox_debugfs = debugfs_create_dir("mailbox", NULL); + + debugfs_create_file("mailbox_summary", 0444, mbox_debugfs, NULL, &mbox_fops); + return 0; +} +subsys_initcall(mbox_init); +#endif /* DEBUG_FS */ From 9b2ffcfb32e27a936da867e14b29e3648f3d4ac4 Mon Sep 17 00:00:00 2001 From: Chunkai Deng Date: Tue, 26 May 2026 11:38:03 +0800 Subject: [PATCH 14/23] dt-bindings: mailbox: qcom: Add IPCC support for Maili Platform Document the Inter-Processor Communication Controller on the Qualcomm Maili Platform, which will be used to route interrupts across various subsystems found on the SoC. Signed-off-by: Chunkai Deng Reviewed-by: Manivannan Sadhasivam Signed-off-by: Jassi Brar --- Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml index fae20a6615f6..3839e1f5f904 100644 --- a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml +++ b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml @@ -28,6 +28,7 @@ properties: - qcom,glymur-ipcc - qcom,hawi-ipcc - qcom,kaanapali-ipcc + - qcom,maili-ipcc - qcom,milos-ipcc - qcom,qcs8300-ipcc - qcom,qdu1000-ipcc From 82ef9a635d7130ca27ec9dd88c16afc39c83a4e8 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:26 +0200 Subject: [PATCH 15/23] mailbox: imx: Forward the timeout/ error in imx_mu_generic_tx() imx_mu_generic_tx() for the IMX_MU_TYPE_TXDB_V2 type polls on a register which may timeout and is recognized as an error. This error is siltently dropped and not dropped to the caller. Forward the error to the caller. Fixes: b5ef17917f3a7 ("mailbox: imx: fix TXDB_V2 channel race condition") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 246a9a9e3952..0028073be4a7 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -227,6 +227,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, u32 val; int ret, count; + ret = 0; switch (cp->type) { case IMX_MU_TYPE_TX: imx_mu_write(priv, *arg, priv->dcfg->xTR + cp->idx * 4); @@ -259,7 +260,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv, return -EINVAL; } - return 0; + return ret; } static int imx_mu_generic_rx(struct imx_mu_priv *priv, From 5ccea7eacb7786c358833634f45700365f6c1d99 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:27 +0200 Subject: [PATCH 16/23] mailbox: imx: Add a channel shutdown field sashiko complained about possible teardown problem. The scenario CPU 0 CPU 1 imx_mu_isr() imx_mu_shutdown() imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); imx_mu_specific_rx() imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0); free_irq() The RX event remains enabled because in this short window the RX event was disabled in ->shutdown() while the interrupt was active and then enabled again by the ISR while ->shutdown waited in free_irq(). This race requires timing and if happens can be problematic on shared handlers if the "removed" channel triggers an interrupt. In this case the irq-core will shutdown the interrupt with the "nobody cared" message. Introduce imx_mu_con_priv::shutdown to signal that the channel is shutting down. This flag is set with the lock held (by imx_mu_xcr_clr_shut()). The unmask side uses imx_mu_xcr_set_act() which only enables the event if the channel has not been shutdown and serialises on the same lock. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 40 +++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 0028073be4a7..e261b382d5c9 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -80,6 +80,7 @@ struct imx_mu_con_priv { enum imx_mu_chan_type type; struct mbox_chan *chan; struct work_struct txdb_work; + bool shutdown; }; struct imx_mu_priv { @@ -219,6 +220,36 @@ static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 se return val; } +static void imx_mu_xcr_clr_shut(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, + enum imx_mu_xcr type, u32 clr) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->xcr_lock, flags); + cp->shutdown = true; + + val = imx_mu_read(priv, priv->dcfg->xCR[type]); + val &= ~clr; + imx_mu_write(priv, val, priv->dcfg->xCR[type]); + spin_unlock_irqrestore(&priv->xcr_lock, flags); +} + +static void imx_mu_xcr_set_act(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, + enum imx_mu_xcr type, u32 set) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->xcr_lock, flags); + if (!cp->shutdown) { + val = imx_mu_read(priv, priv->dcfg->xCR[type]); + val |= set; + imx_mu_write(priv, val, priv->dcfg->xCR[type]); + } + spin_unlock_irqrestore(&priv->xcr_lock, flags); +} + static int imx_mu_generic_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data) @@ -377,7 +408,7 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * *data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % num_rr) * 4); } - imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0); + imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0)); mbox_chan_received_data(cp->chan, (void *)priv->msg); return 0; @@ -605,6 +636,7 @@ static int imx_mu_startup(struct mbox_chan *chan) return ret; } + cp->shutdown = false; switch (cp->type) { case IMX_MU_TYPE_RX: imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx), 0); @@ -639,13 +671,13 @@ static void imx_mu_shutdown(struct mbox_chan *chan) switch (cp->type) { case IMX_MU_TYPE_TX: - imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); + imx_mu_xcr_clr_shut(priv, cp, IMX_MU_TCR, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); break; case IMX_MU_TYPE_RX: - imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); + imx_mu_xcr_clr_shut(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); break; case IMX_MU_TYPE_RXDB: - imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); + imx_mu_xcr_clr_shut(priv, cp, IMX_MU_GIER, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); break; case IMX_MU_TYPE_RST: imx_mu_xcr_rmw(priv, IMX_MU_CR, IMX_MU_xCR_RST(priv->dcfg->type), 0); From 1f602619e408b6e9655ee76656a2a5ab6e89c5e4 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:28 +0200 Subject: [PATCH 17/23] mailbox: imx: Use devm_pm_runtime_enable() sashiko complained about early usage of the device while probe isn't completed. This can be mitigated by delaying the pm_runtime_enable() into the removal path instead doing it early. This ensures that in an error case the device is removed (and imx_mu_shutdown()) before pm_runtime_disable() so we don't have to do this manually. For the order to work, lets move devm_mbox_controller_register() until after the pm-runtime part. So the reverse order will be mbox-controller removal followed by disabling pm runtime. Use devm_pm_runtime_enable(), remove manual pm_runtime_disable() invocations and move the pm_runtime handling in probe before devm_mbox_controller_register(). Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index e261b382d5c9..516a05b64daa 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -966,38 +966,36 @@ static int imx_mu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - ret = devm_mbox_controller_register(dev, &priv->mbox); - if (ret) + ret = devm_pm_runtime_enable(dev); + if (ret < 0) goto disable_clk; - of_platform_populate(dev->of_node, NULL, NULL, dev); - - pm_runtime_enable(dev); - ret = pm_runtime_resume_and_get(dev); if (ret < 0) - goto disable_runtime_pm; + goto disable_clk; ret = pm_runtime_put_sync(dev); if (ret < 0) - goto disable_runtime_pm; + goto disable_clk; clk_disable_unprepare(priv->clk); + ret = devm_mbox_controller_register(dev, &priv->mbox); + if (ret) + goto err_out; + + of_platform_populate(dev->of_node, NULL, NULL, dev); + return 0; -disable_runtime_pm: - pm_runtime_disable(dev); disable_clk: clk_disable_unprepare(priv->clk); +err_out: return ret; } static void imx_mu_remove(struct platform_device *pdev) { - struct imx_mu_priv *priv = platform_get_drvdata(pdev); - - pm_runtime_disable(priv->dev); } static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { From dd1b321e8024fb01404fe163076c9010c5df8608 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:29 +0200 Subject: [PATCH 18/23] mailbox: imx: use devm_of_platform_populate() The driver uses of_platform_populate() but does not remove the added devices on removal. This can lead to "double devices" on module removal followed by adding the module again. Use devm_of_platform_populate() to remove the populated devices once the parent device is removed. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 516a05b64daa..a8d4e970fb78 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -984,7 +984,7 @@ static int imx_mu_probe(struct platform_device *pdev) if (ret) goto err_out; - of_platform_populate(dev->of_node, NULL, NULL, dev); + devm_of_platform_populate(dev); return 0; From fbc0f319cee18f40ae3f8658086217c655ad2489 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:30 +0200 Subject: [PATCH 19/23] mailbox: imx: Use channel index instead of zero in imx_mu_specific_rx() imx_mu_specific_rx() masks channel 0 and unmasks it again at the end of the function. Given that at startup the channel index got unmasked it should do the right job. This here either unmasks the actual channel or another one but should have no impact given that it reverses its doing at the end. Peng Fan commented here: | For specific rx channel, whether it is i.MX8 SCU or i.MX ELE, actually there is | only 1 channel as of now, but it seems better to use cp->idx in case more | channels in future. Use the channel index instead of zero. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index a8d4e970fb78..408cd083c64e 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -381,7 +381,7 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * data = (u32 *)priv->msg; - imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, 0)); + imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); *data++ = imx_mu_read(priv, priv->dcfg->xRR); if (priv->dcfg->type & IMX_MU_V2_S4) { @@ -408,7 +408,7 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * *data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % num_rr) * 4); } - imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0)); + imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); mbox_chan_received_data(cp->chan, (void *)priv->msg); return 0; From 692e1bc96a5dc7d5c43d937c2a50b56303544dea Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:31 +0200 Subject: [PATCH 20/23] mailbox: imx: Start splitting the IRQ handler in primary and threaded handler Split the mailbox irq handling into a primary handler (imx_mu_isr()) and a threaded handler (imx_mu_isr_th()). The primary handler masks the interrupt event so the threaded handler can run without raising the interrupt again. The goal here is to invoke the mailbox core functions (such as mbox_chan_received_data(), mbox_chan_txdone()) in preemptible context which is made possible by using an threaded interrupt handler. This in turn means that mailbox's client callbacks are invoked in preemptible context, too. This then allows the mailbox client callback to skip an indirection via a workqueue if it requries preemptible callback. As a first step, prepare the logic and move TX handling part. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 408cd083c64e..87acc43cb99c 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -540,11 +540,31 @@ static void imx_mu_txdb_work(struct work_struct *t) mbox_chan_txdone(cp->chan, 0); } +static irqreturn_t imx_mu_isr_th(int irq, void *p) +{ + struct mbox_chan *chan = p; + struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); + struct imx_mu_con_priv *cp = chan->con_priv; + + switch (cp->type) { + case IMX_MU_TYPE_TX: + mbox_chan_txdone(chan, 0); + break; + + default: + dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n", + cp->type); + return IRQ_NONE; + } + return IRQ_HANDLED; +} + static irqreturn_t imx_mu_isr(int irq, void *p) { struct mbox_chan *chan = p; struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); struct imx_mu_con_priv *cp = chan->con_priv; + irqreturn_t ret = IRQ_HANDLED; u32 val, ctrl; switch (cp->type) { @@ -580,7 +600,7 @@ static irqreturn_t imx_mu_isr(int irq, void *p) if ((val == IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_TX)) { imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx)); - mbox_chan_txdone(chan, 0); + ret = IRQ_WAKE_THREAD; } else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_RX)) { priv->dcfg->rx(priv, cp); @@ -595,7 +615,7 @@ static irqreturn_t imx_mu_isr(int irq, void *p) if (priv->suspend) pm_system_wakeup(); - return IRQ_HANDLED; + return ret; } static int imx_mu_send_data(struct mbox_chan *chan, void *data) @@ -630,7 +650,8 @@ static int imx_mu_startup(struct mbox_chan *chan) if (!(priv->dcfg->type & IMX_MU_V2_IRQ)) irq_flag |= IRQF_SHARED; - ret = request_irq(priv->irq[cp->type], imx_mu_isr, irq_flag, cp->irq_desc, chan); + ret = request_threaded_irq(priv->irq[cp->type], imx_mu_isr, imx_mu_isr_th, + irq_flag, cp->irq_desc, chan); if (ret) { dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq[cp->type]); return ret; From e55bea377d492f0fd4a7f657fc2a9247b5b96afb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:32 +0200 Subject: [PATCH 21/23] mailbox: imx: Move the RX part of the mailbox into the threaded handler Move RX callback handling into the threaded handler. This is similar to the TX side except that we explicitly mask the source interrupt in the primary handler and unmask it in the threaded handler again after success. This was done automatically in the TX part. The masking/ unmasking can be removed from imx_mu_specific_rx() since it already happens in the primary/ threaded handler before invoking the channel specific callback. Move RX channel handling into threaded handler. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 87acc43cb99c..1219c35b116e 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -381,7 +381,6 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * data = (u32 *)priv->msg; - imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); *data++ = imx_mu_read(priv, priv->dcfg->xRR); if (priv->dcfg->type & IMX_MU_V2_S4) { @@ -408,7 +407,6 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv * *data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % num_rr) * 4); } - imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); mbox_chan_received_data(cp->chan, (void *)priv->msg); return 0; @@ -551,6 +549,11 @@ static irqreturn_t imx_mu_isr_th(int irq, void *p) mbox_chan_txdone(chan, 0); break; + case IMX_MU_TYPE_RX: + if (!priv->dcfg->rx(priv, cp)) + imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); + break; + default: dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n", cp->type); @@ -603,7 +606,8 @@ static irqreturn_t imx_mu_isr(int irq, void *p) ret = IRQ_WAKE_THREAD; } else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_RX)) { - priv->dcfg->rx(priv, cp); + imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); + ret = IRQ_WAKE_THREAD; } else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_RXDB)) { priv->dcfg->rxdb(priv, cp); From 3225a745f51747787cb05de85ab44e962a3c664b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:33 +0200 Subject: [PATCH 22/23] mailbox: imx: Move the RXDB part of the mailbox into the threaded handler Move RXDB callback handling into the threaded handler. This similar to the RX side and since the imx_mu_dcfg::rxdb callback can return an error, the interrupt is only enabled on success. Move RXDB callback handling into the threaded handler. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 1219c35b116e..1bd434bdff87 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -554,6 +554,11 @@ static irqreturn_t imx_mu_isr_th(int irq, void *p) imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx)); break; + case IMX_MU_TYPE_RXDB: + if (!priv->dcfg->rxdb(priv, cp)) + imx_mu_xcr_set_act(priv, cp, IMX_MU_GIER, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); + break; + default: dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n", cp->type); @@ -610,7 +615,8 @@ static irqreturn_t imx_mu_isr(int irq, void *p) ret = IRQ_WAKE_THREAD; } else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) && (cp->type == IMX_MU_TYPE_RXDB)) { - priv->dcfg->rxdb(priv, cp); + imx_mu_xcr_rmw(priv, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(priv->dcfg->type, cp->idx)); + ret = IRQ_WAKE_THREAD; } else { dev_warn_ratelimited(priv->dev, "Not handled interrupt\n"); return IRQ_NONE; From 36cac4b5101f8ecbc851356df175b99543c84ec6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 17 Jun 2026 08:55:34 +0200 Subject: [PATCH 23/23] mailbox: imx: Don't force-thread the primary handler The primary interrupt handler (imx_mu_isr()) no longer invokes any callbacks it only masks the interrupt source and returns. In a forced-threaded environment the IRQ-core will force-thread the primary handler which can be avoided. The primary handler uses a spinlock_t to protect the RMW operation in imx_mu_xcr_rmw() - nothing that may introduce long latencies. The lock can be turned into a raw_spinlock_t and then the primary handler can run in hardirq context even on PREEMPT_RT skipping one thread. Make struct imx_mu_priv::xcr_lock a raw_spinlock_t and skip force-threading the primrary handler by marking it IRQF_NO_THREAD. Reviewed-by: Peng Fan Reviewed-by: Mathieu Poirier Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Jassi Brar --- drivers/mailbox/imx-mailbox.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 1bd434bdff87..40bad2b3e4d1 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -87,7 +87,7 @@ struct imx_mu_priv { struct device *dev; void __iomem *base; void *msg; - spinlock_t xcr_lock; /* control register lock */ + raw_spinlock_t xcr_lock; /* control register lock */ struct mbox_controller mbox; struct mbox_chan mbox_chans[IMX_MU_CHANS]; @@ -207,15 +207,14 @@ static int imx_mu_rx_waiting_read(struct imx_mu_priv *priv, u32 *val, u32 idx) static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 set, u32 clr) { - unsigned long flags; u32 val; - spin_lock_irqsave(&priv->xcr_lock, flags); + guard(raw_spinlock_irqsave)(&priv->xcr_lock); + val = imx_mu_read(priv, priv->dcfg->xCR[type]); val &= ~clr; val |= set; imx_mu_write(priv, val, priv->dcfg->xCR[type]); - spin_unlock_irqrestore(&priv->xcr_lock, flags); return val; } @@ -223,31 +222,27 @@ static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 se static void imx_mu_xcr_clr_shut(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, enum imx_mu_xcr type, u32 clr) { - unsigned long flags; u32 val; - spin_lock_irqsave(&priv->xcr_lock, flags); + guard(raw_spinlock_irqsave)(&priv->xcr_lock); cp->shutdown = true; val = imx_mu_read(priv, priv->dcfg->xCR[type]); val &= ~clr; imx_mu_write(priv, val, priv->dcfg->xCR[type]); - spin_unlock_irqrestore(&priv->xcr_lock, flags); } static void imx_mu_xcr_set_act(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, enum imx_mu_xcr type, u32 set) { - unsigned long flags; u32 val; - spin_lock_irqsave(&priv->xcr_lock, flags); + guard(raw_spinlock_irqsave)(&priv->xcr_lock); if (!cp->shutdown) { val = imx_mu_read(priv, priv->dcfg->xCR[type]); val |= set; imx_mu_write(priv, val, priv->dcfg->xCR[type]); } - spin_unlock_irqrestore(&priv->xcr_lock, flags); } static int imx_mu_generic_tx(struct imx_mu_priv *priv, @@ -640,7 +635,7 @@ static int imx_mu_startup(struct mbox_chan *chan) { struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); struct imx_mu_con_priv *cp = chan->con_priv; - unsigned long irq_flag = 0; + unsigned long irq_flag = IRQF_NO_THREAD; int ret; pm_runtime_get_sync(priv->dev); @@ -988,7 +983,7 @@ static int imx_mu_probe(struct platform_device *pdev) goto disable_clk; } - spin_lock_init(&priv->xcr_lock); + raw_spin_lock_init(&priv->xcr_lock); priv->mbox.dev = dev; priv->mbox.ops = &imx_mu_ops;