use ax_kspin::SpinNoIrq as Mutex;
pub const CSR_GSTAT: u16 = 0x50;
pub const CSR_EENTRY: u16 = 0x0c;
pub const CSR_GCTL: u16 = 0x51;
pub const CSR_GTLBC: u16 = 0x15;
pub const CSR_GINTC: u16 = 0x52;
pub const CSR_PGDL: u16 = 0x19;
pub const CSR_PGDH: u16 = 0x1a;
pub const CSR_PWCL: u16 = 0x1c;
pub const CSR_PWCH: u16 = 0x1d;
pub const CSR_STLBPS: u16 = 0x1e;
pub const CSR_TLBRENTRY: u16 = 0x88;
pub const CSR_ECFG: u16 = 0x4;
pub const GCSR_ESTAT: usize = 0x5;
pub const GCSR_EENTRY: usize = 0x0c;
pub const GINTC_HWIS_MASK: usize = 0xff;
pub const GINTC_HWIS_SHIFT: usize = 0;
pub const GINTC_HWIP_MASK: usize = 0xff << 8;
pub const GINTC_HWIP_SHIFT: usize = 8;
pub const INT_HWI0: usize = 2;
pub const INT_HWI7: usize = 9;
pub const INT_TIMER: usize = 11;
pub const INT_IPI: usize = 12;
static INJECT_INT_LOCK: Mutex<()> = Mutex::new(());
#[cfg(target_arch = "loongarch64")]
#[inline(always)]
pub(crate) unsafe fn csr_read<const CSR_NUM: u16>() -> usize {
let value: usize;
core::arch::asm!("csrrd {}, {}", out(reg) value, const CSR_NUM);
value
}
#[cfg(not(target_arch = "loongarch64"))]
#[inline(always)]
pub(crate) unsafe fn csr_read<const CSR_NUM: u16>() -> usize {
let _ = CSR_NUM;
0
}
#[cfg(target_arch = "loongarch64")]
#[inline(always)]
pub(crate) unsafe fn csr_write<const CSR_NUM: u16>(value: usize) {
core::arch::asm!("csrwr {}, {}", in(reg) value, const CSR_NUM);
}
#[cfg(not(target_arch = "loongarch64"))]
#[inline(always)]
pub(crate) unsafe fn csr_write<const CSR_NUM: u16>(value: usize) {
let _ = (CSR_NUM, value);
}
#[cfg(target_arch = "loongarch64")]
#[inline(always)]
pub(crate) unsafe fn gcsr_read<const GCSR_NUM: usize>() -> usize {
let value: usize;
core::arch::asm!("gcsrrd {}, {}", out(reg) value, const GCSR_NUM);
value
}
#[cfg(not(target_arch = "loongarch64"))]
#[inline(always)]
pub(crate) unsafe fn gcsr_read<const GCSR_NUM: usize>() -> usize {
let _ = GCSR_NUM;
0
}
#[cfg(target_arch = "loongarch64")]
#[inline(always)]
pub(crate) unsafe fn gcsr_write<const GCSR_NUM: usize>(value: usize) {
core::arch::asm!("gcsrwr {}, {}", in(reg) value, const GCSR_NUM);
}
#[cfg(not(target_arch = "loongarch64"))]
#[inline(always)]
pub(crate) unsafe fn gcsr_write<const GCSR_NUM: usize>(value: usize) {
let _ = (GCSR_NUM, value);
}
#[inline(always)]
pub(crate) fn gstat_read() -> usize {
unsafe { csr_read::<CSR_GSTAT>() }
}
#[inline(always)]
pub(crate) unsafe fn gstat_write(value: usize) {
csr_write::<CSR_GSTAT>(value);
}
#[inline(always)]
pub(crate) unsafe fn set_csr_bits<const CSR_NUM: u16>(
range_lsb: usize,
range_width: usize,
value: usize,
) {
let mask = ((1usize << range_width) - 1) << range_lsb;
let current = csr_read::<CSR_NUM>();
let new_value = (current & !mask) | ((value << range_lsb) & mask);
csr_write::<CSR_NUM>(new_value);
}
#[inline(always)]
pub(crate) unsafe fn set_csr_bit<const CSR_NUM: u16>(bit: usize, value: bool) {
let current = csr_read::<CSR_NUM>();
let mask = 1usize << bit;
let new_value = if value {
current | mask
} else {
current & !mask
};
csr_write::<CSR_NUM>(new_value);
}
#[inline(always)]
pub(crate) unsafe fn gstat_set_gid(gid: usize) {
set_csr_bits::<CSR_GSTAT>(16, 8, gid);
}
#[inline(always)]
pub(crate) unsafe fn gstat_set_pgm(pgm: bool) {
set_csr_bit::<CSR_GSTAT>(1, pgm);
}
#[inline(always)]
pub(crate) unsafe fn gtlbc_set_use_tgid(use_tgid: bool) {
set_csr_bit::<CSR_GTLBC>(12, use_tgid);
}
#[inline(always)]
pub(crate) unsafe fn gtlbc_set_tgid(tgid: usize) {
set_csr_bits::<CSR_GTLBC>(16, 8, tgid);
}
#[inline(always)]
pub(crate) unsafe fn gcfg_set_matc(matc: usize) {
set_csr_bits::<CSR_GCTL>(4, 2, matc);
}
#[inline(always)]
pub(crate) unsafe fn gcfg_set_topi(topi: bool) {
set_csr_bit::<CSR_GCTL>(7, topi);
}
#[inline(always)]
pub(crate) unsafe fn gcfg_set_toti(toti: bool) {
set_csr_bit::<CSR_GCTL>(9, toti);
}
#[inline(always)]
pub(crate) unsafe fn gcfg_set_toe(toe: bool) {
set_csr_bit::<CSR_GCTL>(11, toe);
}
#[inline(always)]
pub(crate) unsafe fn gcfg_set_top(top: bool) {
set_csr_bit::<CSR_GCTL>(13, top);
}
#[inline(always)]
pub(crate) unsafe fn gcfg_set_tohu(tohu: bool) {
set_csr_bit::<CSR_GCTL>(15, tohu);
}
#[inline(always)]
pub(crate) unsafe fn gcfg_set_toci(toci: usize) {
set_csr_bits::<CSR_GCTL>(20, 2, toci);
}
#[inline(always)]
pub(crate) unsafe fn gcfg_set_gpm_num(gpm_num: usize) {
set_csr_bits::<CSR_GCTL>(24, 3, gpm_num);
}
#[inline(always)]
pub(crate) unsafe fn gintc_set_hwip(mask: usize) {
let mut gintc = read_gintc();
gintc &= !GINTC_HWIP_MASK;
gintc |= (mask << GINTC_HWIP_SHIFT) & GINTC_HWIP_MASK;
write_gintc(gintc);
}
#[inline(always)]
pub(crate) unsafe fn set_ecfg_line_enabled(line: usize, enabled: bool) {
let bit = 1usize << line;
let current = csr_read::<CSR_ECFG>();
let new_value = if enabled {
current | bit
} else {
current & !bit
};
csr_write::<CSR_ECFG>(new_value);
}
#[inline(always)]
pub(crate) unsafe fn set_ecfg_vs(vs: usize) {
set_csr_bits::<CSR_ECFG>(16, 3, vs);
}
#[inline(always)]
pub(crate) unsafe fn get_ecfg_vs() -> usize {
(csr_read::<CSR_ECFG>() >> 16) & 0x7
}
#[inline(always)]
pub(crate) fn gcsr_eentry_read() -> usize {
unsafe { gcsr_read::<GCSR_EENTRY>() }
}
fn read_gintc() -> usize {
unsafe { csr_read::<CSR_GINTC>() }
}
unsafe fn write_gintc(value: usize) {
csr_write::<CSR_GINTC>(value);
}
pub fn inject_interrupt(vector: usize) {
if vector > INT_IPI {
log::warn!("LoongArch64: invalid interrupt vector {vector}");
return;
}
let _guard = INJECT_INT_LOCK.lock();
unsafe {
if (INT_HWI0..=INT_HWI7).contains(&vector) {
let hwis_bit = 1 << (vector - INT_HWI0);
let current_hwis = (read_gintc() & GINTC_HWIS_MASK) >> GINTC_HWIS_SHIFT;
let mut gintc = read_gintc();
gintc &= !GINTC_HWIS_MASK;
gintc |= ((current_hwis | hwis_bit) << GINTC_HWIS_SHIFT) & GINTC_HWIS_MASK;
write_gintc(gintc);
} else {
let estat = gcsr_read::<GCSR_ESTAT>();
gcsr_write::<GCSR_ESTAT>(estat | (1usize << vector));
}
}
}