target/ast10x0/peripherals/sgpiom: fix interrupt configure ordering configure_interrupt enabled before programming sensitivity, creating a window where stale sensitivity codes could fire, and never cleared latched status. Fix: program sensitivity, clear pending, then enable. write_sens_bits now masks enabled pins during the 3-RMW sensitivity update to prevent transient spurious latches. Signed-off-by: Steven Lee <steven_lee@aspeedtech.com>
diff --git a/target/ast10x0/peripherals/sgpiom/hal_impl.rs b/target/ast10x0/peripherals/sgpiom/hal_impl.rs index 51e7c19..2667cff 100644 --- a/target/ast10x0/peripherals/sgpiom/hal_impl.rs +++ b/target/ast10x0/peripherals/sgpiom/hal_impl.rs
@@ -141,10 +141,6 @@ mask: Self::Mask, sensitivity: EdgeSensitivity, ) -> Result<(), Self::Error> { - // Validate up front so a mixed valid/invalid mask cannot partially - // program some pins before erroring. - self.dev.validate_mask(mask.0)?; - let (mode, trig) = match sensitivity { EdgeSensitivity::RisingEdge => (InterruptMode::Edge, InterruptTrigger::High), EdgeSensitivity::FallingEdge => (InterruptMode::Edge, InterruptTrigger::Low), @@ -153,14 +149,8 @@ EdgeSensitivity::LowLevel => (InterruptMode::Level, InterruptTrigger::Low), }; - let mut pins = mask.0; - while pins != 0 { - let pin = pins.trailing_zeros() as u8; - self.sgpiom - .configure_interrupt_sensitivity(&self.dev, pin, mode, trig)?; - pins &= pins - 1; - } - Ok(()) + self.sgpiom + .configure_interrupt_sensitivity_masked(&self.dev, mask.0, mode, trig) } /// Enable/disable/clear/query interrupts for the masked pins.
diff --git a/target/ast10x0/peripherals/sgpiom/register_block.rs b/target/ast10x0/peripherals/sgpiom/register_block.rs index a2732d0..79016c3 100644 --- a/target/ast10x0/peripherals/sgpiom/register_block.rs +++ b/target/ast10x0/peripherals/sgpiom/register_block.rs
@@ -212,26 +212,46 @@ Ok(Some(int_type)) } - /// Write the 3-bit sensitivity code for a single `bit` into the bank's - /// `int_sens_type[0..2]` registers, leaving other pins untouched. - fn write_sens_bits(&self, bank: Bank, bit: u32, int_type: u8) { - let mut s0 = self.int_sens_read(bank, 0) & !bit; - let mut s1 = self.int_sens_read(bank, 1) & !bit; - let mut s2 = self.int_sens_read(bank, 2) & !bit; + /// Write the 3-bit sensitivity code for every set bit in `mask` into the + /// bank's `int_sens_type[0..2]` registers, leaving other pins untouched. + /// + /// The three sensitivity registers can only be rewritten one at a time, so + /// a pin whose interrupt is enabled would pass through transient codes + /// while they are updated (e.g. level-high `011` -> falling-edge `000` + /// passes through level-low `010` after the first write) and could latch a + /// spurious interrupt. To prevent that, any currently enabled pins in + /// `mask` are disabled for the duration of the update; status latched + /// under the old or transient sensitivity is cleared before their enable + /// bits are restored. + fn write_sens_bits(&self, bank: Bank, mask: u32, int_type: u8) { + let en = self.int_en_read(bank); + let enabled = en & mask; + if enabled != 0 { + self.int_en_write(bank, en & !mask); + } + + let mut s0 = self.int_sens_read(bank, 0) & !mask; + let mut s1 = self.int_sens_read(bank, 1) & !mask; + let mut s2 = self.int_sens_read(bank, 2) & !mask; if (int_type & 0x1) != 0 { - s0 |= bit; + s0 |= mask; } if (int_type & 0x2) != 0 { - s1 |= bit; + s1 |= mask; } if (int_type & 0x4) != 0 { - s2 |= bit; + s2 |= mask; } self.int_sens_write(bank, 0, s0); self.int_sens_write(bank, 1, s1); self.int_sens_write(bank, 2, s2); + + if enabled != 0 { + self.clear_interrupt_status(bank, enabled); + self.int_en_write(bank, self.int_en_read(bank) | enabled); + } } /// Configure a pin's interrupt sensitivity *and* enable/disable bit. @@ -255,9 +275,13 @@ self.int_en_write(dev.bank, en); } Some(int_type) => { + // Program sensitivity while the pin is (still) disabled, then + // clear any status latched under the previous sensitivity so a + // stale event cannot fire the moment the enable bit is set. + self.write_sens_bits(dev.bank, bit, int_type); + self.clear_interrupt_status(dev.bank, bit); let en = self.int_en_read(dev.bank) | bit; self.int_en_write(dev.bank, en); - self.write_sens_bits(dev.bank, bit, int_type); } } @@ -275,11 +299,29 @@ trig: InterruptTrigger, ) -> Result<(), Error> { dev.validate_pin(pin)?; - let bit = 1u32 << pin; // Disabled has no sensitivity to program; treat as code 0 (falling edge) // which is inert while the enable bit stays clear. let int_type = Self::interrupt_sens_type(mode, trig)?.unwrap_or(0); - self.write_sens_bits(dev.bank, bit, int_type); + self.write_sens_bits(dev.bank, 1u32 << pin, int_type); + Ok(()) + } + + /// Program interrupt sensitivity for every pin in `mask` in one pass + /// (three register read-modify-writes total, instead of three per pin). + /// Like [`Self::configure_interrupt_sensitivity`], the enable bits are not + /// changed. + pub fn configure_interrupt_sensitivity_masked( + &self, + dev: &BankDevice, + mask: u32, + mode: InterruptMode, + trig: InterruptTrigger, + ) -> Result<(), Error> { + dev.validate_mask(mask)?; + let int_type = Self::interrupt_sens_type(mode, trig)?.unwrap_or(0); + if mask != 0 { + self.write_sens_bits(dev.bank, mask, int_type); + } Ok(()) }