Skip to main content

k230_kpu/
lib.rs

1#![no_std]
2
3use core::{hint::spin_loop, ptr};
4
5pub const KPU_CFG_PADDR: usize = 0x8040_0000;
6pub const KPU_CFG_SIZE: usize = 0x800;
7pub const KPU_L2_PADDR: usize = 0x8000_0000;
8pub const KPU_L2_SIZE: usize = 0x20_0000;
9pub const KPU_IRQ: usize = 189;
10pub const KPU_FAKE_OUTPUT_PADDR: usize = 0x1009_0000;
11pub const KPU_FAKE_OUTPUT_SIZE: usize = 0x10_0000;
12pub const KPU_RUNTIME_RDATA_PADDR: usize = 0x1000_0000;
13pub const KPU_RUNTIME_RDATA_SIZE: usize = 0x9_0000;
14pub const KPU_RUNTIME_COMMAND_PADDR: usize = 0x1019_0000;
15pub const KPU_RUNTIME_COMMAND_SIZE: usize = 0x37_0000;
16pub const KPU_RUNTIME_DIRECT_IO_PADDR: usize = 0x1050_0000;
17pub const KPU_RUNTIME_DIRECT_IO_SIZE: usize = 0xb0_0000;
18pub const KPU_RUNTIME_DDR_PADDR: usize = 0x3c00_0000;
19pub const KPU_RUNTIME_DDR_SIZE: usize = 0x400_0000;
20
21pub const KPU_RUNTIME_RDATA_BASE: usize = 0x1000_0020;
22pub const KPU_RUNTIME_FUNCTION_COMMAND_PADDR: usize = 0x1032_b020;
23pub const KPU_RUNTIME_ARG_TABLE_PADDR: usize = KPU_L2_PADDR;
24pub const KPU_RUNTIME_DIRECT_SOURCE_PADDR: usize = 0x1050_0020;
25pub const KPU_RUNTIME_DIRECT_OUTPUT_PADDR: usize = 0x1050_1020;
26
27pub const COMMAND_START: usize = 0x100;
28pub const COMMAND_END: usize = 0x104;
29pub const COMMAND_HI: usize = 0x108;
30pub const CONTROL: usize = 0x128;
31pub const STATUS_LO: usize = 0x130;
32pub const STATUS_HI: usize = 0x134;
33
34pub const CONTROL_CLEAR: u32 = 0x4;
35pub const CONTROL_START: u32 = 0x9;
36pub const DONE_STATUS: u64 = 0x0000_0004_0000_0004;
37
38pub const KPU_IOC_GET_STATUS: u32 = 0x4b00;
39pub const KPU_IOC_CLEAR: u32 = 0x4b01;
40pub const KPU_IOC_PROGRAM_COMMAND: u32 = 0x4b02;
41pub const KPU_IOC_START: u32 = 0x4b03;
42pub const KPU_IOC_RUN: u32 = 0x4b04;
43pub const KPU_IOC_WAIT_DONE: u32 = 0x4b05;
44pub const KPU_IOC_GET_INFO: u32 = 0x4b06;
45pub const KPU_IOC_GET_IRQ_COUNT: u32 = 0x4b07;
46
47pub const KPU_MMAP_CFG_OFFSET: u64 = 0;
48pub const KPU_MMAP_L2_OFFSET: u64 = 0x1000;
49pub const KPU_MMAP_FAKE_OUTPUT_OFFSET: u64 = 0x2000;
50pub const KPU_MMAP_RUNTIME_RDATA_OFFSET: u64 = 0x3000;
51pub const KPU_MMAP_RUNTIME_COMMAND_OFFSET: u64 = 0x4000;
52pub const KPU_MMAP_RUNTIME_DIRECT_IO_OFFSET: u64 = 0x5000;
53pub const KPU_MMAP_RUNTIME_DDR_OFFSET: u64 = 0x6000;
54
55pub const KPU_IRQ_NONE: u32 = u32::MAX;
56pub const KPU_INFO_F_FDT: u32 = 0x1;
57pub const KPU_INFO_F_IRQ_WAIT: u32 = 0x2;
58pub const KPU_INFO_F_FAKE_OUTPUT: u32 = 0x4;
59pub const KPU_INFO_F_RUNTIME_SCRATCH: u32 = 0x8;
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum Error {
63    CommandRangeCrosses4G,
64    CommandRangeEmpty,
65    TimedOut,
66}
67
68#[repr(C)]
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub struct CommandRange {
71    pub start_paddr: u64,
72    pub end_paddr: u64,
73}
74
75#[repr(C)]
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub struct KpuInfo {
78    pub cfg_paddr: u64,
79    pub cfg_size: u64,
80    pub l2_paddr: u64,
81    pub l2_size: u64,
82    pub irq: u32,
83    pub flags: u32,
84}
85
86#[derive(Debug, Clone, Copy)]
87pub struct Kpu {
88    base_vaddr: usize,
89}
90
91impl Kpu {
92    /// # Safety
93    ///
94    /// `base_vaddr` must point to a valid mapped K230 KPU CFG MMIO window.
95    pub const unsafe fn new(base_vaddr: usize) -> Self {
96        Self { base_vaddr }
97    }
98
99    pub fn program_command(&self, range: CommandRange) -> Result<(), Error> {
100        let (start, end, hi) = command_words(range)?;
101        self.write_reg(COMMAND_START, start);
102        self.write_reg(COMMAND_END, end);
103        self.write_reg(COMMAND_HI, hi);
104        Ok(())
105    }
106
107    pub fn run_command(&self, range: CommandRange) -> Result<(), Error> {
108        self.clear_done();
109        self.program_command(range)?;
110        self.start();
111        Ok(())
112    }
113
114    pub fn clear_done(&self) {
115        self.write_reg(CONTROL, CONTROL_CLEAR);
116    }
117
118    pub fn start(&self) {
119        self.write_reg(CONTROL, CONTROL_START);
120    }
121
122    pub fn status(&self) -> u64 {
123        let lo = self.read_reg(STATUS_LO) as u64;
124        let hi = self.read_reg(STATUS_HI) as u64;
125        (hi << 32) | lo
126    }
127
128    pub fn is_done(&self) -> bool {
129        self.status() & DONE_STATUS == DONE_STATUS
130    }
131
132    pub fn wait_done(&self, poll_limit: usize) -> Result<(), Error> {
133        for _ in 0..poll_limit {
134            if self.is_done() {
135                return Ok(());
136            }
137            spin_loop();
138        }
139        Err(Error::TimedOut)
140    }
141
142    pub fn read_reg(&self, offset: usize) -> u32 {
143        unsafe { ptr::read_volatile((self.base_vaddr + offset) as *const u32) }
144    }
145
146    pub fn write_reg(&self, offset: usize, value: u32) {
147        unsafe { ptr::write_volatile((self.base_vaddr + offset) as *mut u32, value) }
148    }
149}
150
151pub fn command_words(range: CommandRange) -> Result<(u32, u32, u32), Error> {
152    if range.start_paddr >= range.end_paddr {
153        return Err(Error::CommandRangeEmpty);
154    }
155    if range.start_paddr >> 32 != range.end_paddr >> 32 {
156        return Err(Error::CommandRangeCrosses4G);
157    }
158    Ok((
159        range.start_paddr as u32,
160        range.end_paddr as u32,
161        (range.start_paddr >> 32) as u32,
162    ))
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn command_words_accept_same_4g_window() {
171        assert_eq!(
172            command_words(CommandRange {
173                start_paddr: 0x1_0000_1000,
174                end_paddr: 0x1_0000_2000,
175            }),
176            Ok((0x1000, 0x2000, 0x1))
177        );
178    }
179
180    #[test]
181    fn command_words_reject_empty_range() {
182        assert_eq!(
183            command_words(CommandRange {
184                start_paddr: 0x1000,
185                end_paddr: 0x1000,
186            }),
187            Err(Error::CommandRangeEmpty)
188        );
189    }
190
191    #[test]
192    fn command_words_reject_crossing_4g_window() {
193        assert_eq!(
194            command_words(CommandRange {
195                start_paddr: 0xffff_ff00,
196                end_paddr: 0x1_0000_0100,
197            }),
198            Err(Error::CommandRangeCrosses4G)
199        );
200    }
201
202    #[test]
203    fn uapi_layout_and_constants_are_stable() {
204        assert_eq!(core::mem::size_of::<CommandRange>(), 16);
205        assert_eq!(core::mem::offset_of!(CommandRange, start_paddr), 0);
206        assert_eq!(core::mem::offset_of!(CommandRange, end_paddr), 8);
207
208        assert_eq!(KPU_IOC_GET_STATUS, 0x4b00);
209        assert_eq!(KPU_IOC_CLEAR, 0x4b01);
210        assert_eq!(KPU_IOC_PROGRAM_COMMAND, 0x4b02);
211        assert_eq!(KPU_IOC_START, 0x4b03);
212        assert_eq!(KPU_IOC_RUN, 0x4b04);
213        assert_eq!(KPU_IOC_WAIT_DONE, 0x4b05);
214        assert_eq!(KPU_IOC_GET_INFO, 0x4b06);
215        assert_eq!(KPU_IOC_GET_IRQ_COUNT, 0x4b07);
216        assert_eq!(KPU_MMAP_CFG_OFFSET, 0);
217        assert_eq!(KPU_MMAP_L2_OFFSET, 0x1000);
218        assert_eq!(KPU_MMAP_FAKE_OUTPUT_OFFSET, 0x2000);
219        assert_eq!(KPU_MMAP_RUNTIME_RDATA_OFFSET, 0x3000);
220        assert_eq!(KPU_MMAP_RUNTIME_COMMAND_OFFSET, 0x4000);
221        assert_eq!(KPU_MMAP_RUNTIME_DIRECT_IO_OFFSET, 0x5000);
222        assert_eq!(KPU_MMAP_RUNTIME_DDR_OFFSET, 0x6000);
223        assert_eq!(KPU_CFG_PADDR, 0x8040_0000);
224        assert_eq!(KPU_L2_PADDR, 0x8000_0000);
225        assert_eq!(KPU_FAKE_OUTPUT_PADDR, 0x1009_0000);
226        assert_eq!(KPU_RUNTIME_RDATA_PADDR, 0x1000_0000);
227        assert_eq!(KPU_RUNTIME_COMMAND_PADDR, 0x1019_0000);
228        assert_eq!(KPU_RUNTIME_DIRECT_IO_PADDR, 0x1050_0000);
229        assert_eq!(KPU_RUNTIME_DDR_PADDR, 0x3c00_0000);
230        assert_eq!(KPU_RUNTIME_RDATA_BASE, 0x1000_0020);
231        assert_eq!(KPU_RUNTIME_FUNCTION_COMMAND_PADDR, 0x1032_b020);
232        assert_eq!(KPU_RUNTIME_ARG_TABLE_PADDR, 0x8000_0000);
233        assert_eq!(KPU_RUNTIME_DIRECT_SOURCE_PADDR, 0x1050_0020);
234        assert_eq!(KPU_RUNTIME_DIRECT_OUTPUT_PADDR, 0x1050_1020);
235        assert_eq!(KPU_CFG_SIZE, 0x800);
236        assert_eq!(KPU_L2_SIZE, 0x20_0000);
237        assert_eq!(KPU_FAKE_OUTPUT_SIZE, 0x10_0000);
238        assert_eq!(KPU_RUNTIME_RDATA_SIZE, 0x9_0000);
239        assert_eq!(KPU_RUNTIME_COMMAND_SIZE, 0x37_0000);
240        assert_eq!(KPU_RUNTIME_DIRECT_IO_SIZE, 0xb0_0000);
241        assert_eq!(KPU_RUNTIME_DDR_SIZE, 0x400_0000);
242        assert_eq!(KPU_IRQ_NONE, u32::MAX);
243        assert_eq!(KPU_INFO_F_FDT, 0x1);
244        assert_eq!(KPU_INFO_F_IRQ_WAIT, 0x2);
245        assert_eq!(KPU_INFO_F_FAKE_OUTPUT, 0x4);
246        assert_eq!(KPU_INFO_F_RUNTIME_SCRATCH, 0x8);
247        assert_eq!(COMMAND_START, 0x100);
248        assert_eq!(COMMAND_END, 0x104);
249        assert_eq!(COMMAND_HI, 0x108);
250        assert_eq!(CONTROL, 0x128);
251        assert_eq!(STATUS_LO, 0x130);
252        assert_eq!(STATUS_HI, 0x134);
253        assert_eq!(DONE_STATUS, 0x0000_0004_0000_0004);
254
255        assert_eq!(core::mem::size_of::<KpuInfo>(), 40);
256        assert_eq!(core::mem::offset_of!(KpuInfo, cfg_paddr), 0);
257        assert_eq!(core::mem::offset_of!(KpuInfo, cfg_size), 8);
258        assert_eq!(core::mem::offset_of!(KpuInfo, l2_paddr), 16);
259        assert_eq!(core::mem::offset_of!(KpuInfo, l2_size), 24);
260        assert_eq!(core::mem::offset_of!(KpuInfo, irq), 32);
261        assert_eq!(core::mem::offset_of!(KpuInfo, flags), 36);
262    }
263}