kernel/drivers/pic/
clint.rs

1//! RISC-V Core Local Interrupt Controller (CLINT) Implementation
2//!
3//! The CLINT manages CPU-local interrupts such as timer interrupts and
4//! software interrupts in RISC-V systems.
5
6use crate::{device::{manager::{DeviceManager, DriverPriority}, platform::{resource::PlatformDeviceResourceType, PlatformDeviceDriver, PlatformDeviceInfo}}, driver_initcall, interrupt::{
7    controllers::{LocalInterruptController, LocalInterruptType}, CpuId, InterruptError, InterruptManager, InterruptResult
8}};
9use alloc::{boxed::Box, vec};
10use core::ptr::{read_volatile, write_volatile};
11
12/// CLINT register offsets (relative to base address)
13const CLINT_MSIP_OFFSET: usize = 0x0000;     // Software interrupt pending
14const CLINT_MTIMECMP_OFFSET: usize = 0x4000; // Timer compare registers
15const CLINT_MTIME_OFFSET: usize = 0xBFF8;    // Timer value
16
17/// CLINT register stride per CPU
18const CLINT_MSIP_STRIDE: usize = 4;
19const CLINT_MTIMECMP_STRIDE: usize = 8;
20
21/// Maximum number of CPUs supported by this CLINT implementation
22const MAX_CPUS: CpuId = 4095;
23
24/// RISC-V CLINT Implementation
25pub struct Clint {
26    /// Base address of the CLINT
27    base_addr: usize,
28    /// Maximum number of CPUs this CLINT supports
29    max_cpus: CpuId,
30}
31
32impl Clint {
33    /// Create a new CLINT instance
34    /// 
35    /// # Arguments
36    /// 
37    /// * `base_addr` - Physical base address of the CLINT
38    /// * `max_cpus` - Maximum number of CPUs supported
39    /// 
40    /// The base address is used to calculate all register addresses using
41    /// relative offsets defined in the CLINT specification.
42    pub fn new(base_addr: usize, max_cpus: CpuId) -> Self {
43        Self {
44            base_addr,
45            max_cpus: max_cpus.min(MAX_CPUS),
46        }
47    }
48
49    /// Get the address of the software interrupt pending register for a CPU
50    fn msip_addr(&self, cpu_id: CpuId) -> usize {
51        self.base_addr + CLINT_MSIP_OFFSET + (cpu_id as usize * CLINT_MSIP_STRIDE)
52    }
53
54    /// Get the address of the timer compare register for a CPU
55    fn mtimecmp_addr(&self, cpu_id: CpuId) -> usize {
56        self.base_addr + CLINT_MTIMECMP_OFFSET + (cpu_id as usize * CLINT_MTIMECMP_STRIDE)
57    }
58
59    /// Get the address of the timer value register
60    fn mtime_addr(&self) -> usize {
61        self.base_addr + CLINT_MTIME_OFFSET
62    }
63
64    /// Validate CPU ID
65    fn validate_cpu_id(&self, cpu_id: CpuId) -> InterruptResult<()> {
66        if cpu_id >= self.max_cpus {
67            Err(InterruptError::InvalidCpuId)
68        } else {
69            Ok(())
70        }
71    }
72}
73
74impl LocalInterruptController for Clint {
75    /// Initialize the CLINT for a specific CPU
76    fn init(&mut self, cpu_id: CpuId) -> InterruptResult<()> {
77        self.validate_cpu_id(cpu_id)?;
78
79        // Clear software interrupt
80        self.clear_software_interrupt(cpu_id)?;
81        
82        // Set timer to maximum value (effectively disable)
83        self.set_timer(cpu_id, u64::MAX)?;
84
85        Ok(())
86    }
87
88    /// Enable a specific local interrupt type for a CPU
89    fn enable_interrupt(&mut self, cpu_id: CpuId, interrupt_type: LocalInterruptType) -> InterruptResult<()> {
90        self.validate_cpu_id(cpu_id)?;
91
92        match interrupt_type {
93            LocalInterruptType::Timer => {
94                // Timer interrupts are enabled by setting mtimecmp
95                // This is done via set_timer() method
96                Ok(())
97            }
98            LocalInterruptType::Software => {
99                // Software interrupts are enabled by setting MSIP
100                // This is done via send_software_interrupt() method
101                Ok(())
102            }
103            LocalInterruptType::External => {
104                // External interrupts are not managed by CLINT
105                Err(InterruptError::NotSupported)
106            }
107        }
108    }
109
110    /// Disable a specific local interrupt type for a CPU
111    fn disable_interrupt(&mut self, cpu_id: CpuId, interrupt_type: LocalInterruptType) -> InterruptResult<()> {
112        self.validate_cpu_id(cpu_id)?;
113
114        match interrupt_type {
115            LocalInterruptType::Timer => {
116                // Disable timer by setting mtimecmp to maximum value
117                self.set_timer(cpu_id, u64::MAX)
118            }
119            LocalInterruptType::Software => {
120                // Disable software interrupt by clearing MSIP
121                self.clear_software_interrupt(cpu_id)
122            }
123            LocalInterruptType::External => {
124                // External interrupts are not managed by CLINT
125                Err(InterruptError::NotSupported)
126            }
127        }
128    }
129
130    /// Check if a specific local interrupt type is pending for a CPU
131    fn is_pending(&self, cpu_id: CpuId, interrupt_type: LocalInterruptType) -> bool {
132        if self.validate_cpu_id(cpu_id).is_err() {
133            return false;
134        }
135
136        match interrupt_type {
137            LocalInterruptType::Timer => {
138                let current_time = self.get_time();
139                let compare_time = unsafe {
140                    read_volatile(self.mtimecmp_addr(cpu_id) as *const u64)
141                };
142                current_time >= compare_time
143            }
144            LocalInterruptType::Software => {
145                let msip = unsafe {
146                    read_volatile(self.msip_addr(cpu_id) as *const u32)
147                };
148                (msip & 1) != 0
149            }
150            LocalInterruptType::External => false, // Not managed by CLINT
151        }
152    }
153
154    /// Clear a pending local interrupt for a CPU
155    fn clear_interrupt(&mut self, cpu_id: CpuId, interrupt_type: LocalInterruptType) -> InterruptResult<()> {
156        self.validate_cpu_id(cpu_id)?;
157
158        match interrupt_type {
159            LocalInterruptType::Timer => {
160                // Clear timer interrupt by setting mtimecmp to future time
161                let current_time = self.get_time();
162                self.set_timer(cpu_id, current_time + 1000000) // 1M cycles in future
163            }
164            LocalInterruptType::Software => {
165                self.clear_software_interrupt(cpu_id)
166            }
167            LocalInterruptType::External => {
168                Err(InterruptError::NotSupported)
169            }
170        }
171    }
172
173    /// Send a software interrupt to a specific CPU
174    fn send_software_interrupt(&mut self, target_cpu: CpuId) -> InterruptResult<()> {
175        self.validate_cpu_id(target_cpu)?;
176
177        let addr = self.msip_addr(target_cpu);
178        unsafe {
179            write_volatile(addr as *mut u32, 1);
180        }
181
182        Ok(())
183    }
184
185    /// Clear a software interrupt for a specific CPU
186    fn clear_software_interrupt(&mut self, cpu_id: CpuId) -> InterruptResult<()> {
187        // self.validate_cpu_id(cpu_id)?;
188
189        // let addr = self.msip_addr(cpu_id);
190        // unsafe {
191        //     write_volatile(addr as *mut u32, 0);
192        // }
193
194        // TODO: Use SBI to clear software interrupt
195        // For now, just return Ok
196
197        Ok(())
198    }
199
200    /// Set timer interrupt for a specific CPU
201    fn set_timer(&mut self, cpu_id: CpuId, time: u64) -> InterruptResult<()> {
202        self.validate_cpu_id(cpu_id)?;
203
204        // Set the timer compare register to the specified time using SBI
205        crate::arch::riscv64::instruction::sbi::sbi_set_timer(time);
206        
207        Ok(())
208    }
209
210    /// Get current timer value
211    fn get_time(&self) -> u64 {
212        unsafe {
213            read_volatile(self.mtime_addr() as *const u64)
214        }
215    }
216}
217
218unsafe impl Send for Clint {}
219unsafe impl Sync for Clint {}
220
221fn probe_fn(device: &PlatformDeviceInfo) -> Result<(), &'static str> {
222    let res = device.get_resources();
223    if res.is_empty() {
224        return Err("No resources found");
225    }
226
227    // Get memory region resource (res_type == PlatformDeviceResourceType::MEM)
228    let mem_res = res.iter()
229        .find(|r| r.res_type == PlatformDeviceResourceType::MEM)
230        .ok_or("Memory resource not found")?;
231    
232    let base_addr = mem_res.start as usize;
233
234    // Create CLINT controller
235    let mut controller = Box::new(Clint::new(base_addr, 4)); // Example: 4 CPUs for QEMU virt
236    
237    // Initialize CLINT (Currently only initializes for CPU 0)
238    if let Err(e) = controller.init(0) {
239        crate::early_println!("[interrupt] Failed to initialize CLINT for CPU {}: {}", 0, e);
240        return Err("Failed to initialize CLINT");
241    }
242
243    // Register with InterruptManager instead of DeviceManager
244    match InterruptManager::global().lock().register_local_controller_for_range(controller, 0..4) {
245        Ok(_) => {
246            crate::early_println!("[interrupt] CLINT registered at base address: {:#x}", base_addr);
247        },
248        Err(e) => {
249            crate::early_println!("[interrupt] Failed to register CLINT: {}", e);
250            return Err("Failed to register CLINT");
251        }
252    }
253
254    Ok(())
255}
256
257fn remove_fn(_device: &PlatformDeviceInfo) -> Result<(), &'static str> {
258    Ok(())
259}
260
261fn register_driver() {
262    let driver = PlatformDeviceDriver::new(
263        "riscv-clint",
264        probe_fn,
265        remove_fn,
266        vec!["sifive,clint0", "riscv,clint0"],
267    );
268    // Register the driver with the kernel
269    DeviceManager::get_mut_manager().register_driver(Box::new(driver), DriverPriority::Critical);
270}
271
272driver_initcall!(register_driver);
273
274
275#[cfg(test)]
276mod tests {
277    use super::*;
278
279    #[test_case]
280    fn test_clint_creation() {
281        let clint = Clint::new(0x200_0000, 4);
282        assert_eq!(clint.max_cpus, 4);
283    }
284
285    #[test_case]
286    fn test_address_calculation() {
287        let clint = Clint::new(0x200_0000, 4);
288        
289        // Test MSIP addresses
290        assert_eq!(clint.msip_addr(0), 0x200_0000);
291        assert_eq!(clint.msip_addr(1), 0x200_0004);
292        assert_eq!(clint.msip_addr(3), 0x200_000C);
293        
294        // Test MTIMECMP addresses
295        assert_eq!(clint.mtimecmp_addr(0), 0x200_4000);
296        assert_eq!(clint.mtimecmp_addr(1), 0x200_4008);
297        assert_eq!(clint.mtimecmp_addr(3), 0x200_4018);
298        
299        // Test MTIME address
300        assert_eq!(clint.mtime_addr(), 0x200_BFF8);
301    }
302
303    #[test_case]
304    fn test_different_base_address() {
305        // Test with different base address to ensure base_addr is properly used
306        let clint = Clint::new(0x300_0000, 4);
307        
308        // Test MSIP addresses with different base
309        assert_eq!(clint.msip_addr(0), 0x300_0000);
310        assert_eq!(clint.msip_addr(1), 0x300_0004);
311        
312        // Test MTIMECMP addresses with different base
313        assert_eq!(clint.mtimecmp_addr(0), 0x300_4000);
314        assert_eq!(clint.mtimecmp_addr(1), 0x300_4008);
315        
316        // Test MTIME address with different base
317        assert_eq!(clint.mtime_addr(), 0x300_BFF8);
318    }
319
320    #[test_case]
321    fn test_validation() {
322        let clint = Clint::new(0x200_0000, 4);
323        
324        // Valid CPU IDs should pass
325        assert!(clint.validate_cpu_id(0).is_ok());
326        assert!(clint.validate_cpu_id(3).is_ok());
327        
328        // Invalid CPU IDs should fail
329        assert!(clint.validate_cpu_id(4).is_err());
330        assert!(clint.validate_cpu_id(100).is_err());
331    }
332}