use core::array;
use kernel::{
device,
io::{
poll::read_poll_timeout,
Io, },
prelude::*,
sync::aref::ARef,
time::{
delay::fsleep,
Delta, },
transmute::FromBytes, };
use crate::{
driver::Bar0,
falcon::{
gsp::Gsp,
sec2::Sec2,
Falcon, },
gsp::{
cmdq::{
Cmdq,
MessageFromGsp, },
fw,
},
num::FromSafeCast,
sbuffer::SBufferIter,
};
struct GspSequence {
cmd_index: u32,
cmd_data: KVec<u8>,
}
impl MessageFromGsp for GspSequence {
const FUNCTION: fw::MsgFunction = fw::MsgFunction::GspRunCpuSequencer;
type InitError = Error;
type Message = fw::RunCpuSequencer;
fn read(
msg: &Self::Message,
sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
) -> Result<Self, Self::InitError> {
let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?;
Ok(GspSequence {
cmd_index: msg.cmd_index(),
cmd_data,
})
}
}
const CMD_SIZE: usize = size_of::<fw::SequencerBufferCmd>();
#[allow(clippy::enum_variant_names)]
#[derive(Debug)]
pub(crate) enum GspSeqCmd {
RegWrite(fw::RegWritePayload),
RegModify(fw::RegModifyPayload),
RegPoll(fw::RegPollPayload),
DelayUs(fw::DelayUsPayload),
RegStore(fw::RegStorePayload),
CoreReset,
CoreStart,
CoreWaitForHalt,
CoreResume,
}
impl GspSeqCmd {
pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> {
let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?;
let opcode_size = core::mem::size_of::<u32>();
let (cmd, size) = match fw_cmd.opcode()? {
fw::SeqBufOpcode::RegWrite => {
let payload = fw_cmd.reg_write_payload()?;
let size = opcode_size + size_of_val(&payload);
(GspSeqCmd::RegWrite(payload), size)
}
fw::SeqBufOpcode::RegModify => {
let payload = fw_cmd.reg_modify_payload()?;
let size = opcode_size + size_of_val(&payload);
(GspSeqCmd::RegModify(payload), size)
}
fw::SeqBufOpcode::RegPoll => {
let payload = fw_cmd.reg_poll_payload()?;
let size = opcode_size + size_of_val(&payload);
(GspSeqCmd::RegPoll(payload), size)
}
fw::SeqBufOpcode::DelayUs => {
let payload = fw_cmd.delay_us_payload()?;
let size = opcode_size + size_of_val(&payload);
(GspSeqCmd::DelayUs(payload), size)
}
fw::SeqBufOpcode::RegStore => {
let payload = fw_cmd.reg_store_payload()?;
let size = opcode_size + size_of_val(&payload);
(GspSeqCmd::RegStore(payload), size)
}
fw::SeqBufOpcode::CoreReset => (GspSeqCmd::CoreReset, opcode_size),
fw::SeqBufOpcode::CoreStart => (GspSeqCmd::CoreStart, opcode_size),
fw::SeqBufOpcode::CoreWaitForHalt => (GspSeqCmd::CoreWaitForHalt, opcode_size),
fw::SeqBufOpcode::CoreResume => (GspSeqCmd::CoreResume, opcode_size),
};
if data.len() < size {
dev_err!(dev, "Data is not enough for command\n");
return Err(EINVAL);
}
Ok((cmd, size))
}
}
pub(crate) struct GspSequencer<'a> {
seq_info: GspSequence,
bar: &'a Bar0,
sec2_falcon: &'a Falcon<Sec2>,
gsp_falcon: &'a Falcon<Gsp>,
libos_dma_handle: u64,
bootloader_app_version: u32,
dev: ARef<device::Device>,
}
impl fw::RegWritePayload {
fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
let addr = usize::from_safe_cast(self.addr());
sequencer.bar.try_write32(self.val(), addr)
}
}
impl fw::RegModifyPayload {
fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
let addr = usize::from_safe_cast(self.addr());
sequencer.bar.try_read32(addr).and_then(|val| {
sequencer
.bar
.try_write32((val & !self.mask()) | self.val(), addr)
})
}
}
impl fw::RegPollPayload {
fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
let addr = usize::from_safe_cast(self.addr());
let timeout_us = if self.timeout() == 0 {
4_000_000
} else {
i64::from(self.timeout())
};
sequencer.bar.try_read32(addr)?;
read_poll_timeout(
|| sequencer.bar.try_read32(addr),
|current| (current & self.mask()) == self.val(),
Delta::ZERO,
Delta::from_micros(timeout_us),
)
.map(|_| ())
}
}
impl fw::DelayUsPayload {
fn run(&self, _sequencer: &GspSequencer<'_>) -> Result {
fsleep(Delta::from_micros(i64::from(self.val())));
Ok(())
}
}
impl fw::RegStorePayload {
fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
let addr = usize::from_safe_cast(self.addr());
sequencer.bar.try_read32(addr).map(|_| ())
}
}
impl GspSeqCmd {
fn run(&self, seq: &GspSequencer<'_>) -> Result {
match self {
GspSeqCmd::RegWrite(cmd) => cmd.run(seq),
GspSeqCmd::RegModify(cmd) => cmd.run(seq),
GspSeqCmd::RegPoll(cmd) => cmd.run(seq),
GspSeqCmd::DelayUs(cmd) => cmd.run(seq),
GspSeqCmd::RegStore(cmd) => cmd.run(seq),
GspSeqCmd::CoreReset => {
seq.gsp_falcon.reset(seq.bar)?;
seq.gsp_falcon.dma_reset(seq.bar);
Ok(())
}
GspSeqCmd::CoreStart => {
seq.gsp_falcon.start(seq.bar)?;
Ok(())
}
GspSeqCmd::CoreWaitForHalt => {
seq.gsp_falcon.wait_till_halted(seq.bar)?;
Ok(())
}
GspSeqCmd::CoreResume => {
seq.gsp_falcon.reset(seq.bar)?;
seq.gsp_falcon.write_mailboxes(
seq.bar,
Some(seq.libos_dma_handle as u32),
Some((seq.libos_dma_handle >> 32) as u32),
);
seq.sec2_falcon.start(seq.bar)?;
seq.gsp_falcon
.check_reload_completed(seq.bar, Delta::from_secs(2))?;
let mbox0 = seq.sec2_falcon.read_mailbox0(seq.bar);
if mbox0 != 0 {
dev_err!(seq.dev, "Sequencer: sec2 errors: {:?}\n", mbox0);
return Err(EIO);
}
seq.gsp_falcon
.write_os_version(seq.bar, seq.bootloader_app_version);
if !seq.gsp_falcon.is_riscv_active(seq.bar) {
dev_err!(seq.dev, "Sequencer: RISC-V core is not active\n");
return Err(EIO);
}
Ok(())
}
}
}
}
pub(crate) struct GspSeqIter<'a> {
cmd_data: &'a [u8],
current_offset: usize,
total_cmds: u32,
cmds_processed: u32,
dev: ARef<device::Device>,
}
impl<'a> Iterator for GspSeqIter<'a> {
type Item = Result<GspSeqCmd>;
fn next(&mut self) -> Option<Self::Item> {
if self.cmds_processed >= self.total_cmds || self.current_offset >= self.cmd_data.len() {
return None;
}
if self.current_offset + core::mem::size_of::<u32>() > self.cmd_data.len() {
return Some(Err(EIO));
}
let offset = self.current_offset;
let mut buffer = [0u8; CMD_SIZE];
let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() {
CMD_SIZE
} else {
self.cmd_data.len() - offset
};
buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset + copy_len]);
let cmd_result = GspSeqCmd::new(&buffer, &self.dev);
cmd_result.map_or_else(
|_err| {
dev_err!(self.dev, "Error parsing command at offset {}\n", offset);
None
},
|(cmd, size)| {
self.current_offset += size;
self.cmds_processed += 1;
Some(Ok(cmd))
},
)
}
}
impl<'a> GspSequencer<'a> {
fn iter(&self) -> GspSeqIter<'_> {
let cmd_data = &self.seq_info.cmd_data[..];
GspSeqIter {
cmd_data,
current_offset: 0,
total_cmds: self.seq_info.cmd_index,
cmds_processed: 0,
dev: self.dev.clone(),
}
}
}
pub(crate) struct GspSequencerParams<'a> {
pub(crate) bootloader_app_version: u32,
pub(crate) libos_dma_handle: u64,
pub(crate) gsp_falcon: &'a Falcon<Gsp>,
pub(crate) sec2_falcon: &'a Falcon<Sec2>,
pub(crate) dev: ARef<device::Device>,
pub(crate) bar: &'a Bar0,
}
impl<'a> GspSequencer<'a> {
pub(crate) fn run(cmdq: &Cmdq, params: GspSequencerParams<'a>) -> Result {
let seq_info = loop {
match cmdq.receive_msg::<GspSequence>(Cmdq::RECEIVE_TIMEOUT) {
Ok(seq_info) => break seq_info,
Err(ERANGE) => continue,
Err(e) => return Err(e),
}
};
let sequencer = GspSequencer {
seq_info,
bar: params.bar,
sec2_falcon: params.sec2_falcon,
gsp_falcon: params.gsp_falcon,
libos_dma_handle: params.libos_dma_handle,
bootloader_app_version: params.bootloader_app_version,
dev: params.dev,
};
dev_dbg!(sequencer.dev, "Running CPU Sequencer commands\n");
for cmd_result in sequencer.iter() {
match cmd_result {
Ok(cmd) => cmd.run(&sequencer)?,
Err(e) => {
dev_err!(
sequencer.dev,
"Error running command at index {}\n",
sequencer.seq_info.cmd_index
);
return Err(e);
}
}
}
dev_dbg!(
sequencer.dev,
"CPU Sequencer commands completed successfully\n"
);
Ok(())
}
}