11use 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
1048static IRQ_HANDLER_TABLE : HandlerTable < MAX_IRQ_COUNT > = HandlerTable :: new ( ) ;
1149
@@ -15,14 +53,24 @@ struct IrqIfImpl;
1553impl 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