Re: [PATCH v2 05/10] mailbox: add MediaTek GPUEB IPI mailbox

Nicolas Frattaroli
2025-09-15T13:34:54Z

On Saturday, 13 September 2025 00:11:10 Central European Summer Time Chia-I Wu wrote:
> On Fri, Sep 12, 2025 at 11:38 AM Nicolas Frattaroli
> <nicolas.frattaroli@collabora.com> wrote:
> <snipped>
> > +static irqreturn_t mtk_gpueb_mbox_thread(int irq, void *data)
> > +{
> > + struct mtk_gpueb_mbox_chan *ch = data;
> > + int status;
> > +
> > + status = atomic_cmpxchg(&ch->rx_status,
> > + MBOX_FULL | MBOX_CLOGGED, MBOX_FULL);
> > + if (status == (MBOX_FULL | MBOX_CLOGGED)) {
> > + mtk_gpueb_mbox_read_rx(ch);
> > + writel(BIT(ch->num), ch->ebm->mbox_ctl + MBOX_CTL_IRQ_CLR);
> > + mbox_chan_received_data(&ch->ebm->mbox.chans[ch->num],
> > + ch->rx_buf);
> Given what other drivers do, and how mtk_mfg consumes the data, we should
>
> char buf[MAX_OF_RX_LEN]; // MAX_OF_RX_LEN is 32; we can also
> allocate it during probe
> mtk_gpueb_mbox_read_rx(ch);
> mbox_chan_received_data(..., buf);
>
> mtx_mfg makes a copy eventually anyway.

We don't right now, at least not until after the callback returns.
So we need to have the copy in the mtk_mfg callback, not after the
completion. That's fine and I do want to do this as this is what
the mailbox framework seems to expect clients to do.

> We don't need to maintain any
> extra copy.
>
> Then we might not need rx_status.

We can probably get rid of it if we keep the per-channel
interrupt handler. Otherwise, we may still need clogged,
as we don't want to process interrupts on channels we have
no user for.

>
> > + atomic_set(&ch->rx_status, 0);
> > + return IRQ_HANDLED;
> > + }
> > +
> > + return IRQ_NONE;
> > +}
> > +
> > +static int mtk_gpueb_mbox_send_data(struct mbox_chan *chan, void *data)
> > +{
> > + struct mtk_gpueb_mbox_chan *ch = chan->con_priv;
> > + int i;
> > + u32 *values = data;
> > +
> > + if (atomic_read(&ch->rx_status))
> > + return -EBUSY;
> > +
> > + /*
> > + * We don't want any fancy nonsense, just write the 32-bit values in
> > + * order. memcpy_toio/__iowrite32_copy don't work here, because fancy.
> > + */
> > + for (i = 0; i < ch->c->tx_len; i += 4)
> > + writel(values[i / 4], ch->ebm->mbox_mmio + ch->c->tx_offset + i);
> > +
> > + writel(BIT(ch->num), ch->ebm->mbox_ctl + MBOX_CTL_IRQ_SET);
> > +
> > + return 0;
> > +}
> > +
> > +static int mtk_gpueb_mbox_startup(struct mbox_chan *chan)
> > +{
> > + struct mtk_gpueb_mbox_chan *ch = chan->con_priv;
> > + int ret;
> > +
> > + atomic_set(&ch->rx_status, 0);
> > +
> > + ret = clk_enable(ch->ebm->clk);
> > + if (ret) {
> > + dev_err(ch->ebm->dev, "Failed to enable EB clock: %pe\n",
> > + ERR_PTR(ret));
> > + goto err_clog;
> > + }
> > +
> > + writel(BIT(ch->num), ch->ebm->mbox_ctl + MBOX_CTL_IRQ_CLR);
> > +
> > + ret = devm_request_threaded_irq(ch->ebm->dev, ch->ebm->irq, mtk_gpueb_mbox_isr,
> > + mtk_gpueb_mbox_thread, IRQF_SHARED | IRQF_ONESHOT,
> > + ch->full_name, ch);
> I don't think this warrants a per-channel irq thread.
>
> mbox_chan_received_data is atomic. I think wecan start simple with
> just a devm_request_irq for all channels. mtk_gpueb_mbox_isr can
>
> read bits from MBOX_CTL_RX_STS
> for each bit set:
> read data from rx
> mbox_chan_received_data
> write bits to MBOX_CTL_IRQ_CLR
>

I don't like this approach. It brings us back to having to process
multiple channels per ISR, keep track of when the interrupt should
be enabled and disabled based on how many channels are in use, and
also is not in line with what e.g. omap-mailbox.c does.

Remember that `mbox_chan_received_data` synchronously calls the
mailbox client's rx_callback. In mediatek_mfg's case, this is
fairly small, though with the request to not make the rx buffer
persist beyond the rx_callback it will gain an additional memory
copy. But we can't guarantee that someone isn't going to put a
slow operation in the path. Sure, it's going to be atomic, but
waiting for a spinlock is atomic and not something an ISR would
enjoy. I don't think mailbox clients would expect that if they
take their time they'll stall the interrupt handler for every
other channel.

So we'd keep the interrupt disabled for all channels until the
client that received a message has processed it.

I can see myself getting rid of the handler and just having the
thread function as the bottom half, but I'd really like to keep
the one-IRQ-request-per-channel thing I've got going now as it
made the code a lot easier to reason about. However, doing this
would mean the interrupt is re-enabled after the generic upper
half, when all the business logic that needs to not run
concurrently for an individual channel is in the bottom half.

As far as I can tell, this would then mean we'd have to add
some concurrency exclusion mechanism to the bottom half.

Moving all the logic into the upper half handler function
would make that handler somewhat longer, and I don't know
if IRQF_ONESHOT masks the interrupt for all users of that
IRQ number or just for those with that dev_id. If it's per
dev_id, then I'm fine with moving stuff up there. But from
my reading of the core IRQ handling code, that does not
appear to be the case; one channel getting a reply would
mask *all* channels of the mailbox until the upper half is
completed, and if the upper half calls into a driver
callback synchronously, that may take a hot minute.

Put differently: Is there a problem with one thread per used
channel, or are we going off vibes here? The way it currently
works uses the shared interrupt to mark just that one channel
as busy with rx_status before letting the IRQ for all channels
be unmasked again, which seems ideal to me.