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
  ...
This commit is contained in:
Linus Torvalds
2026-06-23 07:47:40 -07:00
11 changed files with 224 additions and 58 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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 = {

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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 */
@@ -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);

View File

@@ -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];