Skip to content

Commit 41e4327

Browse files
committed
Introduce cosmo sequencer interrupts
Instead of polling for our state changes, rely on the sequencer to deliver interrupts to us
1 parent 944de1e commit 41e4327

6 files changed

Lines changed: 190 additions & 33 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/cosmo/base.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ port = "F"
7474
pin = 2
7575
owner = {name = "sprot", notification = "rot_irq"}
7676

77+
[tasks.sys.config.gpio-irqs.seq_irq]
78+
# FPGA1_TO_SP_IRQ1_L
79+
port = "F"
80+
pin = 5
81+
owner = {name = "cosmo_seq", notification = "seq_irq"}
82+
7783
[tasks.spi2_driver]
7884
name = "drv-stm32h7-spi-server"
7985
priority = 3
@@ -175,7 +181,7 @@ stacksize = 2600
175181
start = true
176182
task-slots = ["sys", "i2c_driver", {spi_front = "spi3_driver"}, "jefe", "packrat", "auxflash", "spartan7_loader", "hf"]
177183
uses = ["mmio_sequencer", "mmio_info"]
178-
notifications = ["timer", "vcore"]
184+
notifications = ["timer", "vcore", "seq-irq"]
179185

180186
[tasks.ignition_flash]
181187
name = "drv-ignition-flash"

drv/cosmo-seq-server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ zerocopy-derive = { workspace = true }
2828
[build-dependencies]
2929
build-fpga-regmap = { path = "../../build/fpga-regmap" }
3030
build-util = { path = "../../build/util" }
31+
build-stm32xx-sys = { path = "../../build/stm32xx-sys" }
3132
idol = { workspace = true }
3233

3334
[features]

drv/cosmo-seq-server/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{fs, io::Write};
77
fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
88
build_util::expose_target_board();
99
build_util::build_notifications()?;
10+
build_stm32xx_sys::build_gpio_irq_pins()?;
1011

1112
let out_dir = build_util::out_dir();
1213
let out_file = out_dir.join("cosmo_fpga.rs");

drv/cosmo-seq-server/src/main.rs

Lines changed: 174 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ enum Trace {
7070
our_state: PowerState,
7171
seq_state: Result<fmc_periph::A0Sm, u8>,
7272
},
73+
SequencerInterrupt {
74+
our_state: PowerState,
75+
seq_state: Result<fmc_periph::A0Sm, u8>,
76+
ifr: fmc_periph::IfrView,
77+
},
7378
PowerDownError(drv_cpu_seq_api::SeqError),
7479
Coretype {
7580
coretype0: bool,
@@ -85,6 +90,9 @@ enum Trace {
8590
pwrokn: u8,
8691
},
8792
Thermtrip,
93+
A0MapoInterrupt,
94+
SmerrInterrupt,
95+
UnexpectedInterrupt,
8896
}
8997
counted_ringbuf!(Trace, 128, Trace::None);
9098

@@ -133,6 +141,8 @@ const SP5R2_PULL: sys_api::Pull = sys_api::Pull::None;
133141
const SP5R3_PULL: sys_api::Pull = sys_api::Pull::None;
134142
const SP5R4_PULL: sys_api::Pull = sys_api::Pull::None;
135143

144+
use gpio_irq_pins::SEQ_IRQ;
145+
136146
////////////////////////////////////////////////////////////////////////////////
137147

