use alloc::collections::VecDeque;
use core::{
cell::Cell,
sync::atomic::{AtomicU32, Ordering},
};
use ax_kspin::SpinNoIrq as Mutex;
use super::{
TDMA_PHYS_BASE, TIU_PHYS_BASE, error::TpuError, platform::TpuRuntimeState, tdma::TdmaRegs,
tiu::TiuRegs,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TpuState {
Uninitialized,
Idle,
Running,
Suspended,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TpuSubmitPath {
DesNormal = 0,
}
#[derive(Debug)]
pub struct TpuTaskNode {
pub pid: u32,
pub seq_no: u32,
pub dmabuf_fd: i32,
pub dmabuf_vaddr: usize,
pub dmabuf_paddr: u64,
pub tpu_path: TpuSubmitPath,
pub ret: i32,
}
#[derive(Default)]
pub struct TpuKernelWork {
pub task_list: VecDeque<TpuTaskNode>,
pub done_list: VecDeque<TpuTaskNode>,
}
struct TpuDeviceInner {
tdma: TdmaRegs,
tiu: TiuRegs,
state: TpuState,
runtime: TpuRuntimeState,
kernel_work: TpuKernelWork,
}
pub struct Sg2002Tpu {
inner: Mutex<TpuDeviceInner>,
seq_counter: AtomicU32,
}
impl Sg2002Tpu {
pub unsafe fn new() -> Self {
let virt_offset = 0xffff_ffc0_0000_0000u64 as isize;
let tdma_vaddr = (TDMA_PHYS_BASE as isize + virt_offset) as *mut u8;
let tiu_vaddr = (TIU_PHYS_BASE as isize + virt_offset) as *mut u8;
unsafe { Self::from_vaddr(tdma_vaddr, tiu_vaddr) }
}
pub unsafe fn from_vaddr(tdma_vaddr: *mut u8, tiu_vaddr: *mut u8) -> Self {
Self {
inner: Mutex::new(TpuDeviceInner {
tdma: unsafe { TdmaRegs::new(tdma_vaddr) },
tiu: unsafe { TiuRegs::new(tiu_vaddr) },
state: TpuState::Uninitialized,
runtime: TpuRuntimeState::default(),
kernel_work: TpuKernelWork::default(),
}),
seq_counter: AtomicU32::new(0),
}
}
pub fn init(&self) -> Result<(), TpuError> {
let mut inner = self.inner.lock();
super::platform::resync_cmd_id(&inner.tdma, &inner.tiu);
inner.state = TpuState::Idle;
inner.runtime = TpuRuntimeState::default();
info!("TPU device initialized");
Ok(())
}
pub fn state(&self) -> TpuState {
self.inner.lock().state
}
pub fn is_ready(&self) -> bool {
self.inner.lock().state == TpuState::Idle
}
pub fn handle_irq(&self) -> bool {
let mut inner = self.inner.lock();
let tdma = &inner.tdma as *const TdmaRegs;
let tiu = &inner.tiu as *const TiuRegs;
let runtime = &mut inner.runtime;
unsafe { super::platform::handle_tdma_irq(&*tdma, &*tiu, runtime) }
}
pub fn next_seq_no(&self) -> u32 {
self.seq_counter.fetch_add(1, Ordering::SeqCst)
}
pub fn submit_dmabuf(
&self,
fd: i32,
seq_no: u32,
dmabuf_vaddr: usize,
dmabuf_paddr: u64,
) -> Result<(), TpuError> {
debug!("[TPU] submit dmabuf: fd={}, seq_no={}", fd, seq_no);
debug!(
"[TPU] Buffer: vaddr=0x{:x}, paddr=0x{:x}",
dmabuf_vaddr, dmabuf_paddr
);
let task = TpuTaskNode {
pid: 0, seq_no,
dmabuf_fd: fd,
dmabuf_vaddr,
dmabuf_paddr,
tpu_path: TpuSubmitPath::DesNormal,
ret: 0,
};
let mut inner = self.inner.lock();
inner.kernel_work.task_list.push_back(task);
self.process_task_locked(&mut inner)?;
Ok(())
}
fn process_task_locked(&self, inner: &mut TpuDeviceInner) -> Result<(), TpuError> {
while let Some(mut task) = inner.kernel_work.task_list.pop_front() {
super::platform::resync_cmd_id(&inner.tdma, &inner.tiu);
inner.runtime.irq_received = false;
let result =
self.run_dmabuf_internal(inner, task.dmabuf_vaddr as *const u8, task.dmabuf_paddr);
task.ret = match result {
Ok(_) => 0,
Err(e) => {
error!("TPU run dmabuf failed: {:?}", e);
-1
}
};
inner.kernel_work.done_list.push_back(task);
}
Ok(())
}
fn run_dmabuf_internal(
&self,
inner: &mut TpuDeviceInner,
dmabuf_vaddr: *const u8,
dmabuf_paddr: u64,
) -> Result<(), TpuError> {
if inner.state != TpuState::Idle && inner.state != TpuState::Uninitialized {
return Err(TpuError::NotInitialized);
}
inner.state = TpuState::Running;
let timeout_counter = Cell::new(0u64);
let timeout_limit = 1_000_000_000u64;
let wait_irq = || -> Result<(), TpuError> {
let mut counter = timeout_counter.get();
while counter < timeout_limit {
counter += 1;
timeout_counter.set(counter);
core::hint::spin_loop();
if counter > 10000 {
break;
}
}
if counter >= timeout_limit {
return Err(TpuError::Timeout);
}
Ok(())
};
let timeout_checker = || -> bool { timeout_counter.get() > timeout_limit };
let tdma = &inner.tdma as *const TdmaRegs;
let tiu = &inner.tiu as *const TiuRegs;
let runtime = &mut inner.runtime;
let result = unsafe {
super::platform::run_dmabuf(
&*tdma,
&*tiu,
dmabuf_vaddr,
dmabuf_paddr,
runtime,
wait_irq,
timeout_checker,
)
};
inner.state = TpuState::Idle;
result
}
pub fn wait_dmabuf(&self, seq_no: u32) -> Result<i32, TpuError> {
debug!("TPU wait dmabuf: seq_no={}", seq_no);
let mut inner = self.inner.lock();
let mut found_idx = None;
for (idx, task) in inner.kernel_work.done_list.iter().enumerate() {
if task.seq_no == seq_no {
found_idx = Some(idx);
break;
}
}
if let Some(idx) = found_idx {
let task = inner.kernel_work.done_list.remove(idx).unwrap();
debug!(
"TPU wait dmabuf completed: seq_no={}, ret={}",
seq_no, task.ret
);
Ok(task.ret)
} else {
warn!("TPU wait dmabuf: seq_no {} not found", seq_no);
Err(TpuError::NotInitialized)
}
}
pub fn cache_flush_paddr(&self, paddr: u64, size: u64) -> Result<(), TpuError> {
debug!("TPU cache flush: paddr=0x{:x}, size={}", paddr, size);
#[cfg(target_arch = "riscv64")]
{
unsafe {
core::arch::asm!("fence iorw, iorw");
}
}
let _ = (paddr, size);
Ok(())
}
pub fn cache_invalidate_paddr(&self, paddr: u64, size: u64) -> Result<(), TpuError> {
debug!("TPU cache invalidate: paddr=0x{:x}, size={}", paddr, size);
#[cfg(target_arch = "riscv64")]
{
unsafe {
core::arch::asm!("fence iorw, iorw");
}
}
let _ = (paddr, size);
Ok(())
}
pub fn suspend(&self) -> Result<(), TpuError> {
let mut inner = self.inner.lock();
if inner.state == TpuState::Suspended {
return Ok(());
}
let tdma = &inner.tdma as *const TdmaRegs;
let tiu = &inner.tiu as *const TiuRegs;
let reg_backup = &mut inner.runtime.reg_backup;
unsafe {
super::platform::backup_registers(&*tdma, &*tiu, reg_backup);
}
inner.state = TpuState::Suspended;
info!("TPU suspended");
Ok(())
}
pub fn resume(&self) -> Result<(), TpuError> {
let mut inner = self.inner.lock();
if inner.state != TpuState::Suspended {
return Err(TpuError::NotInitialized);
}
let tdma = &inner.tdma as *const TdmaRegs;
let tiu = &inner.tiu as *const TiuRegs;
let reg_backup = &inner.runtime.reg_backup;
unsafe {
super::platform::restore_registers(&*tdma, &*tiu, reg_backup);
}
inner.state = TpuState::Idle;
info!("TPU resumed");
Ok(())
}
pub fn reset(&self) {
let mut inner = self.inner.lock();
super::platform::resync_cmd_id(&inner.tdma, &inner.tiu);
inner.runtime = TpuRuntimeState::default();
inner.state = TpuState::Idle;
info!("TPU reset");
}
}
unsafe impl Send for Sg2002Tpu {}
unsafe impl Sync for Sg2002Tpu {}