summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-23 07:47:40 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-23 07:47:40 -0700
commit515db262143e48f09b5dce07bc0db67b8b4d6a73 (patch)
tree5c61af225c1b95bbc18e96131ca7335866e7f552
parent16b2087efdddd0bf042accdbdcc8eedc21bf9227 (diff)
parent36cac4b5101f8ecbc851356df175b99543c84ec6 (diff)
Merge tag 'mailbox-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox
Pull mailbox updates from Jassi Brar: "Core: - add debugfs support for used channels - fix resource leak on startup failure - propagate tx error codes - clarify blocking mode thread support Drivers: - exynos: remove unused register definitions - imx: refactor IRQ handlers, migrate to devm helpers, and other minor improvements - mpfs: fix syscon presence check in inbox ISR - mtk-adsp: fix use-after-free during device teardown - qcom: add dt-bindings for QCOM Maili, Hawi, Shikra APCS, and Nord CPUCP platform support" * tag 'mailbox-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox: (23 commits) mailbox: imx: Don't force-thread the primary handler mailbox: imx: Move the RXDB part of the mailbox into the threaded handler mailbox: imx: Move the RX part of the mailbox into the threaded handler mailbox: imx: Start splitting the IRQ handler in primary and threaded handler mailbox: imx: Use channel index instead of zero in imx_mu_specific_rx() mailbox: imx: use devm_of_platform_populate() mailbox: imx: Use devm_pm_runtime_enable() mailbox: imx: Add a channel shutdown field mailbox: imx: Forward the timeout/ error in imx_mu_generic_tx() dt-bindings: mailbox: qcom: Add IPCC support for Maili Platform mailbox: add list of used channels to debugfs mailbox: don't free the channel if the startup callback failed mailbox: Make mbox_send_message() return error code when tx fails mailbox: Clarify multi-thread is not supported in blocking mode mailbox: mtk-adsp: fix UAF during device teardown mailbox: qcom: Unify user-visible "Qualcomm" name mailbox: exynos: Drop unused register definitions dt-bindings: mailbox: qcom: Add IPCC support for Hawi Platform dt-bindings: mailbox: qcom,cpucp-mbox: Add Hawi compatible dt-bindings: mailbox: qcom: Add Shikra APCS compatible ...
-rw-r--r--Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml1
-rw-r--r--Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml2
-rw-r--r--Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml2
-rw-r--r--drivers/mailbox/Kconfig4
-rw-r--r--drivers/mailbox/exynos-mailbox.c7
-rw-r--r--drivers/mailbox/imx-mailbox.c117
-rw-r--r--drivers/mailbox/mailbox-mpfs.c2
-rw-r--r--drivers/mailbox/mailbox.c101
-rw-r--r--drivers/mailbox/mtk-adsp-mailbox.c9
-rw-r--r--drivers/mailbox/qcom-cpucp-mbox.c35
-rw-r--r--include/linux/mailbox_controller.h2
11 files changed, 224 insertions, 58 deletions
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
diff --git a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml
index 90bfde66cc4a..03359479d926 100644
--- a/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml
+++ b/Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml
@@ -19,7 +19,9 @@ properties:
- items:
- enum:
- qcom,glymur-cpucp-mbox
+ - qcom,hawi-cpucp-mbox
- qcom,kaanapali-cpucp-mbox
+ - qcom,nord-cpucp-mbox
- qcom,sm8750-cpucp-mbox
- const: qcom,x1e80100-cpucp-mbox
- enum:
diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml
index f5c584cf2146..3839e1f5f904 100644
--- a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml
+++ b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml
@@ -26,7 +26,9 @@ properties:
- enum:
- qcom,eliza-ipcc
- qcom,glymur-ipcc
+ - qcom,hawi-ipcc
- qcom,kaanapali-ipcc
+ - qcom,maili-ipcc
- qcom,milos-ipcc
- qcom,qcs8300-ipcc
- qcom,qdu1000-ipcc
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
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 <linux/platform_device.h>
#include <linux/slab.h>
-#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)
diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c
index 246a9a9e3952..40bad2b3e4d1 100644
--- a/drivers/mailbox/imx-mailbox.c
+++ b/drivers/mailbox/imx-mailbox.c
@@ -80,13 +80,14 @@ 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 {
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];
@@ -206,19 +207,44 @@ 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;
}
+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)
+{
+ u32 val;
+
+ 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]);
+}
+
+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)
+{
+ u32 val;
+
+ 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]);
+ }
+}
+
static int imx_mu_generic_tx(struct imx_mu_priv *priv,
struct imx_mu_con_priv *cp,
void *data)
@@ -227,6 +253,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 +286,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,
@@ -349,7 +376,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, 0));
*data++ = imx_mu_read(priv, priv->dcfg->xRR);
if (priv->dcfg->type & IMX_MU_V2_S4) {
@@ -376,7 +402,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_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0);
mbox_chan_received_data(cp->chan, (void *)priv->msg);
return 0;
@@ -508,11 +533,41 @@ 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;
+
+ 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;
+
+ 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);
+ 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) {
@@ -548,13 +603,15 @@ 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);
+ 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);
+ 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;
@@ -563,7 +620,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)
@@ -578,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);
@@ -598,12 +655,14 @@ 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;
}
+ 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);
@@ -638,13 +697,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);
@@ -924,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;
@@ -933,38 +992,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;
+
+ devm_of_platform_populate(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 = {
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);
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index bbc9fd75a95f..efacd24a085d 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -7,6 +7,7 @@
*/
#include <linux/cleanup.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -16,6 +17,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/property.h>
+#include <linux/seq_file.h>
#include <linux/spinlock.h>
static LIST_HEAD(mbox_cons);
@@ -98,8 +100,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)
@@ -258,6 +262,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).
@@ -291,6 +299,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;
}
}
@@ -327,6 +337,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;
@@ -350,10 +373,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;
}
}
@@ -495,15 +517,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);
@@ -632,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 */
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;
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 <linux/module.h>
#include <linux/platform_device.h>
-#define APSS_CPUCP_IPC_CHAN_SUPPORTED 3
#define APSS_CPUCP_MBOX_CMD_OFF 0x4
/* Tx Registers */
@@ -27,6 +26,14 @@
#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
* @mbox: The mailbox controller
@@ -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);
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];