138148
/// Helper type which includes both sequencer and NIC state machine states
@@ -223,6 +233,10 @@ fn init() -> Result<ServerImpl, SeqError> {
223233
sys.gpio_configure_input(SP5_TO_SP_SP5R3, SP5R3_PULL);
224234
sys.gpio_configure_input(SP5_TO_SP_SP5R4, SP5R4_PULL);
225235

236+
// Sequencer interrupt
237+
sys.gpio_configure_input(SEQ_IRQ, sys_api::Pull::None);
238+
sys.gpio_irq_configure(notifications::SEQ_IRQ_MASK, sys_api::Edge::Falling);
239+
226240
let spi_front = drv_spi_api::Spi::from(SPI_FRONT.get_task_id());
227241
let aux = drv_auxflash_api::AuxFlash::from(AUXFLASH.get_task_id());
228242

@@ -390,6 +404,7 @@ impl ServerImpl {
390404
fn log_state_registers(&self) -> StateMachineStates {
391405
let seq_api_status = (&self.seq.seq_api_status).into();
392406
let nic_api_status = (&self.seq.nic_api_status).into();
407+
393408
ringbuf_entry!(Trace::RegStateValues {
394409
seq_api_status,
395410
seq_raw_status: (&self.seq.seq_raw_status).into(),
@@ -513,6 +528,7 @@ impl ServerImpl {
513528
}
514529
};
515530

531+
self.enable_sequencer_interrupts();
516532
// Flip the host flash mux so the CPU can read from it
517533
// (this is secretly infallible on Cosmo, so we can unwrap it)
518534
self.hf.set_mux(drv_hf_api::HfMuxState::HostCPU).unwrap();
@@ -521,6 +537,8 @@ impl ServerImpl {
521537
| (PowerState::A0PlusHP, PowerState::A2)
522538
| (PowerState::A0Thermtrip, PowerState::A2)
523539
| (PowerState::A0Reset, PowerState::A2) => {
540+
// Disable our interrupts before we shutdown
541+
self.disable_sequencer_interrupts();
524542
self.seq.power_ctrl.modify(|m| m.set_a0_en(false));
525543
let mut okay = false;
526544
for _ in 0..200 {
@@ -543,7 +561,6 @@ impl ServerImpl {
543561
// We can't do much else here, since we already cleared the
544562
// a0_en flag to disable the sequencer.
545563
}
546-
547564
// Flip the host flash mux so the SP can read from it
548565
// (this is secretly infallible on Cosmo, so we can unwrap it)
549566
self.hf.set_mux(drv_hf_api::HfMuxState::SP).unwrap();
@@ -571,13 +588,14 @@ impl ServerImpl {
571588

572589
/// Returns the current timer interval, in milliseconds
573590
///
574-
/// If we are in `A0`, then we are waiting for the NIC to come up; if we are
575-
/// in `A0PlusHP`, we're polling for a thermtrip or for someone disabling
576-
/// the NIC. In other states, there's no need to poll.
591+
/// If we are in `A0`, then we are waiting for the NIC to come up;
592+
/// Once we are in `A0PlusHP` we rely on sequencer interrupts for
593+
/// all our state transitions. We still want to catch an unexpected
594+
/// case of sequencer failure so poll for that case specifically.
577595
fn poll_interval(&self) -> Option<u32> {
578596
match self.state {
579597
PowerState::A0 => Some(10),
580-
PowerState::A0PlusHP => Some(100),
598+
PowerState::A0PlusHP => Some(1000),
581599
_ => None,
582600
}
583601
}
@@ -588,6 +606,150 @@ impl ServerImpl {
588606
set_timer_relative(interval, notifications::TIMER_MASK);
589607
}
590608
}
609+
610+
/// Powers down to A2, if that fails for some reason just
611+
/// log an error
612+
fn emergency_a2(&mut self, reason: StateChangeReason) {
613+
// Power down to A2, updating our internal state. We can't
614+
// handle errors here, so log them and continue.
615+
if let Err(e) = self.set_state_impl(PowerState::A2, reason) {
616+
ringbuf_entry!(Trace::PowerDownError(e))
617+
}
618+
}
619+
620+
fn enable_sequencer_interrupts(&mut self) {
621+
// Clear `ifr` in case spurious flags accumulated while disabled
622+
self.seq.ifr.modify(|m| {
623+
m.set_fanfault(false);
624+
m.set_thermtrip(false);
625+
m.set_smerr_assert(false);
626+
m.set_a0mapo(false);
627+
m.set_nicmapo(false);
628+
m.set_amd_pwrok_fedge(false);
629+
m.set_amd_rstn_fedge(false);
630+
});
631+
632+
let _ = self.sys.gpio_irq_control(
633+
notifications::SEQ_IRQ_MASK,
634+
sys_api::IrqControl::Enable,
635+
);
636+
self.seq.ier.modify(|m| {
637+
m.set_fanfault(true);
638+
m.set_thermtrip(true);
639+
m.set_smerr_assert(true);
640+
m.set_a0mapo(true);
641+
m.set_nicmapo(true);
642+
m.set_amd_pwrok_fedge(true);
643+
m.set_amd_rstn_fedge(true);
644+
});
645+
}
646+
647+
fn disable_sequencer_interrupts(&mut self) {
648+
self.seq.ier.modify(|m| {
649+
m.set_fanfault(false);
650+
m.set_thermtrip(false);
651+
m.set_smerr_assert(false);
652+
m.set_a0mapo(false);
653+
m.set_nicmapo(false);
654+
m.set_amd_pwrok_fedge(false);
655+
m.set_amd_rstn_fedge(false);
656+
});
657+
let _ = self.sys.gpio_irq_control(
658+
notifications::SEQ_IRQ_MASK,
659+
sys_api::IrqControl::Disable,
660+
);
661+
}
662+
663+
fn handle_sequencer_interrupt(&mut self) {
664+
let ifr = self.seq.ifr.view();
665+
666+
let state = self.log_state_registers();
667+
ringbuf_entry!(Trace::SequencerInterrupt {
668+
our_state: self.state,
669+
seq_state: state.seq,
670+
ifr,
671+
});
672+
673+
enum InternalAction {
674+
Reset,
675+
ThermTrip,
676+
Smerr,
677+
Mapo,
678+
None,
679+
}
680+
681+
// We check these in lowest to highest priority. We start with
682+
// reset since we expect the CPU to handle that nicely.
683+
// Thermal trip is a terminal state in that we log it but don't
684+
// actually make any changes to the sequencer.
685+
// SMERR is treated as a higher priority than MAPO arbitrarily.
686+
// we probably(?) won't see multiple of these set at a time but
687+
// it's important to account for that case;
688+
689+
let mut action = InternalAction::None;
690+
691+
if ifr.amd_pwrok_fedge || ifr.amd_rstn_fedge {
692+
let rstn = self.seq.amd_reset_fedges.counts();
693+
let pwrokn = self.seq.amd_pwrok_fedges.counts();
694+
695+
// counters and ifr are cleared in the A2 -> A0 transition
696+
// host_sp_comms will be notified of this change and will
697+
// call back into this task to reboot the system (going to
698+
// A2 then back into A0)
699+
ringbuf_entry!(Trace::ResetCounts { rstn, pwrokn });
700+
action = InternalAction::Reset;
701+
}
702+
703+
if ifr.thermtrip {
704+
self.seq.ifr.modify(|h| h.set_thermtrip(false));
705+
ringbuf_entry!(Trace::Thermtrip);
706+
action = InternalAction::ThermTrip;
707+
// Great place for an ereport?
708+
}
709+
710+
if ifr.a0mapo {
711+
self.log_pg_registers();
712+
self.seq.ifr.modify(|h| h.set_a0mapo(false));
713+
ringbuf_entry!(Trace::A0MapoInterrupt);
714+
action = InternalAction::Mapo;
715+
// Great place for an ereport?
716+
}
717+
718+
if ifr.smerr_assert {
719+
self.seq.ifr.modify(|h| h.set_smerr_assert(false));
720+
ringbuf_entry!(Trace::SmerrInterrupt);
721+
action = InternalAction::Smerr;
722+
// Great place for an ereport?
723+
}
724+
// Fan Fault is unconnected
725+
// NIC MAPO is unconnected
726+
727+
match action {
728+
InternalAction::Reset => {
729+
// host_sp_comms will be notified of this change and will
730+
// call back into this task to reboot the system (going to
731+
// A2 then back into A0)
732+
self.set_state_internal(PowerState::A0Reset);
733+
}
734+
InternalAction::ThermTrip => {
735+
// This is a terminal state; we set our state to `A0Thermtrip`
736+
// but do not expect any other task to take action right now
737+
self.set_state_internal(PowerState::A0Thermtrip);
738+
}
739+
InternalAction::Mapo => {
740+
// This is a terminal state (for now)
741+
self.emergency_a2(StateChangeReason::A0Mapo);
742+
}
743+
InternalAction::Smerr => {
744+
// This is a terminal state (for now)
745+
self.emergency_a2(StateChangeReason::SmerrAssert);
746+
}
747+
InternalAction::None => {
748+
// This is unexpected, logging is the best we can do
749+
ringbuf_entry!(Trace::UnexpectedInterrupt);
750+
}
751+
};
752+
}
591753
}
592754

593755
impl idl::InOrderSequencerImpl for ServerImpl {
@@ -641,10 +803,14 @@ impl idl::InOrderSequencerImpl for ServerImpl {
641803

642804
impl NotificationHandler for ServerImpl {
643805
fn current_notification_mask(&self) -> u32 {
644-
notifications::TIMER_MASK
806+
notifications::TIMER_MASK | notifications::SEQ_IRQ_MASK
645807
}
646808

647809
fn handle_notification(&mut self, bits: u32) {
810+
if (bits & notifications::SEQ_IRQ_MASK) != 0 {
811+
self.handle_sequencer_interrupt();
812+
}
813+
648814
if (bits & notifications::TIMER_MASK) == 0 {
649815
return;
650816
}
@@ -672,32 +838,7 @@ impl NotificationHandler for ServerImpl {
672838
});
673839
self.log_pg_registers();
674840

675-
// Power down to A2, updating our internal state. We can't
676-
// handle errors here, so log them and continue.
677-
if let Err(e) = self
678-
.set_state_impl(PowerState::A2, StateChangeReason::Other)
679-
{
680-
ringbuf_entry!(Trace::PowerDownError(e))
681-
}
682-
} else {
683-
// Check that the FPGA has not logged any reset conditions from
684-
// the CPU.
685-
let ifr = self.seq.ifr.view();
686-
if ifr.thermtrip {
687-
self.seq.ifr.modify(|h| h.set_thermtrip(false));
688-
ringbuf_entry!(Trace::Thermtrip);
689-
self.set_state_internal(PowerState::A0Thermtrip)
690-
// this is a terminal state (for now)
691-
} else if ifr.amd_pwrok_fedge || ifr.amd_rstn_fedge {
692-
let rstn = self.seq.amd_reset_fedges.counts();
693-
let pwrokn = self.seq.amd_pwrok_fedges.counts();
694-
ringbuf_entry!(Trace::ResetCounts { rstn, pwrokn });
695-
// counters are cleared in the A2 -> A0 transition
696-
self.set_state_internal(PowerState::A0Reset);
697-
// host_sp_comms will be notified of this change and will
698-
// call back into this task to reboot the system (going to
699-
// A2 then back into A0)
700-
}
841+
self.emergency_a2(StateChangeReason::Unknown);
701842
}
702843
}
703844

@@ -721,3 +862,4 @@ mod fmc_periph {
721862
}
722863

723864
include!(concat!(env!("OUT_DIR"), "/notifications.rs"));
865+
include!(concat!(env!("OUT_DIR"), "/gpio_irq_pins.rs"));

drv/cpu-seq-api/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ pub enum StateChangeReason {
7171
HostReboot,
7272
/// The system powered off because a component has overheated.
7373
Overheat,
74+
/// A0 MAPO fault from the sequencer
75+
A0Mapo,
76+
/// System Management Error
77+
SmerrAssert,
78+
/// The system powered off for reasons we can't explain
79+
Unknown,
7480
}
7581

7682
/// Indicates the result of a power state transition.

0 commit comments

Comments
 (0)