kernel/drivers/pic/
sbi_clint.rs

1//! SBI-based CLINT (Core Local Interruptor) driver for RISC-V architecture.
2//!! This driver uses SBI calls to manage local interrupts such as
3//! timer interrupts and software interrupts.
4
5use core::arch::asm;
6
7use alloc::boxed::Box;
8
9use crate::{
10    early_initcall,
11    interrupt::{
12        CpuId, InterruptError, InterruptManager, InterruptResult,
13        controllers::{LocalInterruptController, LocalInterruptType},
14    },
15};
16
17struct SbiClint {
18    max_cpus: usize,
19    timebase_frequency_hz: u64,
20}
21
22impl SbiClint {
23    /// Validate CPU ID
24    fn validate_cpu_id(&self, cpu_id: CpuId) -> InterruptResult<()> {
25        if cpu_id as usize >= self.max_cpus {
26            Err(InterruptError::InvalidCpuId)
27        } else {
28            Ok(())
29        }
30    }
31}
32
33impl LocalInterruptController for SbiClint {
34    /// Initialize the CLINT for a specific CPU
35    fn init(&mut self, cpu_id: CpuId) -> InterruptResult<()> {
36        self.validate_cpu_id(cpu_id)?;
37
38        // Clear software interrupt
39        self.clear_software_interrupt(cpu_id)?;
40
41        // Set timer to maximum value (effectively disable)
42        self.set_timer(cpu_id, u64::MAX)?;
43
44        Ok(())
45    }
46
47    /// Enable a specific local interrupt type for a CPU
48    fn enable_interrupt(
49        &mut self,
50        _cpu_id: CpuId,
51        interrupt_type: LocalInterruptType,
52    ) -> InterruptResult<()> {
53        match interrupt_type {
54            LocalInterruptType::Timer => {
55                // Timer interrupts are enabled by setting mtimecmp
56                // This is done via set_timer() method
57                Ok(())
58            }
59            LocalInterruptType::Software => {
60                // Software interrupts are enabled by setting MSIP
61                // This is done via send_software_interrupt() method
62                Ok(())
63            }
64            LocalInterruptType::External => {
65                // External interrupts are not managed by CLINT
66                Err(InterruptError::NotSupported)
67            }
68        }
69    }
70
71    /// Disable a specific local interrupt type for a CPU
72    fn disable_interrupt(
73        &mut self,
74        cpu_id: CpuId,
75        interrupt_type: LocalInterruptType,
76    ) -> InterruptResult<()> {
77        match interrupt_type {
78            LocalInterruptType::Timer => {
79                // Disable timer by setting mtimecmp to maximum value
80                self.set_timer(cpu_id, u64::MAX)
81            }
82            LocalInterruptType::Software => {
83                // Disable software interrupt by clearing MSIP
84                self.clear_software_interrupt(cpu_id)
85            }
86            LocalInterruptType::External => {
87                // External interrupts are not managed by CLINT
88                Err(InterruptError::NotSupported)
89            }
90        }
91    }
92
93    /// Check if a specific local interrupt type is pending for a CPU
94    fn is_pending(&self, cpu_id: CpuId, interrupt_type: LocalInterruptType) -> bool {
95        if self.validate_cpu_id(cpu_id).is_err() {
96            return false;
97        }
98
99        match interrupt_type {
100            LocalInterruptType::Timer => false,
101            LocalInterruptType::Software => false,
102            LocalInterruptType::External => false, // Not managed by CLINT
103        }
104    }
105
106    /// Clear a pending local interrupt for a CPU
107    fn clear_interrupt(
108        &mut self,
109        cpu_id: CpuId,
110        interrupt_type: LocalInterruptType,
111    ) -> InterruptResult<()> {
112        self.validate_cpu_id(cpu_id)?;
113
114        match interrupt_type {
115            LocalInterruptType::Timer => {
116                // Clear timer interrupt by setting mtimecmp to future time
117                let current_time = self.get_time();
118                self.set_timer(cpu_id, current_time + 1000000) // 1M cycles in future
119            }
120            LocalInterruptType::Software => self.clear_software_interrupt(cpu_id),
121            LocalInterruptType::External => Err(InterruptError::NotSupported),
122        }
123    }
124
125    /// Send a software interrupt to a specific CPU
126    fn send_software_interrupt(&mut self, _target_cpu: CpuId) -> InterruptResult<()> {
127        Ok(())
128    }
129
130    /// Clear a software interrupt for a specific CPU
131    fn clear_software_interrupt(&mut self, _cpu_id: CpuId) -> InterruptResult<()> {
132        // self.validate_cpu_id(cpu_id)?;
133
134        // let addr = self.msip_addr(cpu_id);
135        // unsafe {
136        //     write_volatile(addr as *mut u32, 0);
137        // }
138
139        // TODO: Use SBI to clear software interrupt
140        // For now, just return Ok
141
142        Ok(())
143    }
144
145    /// Set timer interrupt for a specific CPU
146    fn set_timer(&mut self, cpu_id: CpuId, time: u64) -> InterruptResult<()> {
147        self.validate_cpu_id(cpu_id)?;
148
149        // Set the timer compare register to the specified time using SBI
150        crate::arch::riscv64::instruction::sbi::sbi_set_timer(time);
151
152        Ok(())
153    }
154
155    /// Get current timer value
156    fn get_time(&self) -> u64 {
157        let time: u64;
158        unsafe {
159            asm!(
160                "rdtime {0}",
161                out(reg) time,
162            );
163        }
164        time
165    }
166
167    fn get_timer_frequency_hz(&self) -> u64 {
168        self.timebase_frequency_hz
169    }
170}
171
172unsafe impl Send for SbiClint {}
173unsafe impl Sync for SbiClint {}
174
175fn register_driver() {
176    // Read the timebase frequency once from device tree
177    // Prefer the timebase frequency provided by the device tree.
178    // Fallback keeps QEMU virt default (10MHz) working even if FDT is unavailable.
179    let timebase_frequency_hz =
180        crate::arch::riscv64::fdt::timebase_frequency_hz_from_fdt().unwrap_or(10_000_000);
181
182    // Create the SBI timer controller
183    let mut controller = Box::new(SbiClint {
184        max_cpus: 4,
185        timebase_frequency_hz,
186    });
187
188    if let Err(e) = controller.init(0) {
189        crate::early_println!(
190            "[interrupt] Failed to initialize CLINT for CPU {}: {}",
191            0,
192            e
193        );
194    }
195
196    // Register with InterruptManager instead of DeviceManager
197    match InterruptManager::global()
198        .lock()
199        .register_local_controller_for_range(controller, 0..4)
200    {
201        Ok(_) => {}
202        Err(e) => {
203            crate::early_println!("[interrupt] Failed to register CLINT: {}", e);
204        }
205    }
206}
207
208early_initcall!(register_driver);