|
1 | 1 | //! ARM Generic Interrupt Controller (GIC). |
2 | 2 |
|
3 | | -use arm_gicv2::{GicCpuInterface, GicDistributor}; |
4 | | -use axplat::irq::{HandlerTable, IrqHandler}; |
5 | | -use axplat::mem::VirtAddr; |
| 3 | +use arm_gic_driver::v2::{Ack, Gic, IntId, SGITarget, TargetList, TrapOp, VirtAddr}; |
| 4 | +use axplat::irq::{HandlerTable, IpiTarget, IrqHandler}; |
6 | 5 | use kspin::SpinNoIrq; |
7 | 6 | use lazyinit::LazyInit; |
8 | 7 |
|
9 | 8 | /// The maximum number of IRQs. |
10 | 9 | const MAX_IRQ_COUNT: usize = 1024; |
11 | 10 |
|
12 | | -static GICD: LazyInit<SpinNoIrq<GicDistributor>> = LazyInit::new(); |
| 11 | +static GIC: LazyInit<SpinNoIrq<Gic>> = LazyInit::new(); |
13 | 12 |
|
14 | | -// per-CPU, no lock |
15 | | -static GICC: LazyInit<GicCpuInterface> = LazyInit::new(); |
| 13 | +static TRAP_OP: LazyInit<TrapOp> = LazyInit::new(); |
16 | 14 |
|
17 | 15 | static IRQ_HANDLER_TABLE: HandlerTable<MAX_IRQ_COUNT> = HandlerTable::new(); |
18 | 16 |
|
19 | 17 | /// Enables or disables the given IRQ. |
20 | 18 | pub fn set_enable(irq_num: usize, enabled: bool) { |
21 | | - trace!("GICD set enable: {} {}", irq_num, enabled); |
22 | | - GICD.lock().set_enable(irq_num as _, enabled); |
| 19 | + trace!("GIC set enable: {} {}", irq_num, enabled); |
| 20 | + let intid = unsafe { IntId::raw(irq_num as u32) }; |
| 21 | + GIC.lock().set_irq_enable(intid, enabled); |
23 | 22 | } |
24 | 23 |
|
25 | 24 | /// Registers an IRQ handler for the given IRQ. |
@@ -52,27 +51,70 @@ pub fn unregister_handler(irq_num: usize) -> Option<IrqHandler> { |
52 | 51 | /// IRQ handler table and calls the corresponding handler. If necessary, it |
53 | 52 | /// also acknowledges the interrupt controller after handling. |
54 | 53 | pub fn handle_irq(_unused: usize) { |
55 | | - GICC.handle_irq(|irq_num| { |
56 | | - trace!("IRQ {}", irq_num); |
57 | | - if !IRQ_HANDLER_TABLE.handle(irq_num as _) { |
58 | | - warn!("Unhandled IRQ {}", irq_num); |
| 54 | + let ack = TRAP_OP.ack(); |
| 55 | + debug!("Handling IRQ: {ack:?}"); |
| 56 | + |
| 57 | + let irq_num = match ack { |
| 58 | + Ack::Other(intid) => intid, |
| 59 | + Ack::SGI { intid, cpu_id: _ } => intid, |
| 60 | + }; |
| 61 | + if !IRQ_HANDLER_TABLE.handle(irq_num.to_u32() as _) { |
| 62 | + warn!("Unhandled IRQ {:?}", irq_num); |
| 63 | + } |
| 64 | + if !ack.is_special() { |
| 65 | + TRAP_OP.eoi(ack); |
| 66 | + if TRAP_OP.eoi_mode_ns() { |
| 67 | + TRAP_OP.dir(ack); |
59 | 68 | } |
60 | | - }); |
| 69 | + } |
61 | 70 | } |
62 | 71 |
|
63 | | -/// Initializes GICD (for the primary CPU only). |
64 | | -pub fn init_gicd(gicd_base: VirtAddr, gicc_base: VirtAddr) { |
| 72 | +/// Initializes GIC |
| 73 | +pub fn init_gic(gicd_base: axplat::mem::VirtAddr, gicc_base: axplat::mem::VirtAddr) { |
65 | 74 | info!("Initialize GICv2..."); |
66 | | - GICD.init_once(SpinNoIrq::new(GicDistributor::new(gicd_base.as_mut_ptr()))); |
67 | | - GICC.init_once(GicCpuInterface::new(gicc_base.as_mut_ptr())); |
68 | | - GICD.lock().init(); |
| 75 | + let gicd_base = VirtAddr::new(gicd_base.into()); |
| 76 | + let gicc_base = VirtAddr::new(gicc_base.into()); |
| 77 | + |
| 78 | + let mut gic = unsafe { Gic::new(gicd_base, gicc_base, None) }; |
| 79 | + gic.init(); |
| 80 | + |
| 81 | + GIC.init_once(SpinNoIrq::new(gic)); |
| 82 | + let cpu = GIC.lock().cpu_interface(); |
| 83 | + TRAP_OP.init_once(cpu.trap_operations()); |
69 | 84 | } |
70 | 85 |
|
71 | 86 | /// Initializes GICC (for all CPUs). |
72 | 87 | /// |
73 | | -/// It must be called after [`init_gicd`]. |
| 88 | +/// It must be called after [`init_gic`]. |
74 | 89 | pub fn init_gicc() { |
75 | | - GICC.init(); |
| 90 | + debug!("Initialize GIC CPU Interface..."); |
| 91 | + let mut cpu = GIC.lock().cpu_interface(); |
| 92 | + cpu.init_current_cpu(); |
| 93 | + cpu.set_eoi_mode_ns(false); |
| 94 | +} |
| 95 | + |
| 96 | +/// Sends an inter-processor interrupt (IPI) to the specified target CPU or all CPUs. |
| 97 | +pub fn send_ipi(irq_num: usize, target: IpiTarget) { |
| 98 | + match target { |
| 99 | + IpiTarget::Current { cpu_id: _ } => { |
| 100 | + GIC.lock() |
| 101 | + .send_sgi(IntId::sgi(irq_num as u32), SGITarget::Current); |
| 102 | + } |
| 103 | + IpiTarget::Other { cpu_id } => { |
| 104 | + let target_list = TargetList::new(&mut [cpu_id].into_iter()); |
| 105 | + GIC.lock().send_sgi( |
| 106 | + IntId::sgi(irq_num as u32), |
| 107 | + SGITarget::TargetList(target_list), |
| 108 | + ); |
| 109 | + } |
| 110 | + IpiTarget::AllExceptCurrent { |
| 111 | + cpu_id: _, |
| 112 | + cpu_num: _, |
| 113 | + } => { |
| 114 | + GIC.lock() |
| 115 | + .send_sgi(IntId::sgi(irq_num as u32), SGITarget::AllOther); |
| 116 | + } |
| 117 | + } |
76 | 118 | } |
77 | 119 |
|
78 | 120 | /// Default implementation of [`axplat::irq::IrqIf`] using the GIC. |
@@ -112,6 +154,11 @@ macro_rules! irq_if_impl { |
112 | 154 | fn handle(irq: usize) { |
113 | 155 | $crate::gic::handle_irq(irq) |
114 | 156 | } |
| 157 | + |
| 158 | + /// Sends an inter-processor interrupt (IPI) to the specified target CPU or all CPUs. |
| 159 | + fn send_ipi(irq_num: usize, target: axplat::irq::IpiTarget) { |
| 160 | + $crate::gic::send_ipi(irq_num, target); |
| 161 | + } |
115 | 162 | } |
116 | 163 | }; |
117 | 164 | } |
0 commit comments