Skip to content

Commit 7ae2227

Browse files
authored
feat(la-qemu): add ipi support for LoongArch (#34)
1 parent a632f9a commit 7ae2227

File tree

3 files changed

+93
-20
lines changed

3 files changed

+93
-20
lines changed

platforms/axplat-loongarch64-qemu-virt/axconfig.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ uart-paddr = 0x1FE001E0 # uint
8282
timer-frequency = 100_000_000 # uint
8383
# Timer interrupt number.
8484
timer-irq = 11 # uint
85+
# IPI interrupt num
86+
ipi-irq = 12 # uint
8587

8688
# RTC (ls7a) Address
8789
rtc-paddr = 0x100d_0100 # uint

platforms/axplat-loongarch64-qemu-virt/src/init.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,16 @@ impl InitIf for InitIfImpl {
2525
/// platform configuration and initialization.
2626
fn init_later(_cpu_id: usize, _arg: usize) {
2727
crate::time::init_percpu();
28+
#[cfg(feature = "smp")]
29+
{
30+
axplat::irq::set_enable(crate::config::devices::IPI_IRQ, true);
31+
}
2832
}
2933

3034
/// Initializes the platform at the later stage for secondary cores.
3135
#[cfg(feature = "smp")]
3236
fn init_later_secondary(_cpu_id: usize) {
3337
crate::time::init_percpu();
38+
axplat::irq::set_enable(crate::config::devices::IPI_IRQ, true);
3439
}
3540
}

platforms/axplat-loongarch64-qemu-virt/src/irq.rs

Lines changed: 86 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,49 @@
11
use axplat::irq::{HandlerTable, IpiTarget, IrqHandler, IrqIf};
2-
use loongArch64::register::{
3-
ecfg::{self, LineBasedInterrupt},
4-
ticlr,
2+
use loongArch64::{
3+
iocsr::{iocsr_read_w, iocsr_write_w},
4+
register::{
5+
ecfg::{self, LineBasedInterrupt},
6+
ticlr,
7+
},
58
};
69

10+
use crate::config::devices::{IPI_IRQ, TIMER_IRQ};
11+
712
/// The maximum number of IRQs.
8-
pub const MAX_IRQ_COUNT: usize = 12;
13+
pub const MAX_IRQ_COUNT: usize = 13;
14+
const IOCSR_IPI_SEND_CPU_SHIFT: u32 = 16;
15+
const IOCSR_IPI_SEND_BLOCKING: u32 = 1 << 31;
16+
17+
// [Loongson 3A5000 Manual](https://loongson.github.io/LoongArch-Documentation/Loongson-3A5000-usermanual-EN.html)
18+
// See Section 10.2 for details about IPI registers
19+
const IOCSR_IPI_STATUS: usize = 0x1000;
20+
const IOCSR_IPI_ENABLE: usize = 0x1004;
21+
const IOCSR_IPI_CLEAR: usize = 0x100c;
22+
const IOCSR_IPI_SEND: usize = 0x1040;
23+
24+
fn make_ipi_send_value(cpu_id: usize, vector: u32, blocking: bool) -> u32 {
25+
let mut value = (cpu_id as u32) << IOCSR_IPI_SEND_CPU_SHIFT | vector;
26+
if blocking {
27+
value |= IOCSR_IPI_SEND_BLOCKING;
28+
}
29+
value
30+
}
31+
32+
fn handle_ipi(irq: usize) {
33+
let mut status = iocsr_read_w(IOCSR_IPI_STATUS);
34+
if status == 0 {
35+
return;
36+
}
37+
iocsr_write_w(IOCSR_IPI_CLEAR, status);
38+
trace!("IPI status = {:#x}", status);
39+
while status != 0 {
40+
let vector = status.trailing_zeros() as usize;
41+
status &= !(1 << vector);
42+
if !IRQ_HANDLER_TABLE.handle(irq) {
43+
warn!("Unhandled IRQ {}", irq);
44+
}
45+
}
46+
}
947

1048
static IRQ_HANDLER_TABLE: HandlerTable<MAX_IRQ_COUNT> = HandlerTable::new();
1149

@@ -15,14 +53,24 @@ struct IrqIfImpl;
1553
impl IrqIf for IrqIfImpl {
1654
/// Enables or disables the given IRQ.
1755
fn set_enable(irq_num: usize, enabled: bool) {
18-
if irq_num == crate::config::devices::TIMER_IRQ {
19-
let old_value = ecfg::read().lie();
20-
let new_value = match enabled {
21-
true => old_value | LineBasedInterrupt::TIMER,
22-
false => old_value & !LineBasedInterrupt::TIMER,
23-
};
24-
ecfg::set_lie(new_value);
25-
}
56+
let interrupt_bit = match irq_num {
57+
TIMER_IRQ => LineBasedInterrupt::TIMER,
58+
IPI_IRQ => {
59+
let value = if enabled { u32::MAX } else { 0 };
60+
iocsr_write_w(IOCSR_IPI_ENABLE, value);
61+
LineBasedInterrupt::IPI
62+
}
63+
_ => {
64+
warn!("set_enable: unsupported irq {}", irq_num);
65+
return;
66+
}
67+
};
68+
let old_value = ecfg::read().lie();
69+
let new_value = match enabled {
70+
true => old_value | interrupt_bit,
71+
false => old_value & !interrupt_bit,
72+
};
73+
ecfg::set_lie(new_value);
2674
}
2775

2876
/// Registers an IRQ handler for the given IRQ.
@@ -50,17 +98,35 @@ impl IrqIf for IrqIfImpl {
5098
/// IRQ handler table and calls the corresponding handler. If necessary, it
5199
/// also acknowledges the interrupt controller after handling.
52100
fn handle(irq: usize) {
53-
if irq == crate::config::devices::TIMER_IRQ {
54-
ticlr::clear_timer_interrupt();
55-
}
56-
trace!("IRQ {}", irq);
57-
if !IRQ_HANDLER_TABLE.handle(irq) {
58-
warn!("Unhandled IRQ {}", irq);
101+
if irq == IPI_IRQ {
102+
handle_ipi(irq);
103+
} else {
104+
if irq == TIMER_IRQ {
105+
ticlr::clear_timer_interrupt();
106+
}
107+
trace!("IRQ {}", irq);
108+
if !IRQ_HANDLER_TABLE.handle(irq) {
109+
warn!("Unhandled IRQ {}", irq);
110+
}
59111
}
60112
}
61113

62114
/// Sends an inter-processor interrupt (IPI) to the specified target CPU or all CPUs.
63-
fn send_ipi(_irq_num: usize, _target: IpiTarget) {
64-
todo!()
115+
fn send_ipi(_irq_num: usize, target: IpiTarget) {
116+
match target {
117+
IpiTarget::Current { cpu_id } => {
118+
iocsr_write_w(IOCSR_IPI_SEND, make_ipi_send_value(cpu_id, 0, true));
119+
}
120+
IpiTarget::Other { cpu_id } => {
121+
iocsr_write_w(IOCSR_IPI_SEND, make_ipi_send_value(cpu_id, 0, true));
122+
}
123+
IpiTarget::AllExceptCurrent { cpu_id, cpu_num } => {
124+
for i in 0..cpu_num {
125+
if i != cpu_id {
126+
iocsr_write_w(IOCSR_IPI_SEND, make_ipi_send_value(i, 0, true));
127+
}
128+
}
129+
}
130+
}
65131
}
66132
}

0 commit comments

Comments
 (0)