xref: /DragonOS/kernel/src/driver/irqchip/riscv_intc.rs (revision 1ea2daad8121b77ed704e6d7c3a09f478147441d)
1 use alloc::{string::ToString, sync::Arc};
2 use log::error;
3 use system_error::SystemError;
4 
5 use crate::{
6     arch::interrupt::TrapFrame,
7     driver::clocksource::timer_riscv::{riscv_sbi_timer_irq_desc_init, RiscVSbiTimer},
8     exception::{
9         handle::PerCpuDevIdIrqHandler,
10         irqchip::{IrqChip, IrqChipFlags},
11         irqdata::IrqData,
12         irqdesc::{irq_desc_manager, GenericIrqHandler},
13         irqdomain::{irq_domain_manager, IrqDomain, IrqDomainOps},
14         softirq::do_softirq,
15         HardwareIrqNumber, IrqNumber,
16     },
17     libs::spinlock::{SpinLock, SpinLockGuard},
18     sched::{SchedMode, __schedule},
19 };
20 
21 use super::riscv_sifive_plic::do_plic_irq;
22 
23 static mut RISCV_INTC_DOMAIN: Option<Arc<IrqDomain>> = None;
24 static mut RISCV_INTC_CHIP: Option<Arc<RiscvIntcChip>> = None;
25 
26 #[inline(always)]
27 pub fn riscv_intc_domain() -> &'static Option<Arc<IrqDomain>> {
28     unsafe { &RISCV_INTC_DOMAIN }
29 }
30 
31 #[inline(always)]
32 fn riscv_intc_chip() -> Option<&'static Arc<RiscvIntcChip>> {
33     unsafe { RISCV_INTC_CHIP.as_ref() }
34 }
35 
36 /// RISC-V INTC虚拟中断号的起始值(192映射物理的0)
37 pub const RISCV_INTC_VIRQ_START: u32 = 192;
38 
39 #[derive(Debug)]
40 struct RiscvIntcChip {
41     inner: SpinLock<InnerIrqChip>,
42 }
43 
44 impl IrqChip for RiscvIntcChip {
45     fn name(&self) -> &'static str {
46         "RISC-V INTC"
47     }
48 
49     fn irq_disable(&self, _irq: &Arc<IrqData>) {}
50 
51     fn irq_mask(&self, irq: &Arc<IrqData>) -> Result<(), SystemError> {
52         unsafe { riscv::register::sie::clear_bits(1 << irq.hardware_irq().data()) };
53         Ok(())
54     }
55 
56     fn irq_unmask(&self, irq: &Arc<IrqData>) -> Result<(), SystemError> {
57         unsafe { riscv::register::sie::set_bits(1 << irq.hardware_irq().data()) };
58         Ok(())
59     }
60 
61     fn irq_ack(&self, _irq: &Arc<IrqData>) {}
62 
63     fn can_mask_ack(&self) -> bool {
64         false
65     }
66 
67     fn irq_eoi(&self, _irq: &Arc<IrqData>) {
68         /*
69          * The RISC-V INTC driver uses handle_percpu_devid_irq() flow
70          * for the per-HART local interrupts and child irqchip drivers
71          * (such as PLIC, SBI IPI, CLINT, APLIC, IMSIC, etc) implement
72          * chained handlers for the per-HART local interrupts.
73          *
74          * In the absence of irq_eoi(), the chained_irq_enter() and
75          * chained_irq_exit() functions (used by child irqchip drivers)
76          * will do unnecessary mask/unmask of per-HART local interrupts
77          * at the time of handling interrupts. To avoid this, we provide
78          * an empty irq_eoi() callback for RISC-V INTC irqchip.
79          */
80     }
81 
82     fn can_set_affinity(&self) -> bool {
83         false
84     }
85 
86     fn can_set_flow_type(&self) -> bool {
87         false
88     }
89 
90     fn flags(&self) -> IrqChipFlags {
91         self.inner().flags
92     }
93 }
94 
95 impl RiscvIntcChip {
96     const IRQ_SIZE: u32 = 64;
97     fn new() -> Self {
98         Self {
99             inner: SpinLock::new(InnerIrqChip {
100                 flags: IrqChipFlags::empty(),
101             }),
102         }
103     }
104     fn inner(&self) -> SpinLockGuard<InnerIrqChip> {
105         self.inner.lock_irqsave()
106     }
107 }
108 
109 #[derive(Debug)]
110 struct InnerIrqChip {
111     flags: IrqChipFlags,
112 }
113 
114 #[derive(Debug)]
115 struct RiscvIntcDomainOps;
116 
117 impl IrqDomainOps for RiscvIntcDomainOps {
118     fn map(
119         &self,
120         irq_domain: &Arc<IrqDomain>,
121         hwirq: HardwareIrqNumber,
122         virq: IrqNumber,
123     ) -> Result<(), SystemError> {
124         irq_desc_manager().set_percpu_devid_all(virq)?;
125         irq_domain_manager().domain_set_info(
126             irq_domain,
127             virq,
128             hwirq,
129             riscv_intc_chip().unwrap().clone() as Arc<dyn IrqChip>,
130             irq_domain.host_data(),
131             &PerCpuDevIdIrqHandler,
132             None,
133             None,
134         );
135 
136         return Ok(());
137     }
138 
139     fn unmap(&self, _irq_domain: &Arc<IrqDomain>, _virq: IrqNumber) {
140         todo!("riscv_intc_domain_ops::unmap");
141     }
142 }
143 
144 #[inline(never)]
145 pub unsafe fn riscv_intc_init() -> Result<(), SystemError> {
146     let intc_chip = Arc::new(RiscvIntcChip::new());
147 
148     unsafe {
149         RISCV_INTC_CHIP = Some(intc_chip);
150     }
151 
152     let intc_domain = irq_domain_manager()
153         .create_and_add_linear(
154             "riscv-intc".to_string(),
155             &RiscvIntcDomainOps,
156             RiscvIntcChip::IRQ_SIZE,
157         )
158         .ok_or_else(|| {
159             error!("Failed to create riscv-intc domain");
160             SystemError::ENXIO
161         })?;
162 
163     irq_domain_manager().set_default_domain(intc_domain.clone());
164 
165     unsafe {
166         RISCV_INTC_DOMAIN = Some(intc_domain.clone());
167     }
168 
169     riscv_sbi_timer_irq_desc_init();
170 
171     return Ok(());
172 }
173 
174 /// 把硬件中断号转换为riscv intc芯片的中断域的虚拟中断号
175 pub const fn riscv_intc_hwirq_to_virq(hwirq: HardwareIrqNumber) -> Option<IrqNumber> {
176     if hwirq.data() < RiscvIntcChip::IRQ_SIZE {
177         Some(IrqNumber::new(hwirq.data() + RISCV_INTC_VIRQ_START))
178     } else {
179         None
180     }
181 }
182 
183 /// 把riscv intc芯片的的中断域的虚拟中断号转换为硬件中断号
184 #[allow(dead_code)]
185 pub const fn riscv_intc_virq_to_hwirq(virq: IrqNumber) -> Option<HardwareIrqNumber> {
186     if virq.data() >= RISCV_INTC_VIRQ_START
187         && virq.data() < RISCV_INTC_VIRQ_START + RiscvIntcChip::IRQ_SIZE
188     {
189         Some(HardwareIrqNumber::new(virq.data() - RISCV_INTC_VIRQ_START))
190     } else {
191         None
192     }
193 }
194 
195 /// 将硬件中断号与riscv intc芯片的虚拟中断号关联
196 pub fn riscv_intc_assicate_irq(hwirq: HardwareIrqNumber) -> Option<IrqNumber> {
197     let virq = riscv_intc_hwirq_to_virq(hwirq)?;
198     irq_domain_manager()
199         .domain_associate(
200             riscv_intc_domain().as_ref().or_else(|| {
201                 error!("riscv_intc_domain is None");
202                 None
203             })?,
204             virq,
205             hwirq,
206         )
207         .ok();
208 
209     Some(virq)
210 }
211 
212 /// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/irqchip/irq-riscv-intc.c#23
213 pub fn riscv_intc_irq(trap_frame: &mut TrapFrame) {
214     let hwirq = HardwareIrqNumber::new(trap_frame.cause.code() as u32);
215     if hwirq.data() == 9 {
216         // external interrupt
217         do_plic_irq(trap_frame);
218     } else {
219         GenericIrqHandler::handle_domain_irq(
220             riscv_intc_domain().clone().unwrap(),
221             hwirq,
222             trap_frame,
223         )
224         .ok();
225     }
226     do_softirq();
227     if hwirq.data() == RiscVSbiTimer::TIMER_IRQ.data() {
228         __schedule(SchedMode::SM_PREEMPT);
229     }
230 }
231