xref: /DragonOS/kernel/src/driver/irqchip/riscv_intc.rs (revision 9fab312ea9921618629924ab15c28c2d255b21c6)
1 use alloc::{string::ToString, sync::Arc};
2 use system_error::SystemError;
3 
4 use crate::{
5     arch::interrupt::TrapFrame,
6     driver::clocksource::timer_riscv::{riscv_sbi_timer_irq_desc_init, RiscVSbiTimer},
7     exception::{
8         handle::PerCpuDevIdIrqHandler,
9         irqchip::{IrqChip, IrqChipFlags},
10         irqdata::IrqData,
11         irqdesc::{irq_desc_manager, GenericIrqHandler},
12         irqdomain::{irq_domain_manager, IrqDomain, IrqDomainOps},
13         HardwareIrqNumber, IrqNumber,
14     },
15     libs::spinlock::{SpinLock, SpinLockGuard},
16     sched::{SchedMode, __schedule},
17 };
18 
19 static mut RISCV_INTC_DOMAIN: Option<Arc<IrqDomain>> = None;
20 static mut RISCV_INTC_CHIP: Option<Arc<RiscvIntcChip>> = None;
21 
22 #[inline(always)]
23 pub fn riscv_intc_domain() -> &'static Option<Arc<IrqDomain>> {
24     unsafe { &RISCV_INTC_DOMAIN }
25 }
26 
27 #[inline(always)]
28 fn riscv_intc_chip() -> Option<&'static Arc<RiscvIntcChip>> {
29     unsafe { RISCV_INTC_CHIP.as_ref() }
30 }
31 
32 #[derive(Debug)]
33 struct RiscvIntcChip {
34     inner: SpinLock<InnerIrqChip>,
35 }
36 
37 impl IrqChip for RiscvIntcChip {
38     fn name(&self) -> &'static str {
39         "RISC-V INTC"
40     }
41 
42     fn irq_disable(&self, _irq: &Arc<IrqData>) {}
43 
44     fn irq_mask(&self, irq: &Arc<IrqData>) -> Result<(), SystemError> {
45         unsafe { riscv::register::sie::clear_bits(1 << irq.hardware_irq().data()) };
46         Ok(())
47     }
48 
49     fn irq_unmask(&self, irq: &Arc<IrqData>) -> Result<(), SystemError> {
50         unsafe { riscv::register::sie::set_bits(1 << irq.hardware_irq().data()) };
51         Ok(())
52     }
53 
54     fn irq_ack(&self, _irq: &Arc<IrqData>) {}
55 
56     fn can_mask_ack(&self) -> bool {
57         false
58     }
59 
60     fn irq_eoi(&self, _irq: &Arc<IrqData>) {
61         /*
62          * The RISC-V INTC driver uses handle_percpu_devid_irq() flow
63          * for the per-HART local interrupts and child irqchip drivers
64          * (such as PLIC, SBI IPI, CLINT, APLIC, IMSIC, etc) implement
65          * chained handlers for the per-HART local interrupts.
66          *
67          * In the absence of irq_eoi(), the chained_irq_enter() and
68          * chained_irq_exit() functions (used by child irqchip drivers)
69          * will do unnecessary mask/unmask of per-HART local interrupts
70          * at the time of handling interrupts. To avoid this, we provide
71          * an empty irq_eoi() callback for RISC-V INTC irqchip.
72          */
73     }
74 
75     fn can_set_affinity(&self) -> bool {
76         false
77     }
78 
79     fn can_set_flow_type(&self) -> bool {
80         false
81     }
82 
83     fn flags(&self) -> IrqChipFlags {
84         self.inner().flags
85     }
86 }
87 
88 impl RiscvIntcChip {
89     fn new() -> Self {
90         Self {
91             inner: SpinLock::new(InnerIrqChip {
92                 flags: IrqChipFlags::empty(),
93             }),
94         }
95     }
96     fn inner(&self) -> SpinLockGuard<InnerIrqChip> {
97         self.inner.lock_irqsave()
98     }
99 }
100 
101 #[derive(Debug)]
102 struct InnerIrqChip {
103     flags: IrqChipFlags,
104 }
105 
106 #[derive(Debug)]
107 struct RiscvIntcDomainOps;
108 
109 impl IrqDomainOps for RiscvIntcDomainOps {
110     fn map(
111         &self,
112         irq_domain: &Arc<IrqDomain>,
113         hwirq: HardwareIrqNumber,
114         virq: IrqNumber,
115     ) -> Result<(), SystemError> {
116         irq_desc_manager().set_percpu_devid_all(virq)?;
117         irq_domain_manager().domain_set_info(
118             irq_domain,
119             virq,
120             hwirq,
121             riscv_intc_chip().unwrap().clone() as Arc<dyn IrqChip>,
122             irq_domain.host_data(),
123             &PerCpuDevIdIrqHandler,
124             None,
125             None,
126         );
127 
128         return Ok(());
129     }
130 
131     fn unmap(&self, _irq_domain: &Arc<IrqDomain>, _virq: IrqNumber) {
132         todo!("riscv_intc_domain_ops::unmap");
133     }
134 }
135 
136 #[inline(never)]
137 pub unsafe fn riscv_intc_init() -> Result<(), SystemError> {
138     let intc_chip = Arc::new(RiscvIntcChip::new());
139 
140     unsafe {
141         RISCV_INTC_CHIP = Some(intc_chip);
142     }
143 
144     let intc_domain = irq_domain_manager()
145         .create_and_add_linear("riscv-intc".to_string(), &RiscvIntcDomainOps, 64)
146         .ok_or_else(|| {
147             kerror!("Failed to create riscv-intc domain");
148             SystemError::ENXIO
149         })?;
150 
151     irq_domain_manager().set_default_domain(intc_domain.clone());
152 
153     unsafe {
154         RISCV_INTC_DOMAIN = Some(intc_domain);
155     }
156 
157     riscv_sbi_timer_irq_desc_init();
158 
159     return Ok(());
160 }
161 
162 /// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/irqchip/irq-riscv-intc.c#23
163 pub fn riscv_intc_irq(trap_frame: &mut TrapFrame) {
164     let hwirq = HardwareIrqNumber::new(trap_frame.cause.code() as u32);
165     kdebug!("riscv64_do_irq: interrupt {hwirq:?}");
166     GenericIrqHandler::handle_domain_irq(riscv_intc_domain().clone().unwrap(), hwirq, trap_frame)
167         .ok();
168     if hwirq.data() == RiscVSbiTimer::TIMER_IRQ.data() {
169         __schedule(SchedMode::SM_PREEMPT);
170     }
171 }
172