#![cfg(all(target_arch = "riscv32", feature = "p4-time-driver-irq"))]
use core::cell::RefCell;
use core::sync::atomic::{AtomicU32, Ordering};
use core::task::Waker;
use critical_section::Mutex;
use embassy_time_driver::Driver;
use embassy_time_queue_utils::Queue;
pub static EMAC_IRQS: AtomicU32 = AtomicU32::new(0);
pub static EMAC_IRQ_RX: AtomicU32 = AtomicU32::new(0);
pub static EMAC_IRQ_TX: AtomicU32 = AtomicU32::new(0);
use crate::time_driver_irq_logic::{
is_async_irq, mcause_code, CPU_INT_LINE_EMAC, CPU_INT_LINE_SYSTIMER, EMAC_SBD_CLIC_INDEX,
SYSTIMER_TARGET0_CLIC_INDEX,
};
use crate::{clic, dma::Dma, regs, systimer};
struct P4TimeDriver {
queue: Mutex<RefCell<Queue>>,
}
impl Driver for P4TimeDriver {
fn now(&self) -> u64 {
systimer::now_us()
}
fn schedule_wake(&self, at: u64, waker: &Waker) {
critical_section::with(|cs| {
let mut q = self.queue.borrow(cs).borrow_mut();
q.schedule_wake(at, waker);
arm_next_locked(&mut q);
});
}
}
embassy_time_driver::time_driver_impl!(
static DRIVER: P4TimeDriver = P4TimeDriver {
queue: Mutex::new(RefCell::new(Queue::new())),
}
);
fn arm_next_locked(q: &mut Queue) {
let now_us = systimer::now_us();
let next_us = q.next_expiration(now_us);
if next_us == u64::MAX {
systimer::disarm_alarm0();
return;
}
let target_ticks = systimer::us_to_ticks(next_us);
systimer::arm_alarm0(target_ticks);
}
pub fn init() {
systimer::init_alarm0();
clic::route_systimer_target0(SYSTIMER_TARGET0_CLIC_INDEX);
clic::set_threshold(0);
clic::enable_cpu_int(CPU_INT_LINE_SYSTIMER, 1);
extern "C" {
fn _p4_eth_trap_entry();
}
let addr = _p4_eth_trap_entry as *const () as usize;
unsafe {
core::arch::asm!("csrw mtvec, {}", in(reg) addr);
let mie_meie: usize = 1 << 11;
core::arch::asm!("csrs mie, {}", in(reg) mie_meie);
let mstatus_mie: usize = 1 << 3;
core::arch::asm!("csrs mstatus, {}", in(reg) mstatus_mie);
}
}
pub fn enable_emac_irq() {
let inten = regs::bits::dmainten::TIE
| regs::bits::dmainten::RIE
| regs::bits::dmainten::NIE;
regs::write(regs::dma::INT_EN, inten);
Dma::clear_interrupt_status(Dma::read_interrupt_status());
clic::route_emac_sbd(EMAC_SBD_CLIC_INDEX);
clic::enable_cpu_int(CPU_INT_LINE_EMAC, 1);
}
core::arch::global_asm!(
r#"
.section .trap.start, "ax"
.global _p4_eth_trap_entry
.balign 256
_p4_eth_trap_entry:
addi sp, sp, -80
sw ra, 0(sp)
sw t0, 4(sp)
sw t1, 8(sp)
sw t2, 12(sp)
sw t3, 16(sp)
sw t4, 20(sp)
sw t5, 24(sp)
sw t6, 28(sp)
sw a0, 32(sp)
sw a1, 36(sp)
sw a2, 40(sp)
sw a3, 44(sp)
sw a4, 48(sp)
sw a5, 52(sp)
sw a6, 56(sp)
sw a7, 60(sp)
csrr t0, mepc
sw t0, 64(sp)
csrr t0, mcause
sw t0, 68(sp)
csrr a0, mcause
call rust_trap_dispatch
lw t0, 64(sp)
csrw mepc, t0
lw ra, 0(sp)
lw t0, 4(sp)
lw t1, 8(sp)
lw t2, 12(sp)
lw t3, 16(sp)
lw t4, 20(sp)
lw t5, 24(sp)
lw t6, 28(sp)
lw a0, 32(sp)
lw a1, 36(sp)
lw a2, 40(sp)
lw a3, 44(sp)
lw a4, 48(sp)
lw a5, 52(sp)
lw a6, 56(sp)
lw a7, 60(sp)
addi sp, sp, 80
mret
"#
);
#[no_mangle]
extern "C" fn rust_trap_dispatch(mcause: u32) {
if !is_async_irq(mcause) {
return;
}
let code = mcause_code(mcause);
match code {
x if x == SYSTIMER_TARGET0_CLIC_INDEX => {
systimer::clear_alarm0();
clic::clear_pending(CPU_INT_LINE_SYSTIMER);
systimer::disarm_alarm0();
critical_section::with(|cs| {
let mut q = DRIVER.queue.borrow(cs).borrow_mut();
arm_next_locked(&mut q);
});
}
x if x == EMAC_SBD_CLIC_INDEX => {
let status = Dma::read_interrupt_status();
Dma::clear_interrupt_status(status);
clic::clear_pending(CPU_INT_LINE_EMAC);
EMAC_IRQS.fetch_add(1, Ordering::Relaxed);
if status.has_rx_interrupt() {
EMAC_IRQ_RX.fetch_add(1, Ordering::Relaxed);
crate::wake_rx_task();
}
if status.has_tx_interrupt() {
EMAC_IRQ_TX.fetch_add(1, Ordering::Relaxed);
crate::wake_tx_task();
}
}
_ => {
clic::disable_cpu_int(CPU_INT_LINE_SYSTIMER);
clic::disable_cpu_int(CPU_INT_LINE_EMAC);
}
}
}