[PATCH RFC 01/10] mailbox: add direct synchronous send support
Amirreza Zarrabi
amirreza.zarrabi at oss.qualcomm.com
Mon Sep 28 20:16:52 PDT 2026
Some mailbox controllers can complete a transaction entirely within
the calling context, without going through the queued TX state
machine or a TX-done interrupt. Add a synchronous send path so their
clients can request one and wait for the result directly.
Controllers advertise support via the new send_data_sync() op and
clients opt in by setting tx_sync when requesting the channel.
Channels bound this way must not call mbox_chan_txdone() or
mbox_client_txdone(), and mbox_send_message() and mbox_flush() now
reject them, since only mbox_send_message_sync() is a valid
transmit path. The client remains responsible for serializing calls
to mbox_send_message_sync() against mbox_free_channel().
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi at oss.qualcomm.com>
---
drivers/mailbox/mailbox.c | 72 ++++++++++++++++++++++++++++++++++++--
include/linux/mailbox_client.h | 3 ++
include/linux/mailbox_controller.h | 10 ++++++
3 files changed, 83 insertions(+), 2 deletions(-)
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index efacd24a085d..c640b19de608 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -166,6 +166,12 @@ EXPORT_SYMBOL_GPL(mbox_chan_received_data);
*/
void mbox_chan_txdone(struct mbox_chan *chan, int r)
{
+ if (unlikely(chan->txdone_method & MBOX_TXDONE_BY_RETURN)) {
+ dev_err(chan->mbox->dev,
+ "TX-done notification on direct synchronous channel\n");
+ return;
+ }
+
if (unlikely(!(chan->txdone_method & MBOX_TXDONE_BY_IRQ))) {
dev_err(chan->mbox->dev,
"Controller can't run the TX ticker\n");
@@ -187,6 +193,12 @@ EXPORT_SYMBOL_GPL(mbox_chan_txdone);
*/
void mbox_client_txdone(struct mbox_chan *chan, int r)
{
+ if (unlikely(chan->txdone_method & MBOX_TXDONE_BY_RETURN)) {
+ dev_err(chan->mbox->dev,
+ "TX-done notification on direct synchronous channel\n");
+ return;
+ }
+
if (unlikely(!(chan->txdone_method & MBOX_TXDONE_BY_ACK))) {
dev_err(chan->mbox->dev, "Client can't run the TX ticker\n");
return;
@@ -278,6 +290,9 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
if (!chan || !chan->cl || mssg == MBOX_NO_MSG)
return -EINVAL;
+ if (chan->txdone_method & MBOX_TXDONE_BY_RETURN)
+ return -EOPNOTSUPP;
+
t = add_to_rbuf(chan, mssg);
if (t < 0) {
dev_err(chan->mbox->dev, "Try increasing MBOX_TX_QUEUE_LEN\n");
@@ -308,6 +323,43 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg)
}
EXPORT_SYMBOL_GPL(mbox_send_message);
+/**
+ * mbox_send_message_sync - Send data and wait for transaction completion
+ * @chan: Mailbox channel assigned to this client
+ * @mssg: Client specific message typecasted
+ *
+ * For a channel bound with tx_sync, ask the controller to transmit @mssg and
+ * only return on completion. This function may sleep and must not be called
+ * from atomic context. @mssg must remain valid until this function returns.
+ *
+ * The direct synchronous path does not queue @mssg, does not use active_req,
+ * and does not use a TX-done notification. The client must serialize this
+ * function against mbox_free_channel().
+ *
+ * Return: 0 on success or a negative error code.
+ */
+int mbox_send_message_sync(struct mbox_chan *chan, void *mssg)
+{
+ int ret;
+
+ if (!chan || !chan->cl || mssg == MBOX_NO_MSG)
+ return -EINVAL;
+
+ if (!(chan->txdone_method & MBOX_TXDONE_BY_RETURN))
+ return -EOPNOTSUPP;
+
+ if (chan->cl->tx_prepare)
+ chan->cl->tx_prepare(chan->cl, mssg);
+ /* Try to submit a message to the MBOX controller synchonously */
+ ret = chan->mbox->ops->send_data_sync(chan, mssg);
+
+ if (chan->cl->tx_done)
+ chan->cl->tx_done(chan->cl, mssg, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mbox_send_message_sync);
+
/**
* mbox_flush - flush a mailbox channel
* @chan: mailbox channel to flush
@@ -326,8 +378,11 @@ int mbox_flush(struct mbox_chan *chan, unsigned long timeout)
{
int ret;
+ if (chan->txdone_method & MBOX_TXDONE_BY_RETURN)
+ return -EOPNOTSUPP;
+
if (!chan->mbox->ops->flush)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
ret = chan->mbox->ops->flush(chan, timeout);
if (ret < 0)
@@ -343,7 +398,9 @@ static void mbox_clean_and_put_channel(struct mbox_chan *chan)
scoped_guard(spinlock_irqsave, &chan->lock) {
chan->cl = NULL;
chan->active_req = MBOX_NO_MSG;
- if (chan->txdone_method == MBOX_TXDONE_BY_ACK)
+ if (chan->txdone_method & MBOX_TXDONE_BY_RETURN)
+ chan->txdone_method &= ~MBOX_TXDONE_BY_RETURN;
+ else if (chan->txdone_method == MBOX_TXDONE_BY_ACK)
chan->txdone_method = MBOX_TXDONE_BY_POLL;
}
@@ -355,6 +412,14 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl)
struct device *dev = cl->dev;
int ret;
+ if (cl->tx_sync) {
+ if (!chan->mbox->ops->send_data_sync)
+ return -EOPNOTSUPP;
+
+ if (cl->tx_block || cl->tx_tout || cl->knows_txdone)
+ return -EINVAL;
+ }
+
if (chan->cl || !try_module_get(chan->mbox->dev->driver->owner)) {
dev_err(dev, "%s: mailbox not free\n", __func__);
return -EBUSY;
@@ -380,6 +445,9 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl)
}
}
+ if (cl->tx_sync)
+ chan->txdone_method |= MBOX_TXDONE_BY_RETURN;
+
return 0;
}
diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h
index e5997120f45c..32b1d5ad3bfa 100644
--- a/include/linux/mailbox_client.h
+++ b/include/linux/mailbox_client.h
@@ -21,6 +21,7 @@ struct mbox_chan;
* @knows_txdone: If the client could run the TX state machine. Usually
* if the client receives some ACK packet for transmission.
* Unused if the controller already has TX_Done/RTR IRQ.
+ * @tx_sync: Bind the channel for mbox_send_message_sync().
* @rx_callback: Atomic callback to provide client the data received
* @tx_prepare: Atomic callback to ask client to prepare the payload
* before initiating the transmission if required.
@@ -31,6 +32,7 @@ struct mbox_client {
bool tx_block;
unsigned long tx_tout;
bool knows_txdone;
+ bool tx_sync;
void (*rx_callback)(struct mbox_client *cl, void *mssg);
void (*tx_prepare)(struct mbox_client *cl, void *mssg);
@@ -42,6 +44,7 @@ struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,
const char *name);
struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);
int mbox_send_message(struct mbox_chan *chan, void *mssg);
+int mbox_send_message_sync(struct mbox_chan *chan, void *mssg);
int mbox_flush(struct mbox_chan *chan, unsigned long timeout);
void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */
bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */
diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
index 26a238a6f941..c7dc098324ef 100644
--- a/include/linux/mailbox_controller.h
+++ b/include/linux/mailbox_controller.h
@@ -18,6 +18,7 @@ struct mbox_chan;
#define MBOX_TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */
#define MBOX_TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */
#define MBOX_TXDONE_BY_ACK BIT(2) /* S/W ACK received by Client ticks the TX */
+#define MBOX_TXDONE_BY_RETURN BIT(3) /* TX completes by function return */
/**
* struct mbox_chan_ops - methods to control mailbox channels
@@ -28,6 +29,14 @@ struct mbox_chan;
* transmission of data is reported by the controller via
* mbox_chan_txdone (if it has some TX ACK irq). It must not
* sleep.
+ * @send_data_sync: The API asks the MBOX controller driver, in non-atomic
+ * context, to transmit a message on the bus and wait for the
+ * transaction to complete. It returns 0 if the transaction
+ * completed successfully or a negative error code otherwise.
+ * The controller must not call mbox_chan_txdone() or
+ * mbox_client_txdone() for this operation. Concurrent calls for one
+ * controller must support them, serialize them internally, or
+ * return -EBUSY for a conflicting transaction.
* @flush: Called when a client requests transmissions to be blocking but
* the context doesn't allow sleeping. Typically the controller
* will implement a busy loop waiting for the data to flush out.
@@ -53,6 +62,7 @@ struct mbox_chan;
*/
struct mbox_chan_ops {
int (*send_data)(struct mbox_chan *chan, void *data);
+ int (*send_data_sync)(struct mbox_chan *chan, void *data);
int (*flush)(struct mbox_chan *chan, unsigned long timeout);
int (*startup)(struct mbox_chan *chan);
void (*shutdown)(struct mbox_chan *chan);
--
2.34.1
More information about the linux-riscv
mailing list