in arm_mhuv2.c [537:617]
static irqreturn_t mhuv2_sender_interrupt(int irq, void *data)
{
struct mhuv2 *mhu = data;
struct device *dev = mhu->mbox.dev;
struct mhuv2_mbox_chan_priv *priv;
struct mbox_chan *chan;
unsigned long flags;
int i, found = 0;
u32 stat;
chan = get_irq_chan_comb(mhu, mhu->send->chcomb_int_st);
if (IS_ERR(chan)) {
dev_warn(dev, "Failed to find channel for the Tx interrupt\n");
return IRQ_NONE;
}
priv = chan->con_priv;
if (!IS_PROTOCOL_DOORBELL(priv)) {
writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx + priv->windows - 1].int_clr);
if (chan->cl) {
mbox_chan_txdone(chan, 0);
return IRQ_HANDLED;
}
dev_warn(dev, "Tx interrupt Received on channel (%u) not currently attached to a mailbox client\n",
priv->ch_wn_idx);
return IRQ_NONE;
}
/* Clear the interrupt first, so we don't miss any doorbell later */
writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx].int_clr);
/*
* In Doorbell mode, make sure no new transitions happen while the
* interrupt handler is trying to find the finished doorbell tx
* operations, else we may think few of the transfers were complete
* before they actually were.
*/
spin_lock_irqsave(&mhu->doorbell_pending_lock, flags);
/*
* In case of doorbell mode, the first channel of the window is returned
* by get_irq_chan_comb(). Find all the pending channels here.
*/
stat = readl_relaxed(&mhu->send->ch_wn[priv->ch_wn_idx].stat);
for (i = 0; i < MHUV2_STAT_BITS; i++) {
priv = chan[i].con_priv;
/* Find cases where pending was 1, but stat's bit is cleared */
if (priv->pending ^ ((stat >> i) & 0x1)) {
BUG_ON(!priv->pending);
if (!chan->cl) {
dev_warn(dev, "Tx interrupt received on doorbell (%u : %u) channel not currently attached to a mailbox client\n",
priv->ch_wn_idx, i);
continue;
}
mbox_chan_txdone(&chan[i], 0);
priv->pending = 0;
found++;
}
}
spin_unlock_irqrestore(&mhu->doorbell_pending_lock, flags);
if (!found) {
/*
* We may have already processed the doorbell in the previous
* iteration if the interrupt came right after we cleared it but
* before we read the stat register.
*/
dev_dbg(dev, "Couldn't find the doorbell (%u) for the Tx interrupt interrupt\n",
priv->ch_wn_idx);
return IRQ_NONE;
}
return IRQ_HANDLED;
}