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