[PATCH v7 04/16] mtd: rawnand: sunxi: drain interrupts before reusing the completion
James Hilliard
james.hilliard1 at gmail.com
Thu Oct 15 08:09:08 PDT 2026
On Thu, Oct 15, 2026 at 1:19 AM Sebastian Andrzej Siewior
<bigeasy at linutronix.de> wrote:
>
> On 2026-10-14 19:33:51 [-0600], James Hilliard wrote:
> > --- a/drivers/mtd/nand/raw/sunxi_nand.c
> > +++ b/drivers/mtd/nand/raw/sunxi_nand.c
> > @@ -454,16 +457,19 @@ static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
> > timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
> >
> > if (!use_polling) {
> > - init_completion(&nfc->complete);
> > + reinit_completion(&nfc->complete);
> >
> > writel(events, nfc->regs + NFC_REG_INT);
> >
> > ret = wait_for_completion_timeout(&nfc->complete,
> > msecs_to_jiffies(timeout_ms));
> > - if (!ret)
> > + if (!ret) {
> > + /* Drain the handler before it can restore an old IRQ mask. */
> > + disable_irq(nfc->irq);
> > ret = -ETIMEDOUT;
> > - else
> > + } else {
> > ret = 0;
> > + }
> >
> > writel(0, nfc->regs + NFC_REG_INT);
> > } else {
> > @@ -476,6 +482,12 @@ static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
> >
> > writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
> >
> > + if (!use_polling && ret) {
> > + /* Flush the mask and acknowledgment before re-enabling the IRQ. */
> > + readl(nfc->regs + NFC_REG_INT);
> > + enable_irq(nfc->irq);
>
> Shouldn't you re-read NFC_REG_ST, checking if the event became true even
> in the case of a timeout?
> Say the interrupt wake the thread but the thread never had a chance
> to run. The timer (on another CPU) had a chance to run and did wake this
> thread. Checking the event in the timeout case would help to prevent
> that.
Yes. I've folded a recheck into the IRQ fix for v8, after draining the
handler but before masking and acknowledging the controller events.
One detail is that the handler acknowledges each event and removes it
from NFC_REG_INT. For transfers waiting for both command and DMA
completion, one event may therefore have been consumed already.
Checking status against the original event mask would miss that case.
The timeout path now checks only the remaining enabled events:
if (!ret) {
u32 pending, status;
disable_irq(nfc->irq);
irq_disabled = true;
pending = readl(nfc->regs + NFC_REG_INT) & events;
status = readl(nfc->regs + NFC_REG_ST);
ret = (status & pending) == pending ? 0 : -ETIMEDOUT;
} else {
ret = 0;
}
An empty remaining mask also counts as success: it means the handler
processed all requested events while the timeout path was draining it.
irq_disabled is initialized to false at function entry. After masking
and acknowledging the events, cleanup uses that flag rather than ret:
if (irq_disabled) {
readl(nfc->regs + NFC_REG_INT);
enable_irq(nfc->irq);
}
That preserves the write flush and ensures the IRQ is re-enabled even
when the recheck changes the timeout result to success. Successful
waits and polling are otherwise unchanged.
>
> > + }
> > +
> > if (ret)
> > dev_err(nfc->dev, "wait interrupt timedout\n");
> >
>
> Sebastian
More information about the linux-mtd
mailing list