Skip to content

earlgrey: Properly ack flash operation done interrupt - #390

Open
jesultra wants to merge 1 commit into
OpenPRoT:earlgrey-hwefrom
jesultra:blocking_flash
Open

earlgrey: Properly ack flash operation done interrupt#390
jesultra wants to merge 1 commit into
OpenPRoT:earlgrey-hwefrom
jesultra:blocking_flash

Conversation

@jesultra

@jesultra jesultra commented Aug 2, 2026

Copy link
Copy Markdown

In the blocking flash driver wait_for_notification(), clear the interrupt status on the flash IP before acking the interrupt with the PLIC.

Before this change, the PLIC would re-latch the interrupt immediately after it being ack'ed, as the interrupt request was still active at the flash IP. As a result, next flash operation would erroneously be considered done immediately.

In this case of the flash controller, we were lucky that the interrupt status can be cleared without fully addressing the underlying cause. Had it been a UART input fifo non-empty, then we could not clear the interrupt from within wait_for_notification(), so maybe we need to think about a more generic solution.

The basic problem is that we can only call syscall::interrupt_ack() AFTER the interrupt request from the relevant IP has been de-asserted, and for some IPs, that requires addressing the cause of the interrupt, such as emptying/filling FIFOs.
We could break up the logic in wait_for_notification(), such that FlashCtrlInterrupt would have a new method, maybe ack_notification(), and the convention would be that when handling an interrupt from a blocking thread, the sequence would be:

fn erase(&mut self, start_addr: FlashAddress, size: PowerOf2Usize) -> Result<(), Self::Error> {
    self.driver.start_erase(start_addr, size)?;
    self.blocking.wait_for_notification(|_|
    let return_value = self.driver.complete_op();
    self.blocking.ack_notification();
    return_value
}

That way, the complete_op() gets a chance to address the source of interrupt, before ack_notification() calls syscall::interrupt_ack().
It may be risky to break it up like that, since any early exit from the function could lead to ack_notification() not being called, in effect inhibiting the interrupt in question until next reset. Maybe instead some closure:

fn erase(&mut self, start_addr: FlashAddress, size: PowerOf2Usize) -> Result<(), Self::Error> {
    self.driver.start_erase(start_addr, size)?;
    self.blocking.handle_notification(|_| self.driver.complete_op());
}

This way, handle_notification will wait for the notification, then call the closure in its argument, and finally ack the interrupt, no matter the return value from the closure. This comes at the cost of not adhering to the Blocking trait anymore.

I am not sure what will be the best "standard" way of handling interrupts that need non-trivial code to clear their status. This time, we were lucky that a single register write to the IP was sufficient.

@linux-foundation-easycla

linux-foundation-easycla Bot commented Aug 2, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: jesultra / name: Jes B. Klinke (ea33d2e)

In the blocking flash driver wait_for_notification(), clear the
interrupt status on the flash IP before acking the interrupt with the
PLIC.

Before this change, the PLIC would re-latch the interrupt immediately
after it being ack'ed, as the interrupt request was still active at
the flash IP.  As a result, next flash operation would erroneously be
considered done immediately.
@jesultra jesultra changed the title earlgrey: Avoid spurious completion on flash operations earlgrey: Properly ack flash operation done interrupt Aug 5, 2026
@jesultra
jesultra marked this pull request as ready for review August 5, 2026 11:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant