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 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}