use loongArch64::iocsr::{iocsr_read_w, iocsr_write_w};
use crate::common::PlatOp;
mod eiointc;
mod irq_common;
mod pch_pic;
pub struct Plat;
const IOCSR_IPI_SEND_CPU_SHIFT: u32 = 16;
const IOCSR_IPI_SEND_BLOCKING: u32 = 1 << 31;
const IOCSR_IPI_STATUS: usize = 0x1000;
const IOCSR_IPI_ENABLE: usize = 0x1004;
const IOCSR_IPI_CLEAR: usize = 0x100c;
const IOCSR_IPI_SEND: usize = 0x1040;
const EIOINTC_IRQ: usize = 3;
const IPI_IRQ: usize = 12;
const IPI_VECTOR: u32 = 0;
fn make_ipi_send_value(cpu_id: usize, vector: u32, blocking: bool) -> u32 {
let mut value = (cpu_id as u32) << IOCSR_IPI_SEND_CPU_SHIFT | vector;
if blocking {
value |= IOCSR_IPI_SEND_BLOCKING;
}
value
}
fn ack_pending_ipi() -> u32 {
let status = iocsr_read_w(IOCSR_IPI_STATUS);
if status != 0 {
iocsr_write_w(IOCSR_IPI_CLEAR, status);
trace!("IPI status = {status:#x}");
}
status
}
impl PlatOp for Plat {
type ActiveIrq = ActiveIrq;
fn irq_set_enable(irq: rdrive::IrqId, enable: bool) {
let raw = irq.raw();
if raw == someboot::irq::systimer_irq().raw() {
someboot::irq::irq_set_enable(someboot::irq::IrqId::new(raw), enable);
return;
}
if raw == IPI_IRQ {
let value = if enable { u32::MAX } else { 0 };
iocsr_write_w(IOCSR_IPI_ENABLE, value);
someboot::irq::irq_set_enable(someboot::irq::IrqId::new(raw), enable);
return;
}
eiointc::set_irq_enable(raw, enable);
pch_pic::set_irq_enable(raw, enable);
}
fn send_ipi(_irq: rdrive::IrqId, target: crate::irq::IpiTarget) {
match target {
crate::irq::IpiTarget::Current { cpu_id } | crate::irq::IpiTarget::Other { cpu_id } => {
Self::send_ipi_to_cpu(cpu_id);
}
crate::irq::IpiTarget::AllExceptCurrent { cpu_id, cpu_num } => {
for target_cpu in 0..cpu_num {
if target_cpu != cpu_id {
Self::send_ipi_to_cpu(target_cpu);
}
}
}
}
}
fn begin_irq(raw: usize) -> Option<Self::ActiveIrq> {
match raw {
raw if raw == someboot::irq::systimer_irq().raw() => {
someboot::timer::ack();
Some(ActiveIrq::new(raw.into(), Completion::None))
}
IPI_IRQ => {
let _status = ack_pending_ipi();
Some(ActiveIrq::new(raw.into(), Completion::None))
}
EIOINTC_IRQ => {
let Some(external) = eiointc::claim_irq() else {
debug!("Spurious LoongArch EIOINTC interrupt");
return None;
};
Some(ActiveIrq::new(
external.into(),
Completion::EioIntc { irq: external },
))
}
external => Some(ActiveIrq::new(external.into(), Completion::None)),
}
}
fn active_irq_id(active: &Self::ActiveIrq) -> rdrive::IrqId {
active.id()
}
fn systick_irq() -> rdrive::IrqId {
someboot::irq::systimer_irq().raw().into()
}
fn secondary_init() {}
fn secondary_init_intc(_cpu_idx: usize) {}
fn secondary_init_systick() {}
fn send_ipi_to_cpu(cpu_id: usize) {
iocsr_write_w(
IOCSR_IPI_SEND,
make_ipi_send_value(cpu_id, IPI_VECTOR, false),
);
}
}
enum Completion {
None,
EioIntc { irq: usize },
}
pub struct ActiveIrq {
irq: rdrive::IrqId,
completion: Completion,
}
impl ActiveIrq {
const fn new(irq: rdrive::IrqId, completion: Completion) -> Self {
Self { irq, completion }
}
pub fn id(&self) -> rdrive::IrqId {
self.irq
}
}
impl Drop for ActiveIrq {
fn drop(&mut self) {
match self.completion {
Completion::None => {}
Completion::EioIntc { irq } => eiointc::complete_irq(irq),
}
}
}