aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/psram.rs
diff options
context:
space:
mode:
author1-rafael-1 <[email protected]>2025-09-15 20:07:18 +0200
committer1-rafael-1 <[email protected]>2025-09-15 20:07:18 +0200
commit6bb3d2c0720fa082f27d3cdb70f516058497ec87 (patch)
tree5a1e255cff999b00800f203b91a759c720c973e5 /embassy-rp/src/psram.rs
parenteb685574601d98c44faed9a3534d056199b46e20 (diff)
parent92a6fd2946f2cbb15359290f68aa360953da2ff7 (diff)
Merge branch 'main' into rp2040-rtc-alarm
Diffstat (limited to 'embassy-rp/src/psram.rs')
-rw-r--r--embassy-rp/src/psram.rs682
1 files changed, 682 insertions, 0 deletions
diff --git a/embassy-rp/src/psram.rs b/embassy-rp/src/psram.rs
new file mode 100644
index 000000000..ae43dd5aa
--- /dev/null
+++ b/embassy-rp/src/psram.rs
@@ -0,0 +1,682 @@
1//! PSRAM driver for APS6404L and compatible devices
2//!
3//! This driver provides support for PSRAM (Pseudo-Static RAM) devices connected via QMI CS1.
4//! It handles device verification, initialization, and memory-mapped access configuration.
5//!
6//! This driver is only available on RP235x chips as it requires the QMI CS1 peripheral.
7
8// Credit: Initially based on https://github.com/Altaflux/gb-rp2350 (also licensed Apache 2.0 + MIT).
9// Copyright (c) Altaflux
10
11#![cfg(feature = "_rp235x")]
12
13use critical_section::{acquire, release, CriticalSection, RestoreState};
14
15use crate::pac;
16use crate::qmi_cs1::QmiCs1;
17
18/// PSRAM errors.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21#[non_exhaustive]
22pub enum Error {
23 /// PSRAM device is not detected or not supported
24 DeviceNotFound,
25 /// Invalid configuration
26 InvalidConfig,
27 /// Detected PSRAM size does not match the expected size
28 SizeMismatch,
29}
30
31/// PSRAM device verification type.
32#[derive(Clone, Copy)]
33pub enum VerificationType {
34 /// Skip device verification
35 None,
36 /// Verify as APS6404L device
37 Aps6404l,
38}
39
40/// Memory configuration.
41#[derive(Clone)]
42pub struct Config {
43 /// System clock frequency in Hz
44 pub clock_hz: u32,
45 /// Maximum memory operating frequency in Hz
46 pub max_mem_freq: u32,
47 /// Maximum CS assert time in microseconds (must be <= 8 us)
48 pub max_select_us: u32,
49 /// Minimum CS deassert time in nanoseconds (must be >= 18 ns)
50 pub min_deselect_ns: u32,
51 /// Cooldown period between operations (in SCLK cycles)
52 pub cooldown: u8,
53 /// Page break size for memory operations
54 pub page_break: PageBreak,
55 /// Clock divisor for direct mode operations during initialization
56 pub init_clkdiv: u8,
57 /// Enter Quad Mode command
58 pub enter_quad_cmd: Option<u8>,
59 /// Quad Read command (fast read with 4-bit data)
60 pub quad_read_cmd: u8,
61 /// Quad Write command (page program with 4-bit data)
62 pub quad_write_cmd: Option<u8>,
63 /// Number of dummy cycles for quad read operations
64 pub dummy_cycles: u8,
65 /// Read format configuration
66 pub read_format: FormatConfig,
67 /// Write format configuration
68 pub write_format: Option<FormatConfig>,
69 /// Expected memory size in bytes
70 pub mem_size: usize,
71 /// Device verification type
72 pub verification_type: VerificationType,
73 /// Whether the memory is writable via XIP (e.g., PSRAM vs. read-only flash)
74 pub xip_writable: bool,
75}
76
77/// Page break configuration for memory window operations.
78#[derive(Clone, Copy)]
79pub enum PageBreak {
80 /// No page breaks
81 None,
82 /// Break at 256-byte boundaries
83 _256,
84 /// Break at 1024-byte boundaries
85 _1024,
86 /// Break at 4096-byte boundaries
87 _4096,
88}
89
90/// Format configuration for read/write operations.
91#[derive(Clone)]
92pub struct FormatConfig {
93 /// Width of command prefix phase
94 pub prefix_width: Width,
95 /// Width of address phase
96 pub addr_width: Width,
97 /// Width of command suffix phase
98 pub suffix_width: Width,
99 /// Width of dummy/turnaround phase
100 pub dummy_width: Width,
101 /// Width of data phase
102 pub data_width: Width,
103 /// Length of prefix (None or 8 bits)
104 pub prefix_len: bool,
105 /// Length of suffix (None or 8 bits)
106 pub suffix_len: bool,
107}
108
109/// Interface width for different phases of SPI transfer.
110#[derive(Clone, Copy)]
111pub enum Width {
112 /// Single-bit (standard SPI)
113 Single,
114 /// Dual-bit (2 data lines)
115 Dual,
116 /// Quad-bit (4 data lines)
117 Quad,
118}
119
120impl Default for Config {
121 fn default() -> Self {
122 Self::aps6404l()
123 }
124}
125
126impl Config {
127 /// Create configuration for APS6404L PSRAM.
128 pub fn aps6404l() -> Self {
129 Self {
130 clock_hz: 125_000_000, // Default to 125MHz
131 max_mem_freq: 133_000_000, // APS6404L max frequency
132 max_select_us: 8, // 8 microseconds max CS assert
133 min_deselect_ns: 18, // 18 nanoseconds min CS deassert
134 cooldown: 1, // 1 SCLK cycle cooldown
135 page_break: PageBreak::_1024, // 1024-byte page boundaries
136 init_clkdiv: 10, // Medium clock for initialization
137 enter_quad_cmd: Some(0x35), // Enter Quad Mode
138 quad_read_cmd: 0xEB, // Fast Quad Read
139 quad_write_cmd: Some(0x38), // Quad Page Program
140 dummy_cycles: 24, // 24 dummy cycles for quad read
141 read_format: FormatConfig {
142 prefix_width: Width::Quad,
143 addr_width: Width::Quad,
144 suffix_width: Width::Quad,
145 dummy_width: Width::Quad,
146 data_width: Width::Quad,
147 prefix_len: true, // 8-bit prefix
148 suffix_len: false, // No suffix
149 },
150 write_format: Some(FormatConfig {
151 prefix_width: Width::Quad,
152 addr_width: Width::Quad,
153 suffix_width: Width::Quad,
154 dummy_width: Width::Quad,
155 data_width: Width::Quad,
156 prefix_len: true, // 8-bit prefix
157 suffix_len: false, // No suffix
158 }),
159 mem_size: 8 * 1024 * 1024, // 8MB for APS6404L
160 verification_type: VerificationType::Aps6404l,
161 xip_writable: true, // PSRAM is writable
162 }
163 }
164
165 /// Create a custom memory configuration.
166 pub fn custom(
167 clock_hz: u32,
168 max_mem_freq: u32,
169 max_select_us: u32,
170 min_deselect_ns: u32,
171 cooldown: u8,
172 page_break: PageBreak,
173 init_clkdiv: u8,
174 enter_quad_cmd: Option<u8>,
175 quad_read_cmd: u8,
176 quad_write_cmd: Option<u8>,
177 dummy_cycles: u8,
178 read_format: FormatConfig,
179 write_format: Option<FormatConfig>,
180 mem_size: usize,
181 verification_type: VerificationType,
182 xip_writable: bool,
183 ) -> Self {
184 Self {
185 clock_hz,
186 max_mem_freq,
187 max_select_us,
188 min_deselect_ns,
189 cooldown,
190 page_break,
191 init_clkdiv,
192 enter_quad_cmd,
193 quad_read_cmd,
194 quad_write_cmd,
195 dummy_cycles,
196 read_format,
197 write_format,
198 mem_size,
199 verification_type,
200 xip_writable,
201 }
202 }
203}
204
205/// PSRAM driver.
206pub struct Psram<'d> {
207 #[allow(dead_code)]
208 qmi_cs1: QmiCs1<'d>,
209 size: usize,
210}
211
212impl<'d> Psram<'d> {
213 /// Create a new PSRAM driver instance.
214 ///
215 /// This will detect the PSRAM device and configure it for memory-mapped access.
216 pub fn new(qmi_cs1: QmiCs1<'d>, config: Config) -> Result<Self, Error> {
217 let qmi = pac::QMI;
218 let xip = pac::XIP_CTRL;
219
220 // Verify PSRAM device if requested
221 match config.verification_type {
222 VerificationType::Aps6404l => {
223 Self::verify_aps6404l(&qmi, config.mem_size)?;
224 debug!("APS6404L PSRAM verified, size: {:#x}", config.mem_size);
225 }
226 VerificationType::None => {
227 debug!("Skipping PSRAM verification, assuming size: {:#x}", config.mem_size);
228 }
229 }
230
231 // Initialize PSRAM with proper timing
232 Self::init_psram(&qmi, &xip, &config)?;
233
234 Ok(Self {
235 qmi_cs1,
236 size: config.mem_size,
237 })
238 }
239
240 /// Get the detected PSRAM size in bytes.
241 pub fn size(&self) -> usize {
242 self.size
243 }
244
245 /// Get the base address for memory-mapped access.
246 ///
247 /// After initialization, PSRAM can be accessed directly through memory mapping.
248 /// The base address for CS1 is typically 0x11000000.
249 pub fn base_address(&self) -> *mut u8 {
250 0x1100_0000 as *mut u8
251 }
252
253 /// Verify APS6404L PSRAM device matches expected configuration.
254 #[link_section = ".data.ram_func"]
255 #[inline(never)]
256 fn verify_aps6404l(qmi: &pac::qmi::Qmi, expected_size: usize) -> Result<(), Error> {
257 // APS6404L-specific constants
258 const EXPECTED_KGD: u8 = 0x5D;
259 crate::multicore::pause_core1();
260 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
261
262 {
263 // Helper for making sure `release` is called even if `f` panics.
264 struct Guard {
265 state: RestoreState,
266 }
267
268 impl Drop for Guard {
269 #[inline(always)]
270 fn drop(&mut self) {
271 unsafe { release(self.state) }
272 }
273 }
274
275 let state = unsafe { acquire() };
276 let _guard = Guard { state };
277
278 let _cs = unsafe { CriticalSection::new() };
279
280 let (kgd, eid) = unsafe { Self::read_aps6404l_kgd_eid(qmi) };
281
282 let mut detected_size: u32 = 0;
283 if kgd == EXPECTED_KGD as u32 {
284 detected_size = 1024 * 1024;
285 let size_id = eid >> 5;
286 if eid == 0x26 || size_id == 2 {
287 // APS6404L-3SQR-SN or 8MB variants
288 detected_size *= 8;
289 } else if size_id == 0 {
290 detected_size *= 2;
291 } else if size_id == 1 {
292 detected_size *= 4;
293 }
294 }
295
296 // Verify the detected size matches the expected size
297 if detected_size as usize != expected_size {
298 return Err(Error::SizeMismatch);
299 }
300
301 Ok(())
302 }?;
303
304 crate::multicore::resume_core1();
305
306 Ok(())
307 }
308
309 #[link_section = ".data.ram_func"]
310 #[inline(never)]
311 unsafe fn read_aps6404l_kgd_eid(qmi: &pac::qmi::Qmi) -> (u32, u32) {
312 const RESET_ENABLE_CMD: u8 = 0xf5;
313 const READ_ID_CMD: u8 = 0x9f;
314
315 #[allow(unused_assignments)]
316 let mut kgd: u32 = 0;
317 #[allow(unused_assignments)]
318 let mut eid: u32 = 0;
319
320 let qmi_base = qmi.as_ptr() as usize;
321
322 #[cfg(target_arch = "arm")]
323 core::arch::asm!(
324 // Configure DIRECT_CSR: shift clkdiv (30) to bits 29:22 and set EN (bit 0)
325 "movs {temp}, #30",
326 "lsls {temp}, {temp}, #22",
327 "orr {temp}, {temp}, #1", // Set EN bit
328 "str {temp}, [{qmi_base}]",
329
330 // Poll for BUSY to clear before first operation
331 "1:",
332 "ldr {temp}, [{qmi_base}]",
333 "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position
334 "bmi 1b", // Branch if negative (BUSY = 1)
335
336 // Assert CS1N (bit 3)
337 "ldr {temp}, [{qmi_base}]",
338 "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit (bit 3)
339 "str {temp}, [{qmi_base}]",
340
341 // Transmit RESET_ENABLE_CMD as quad
342 // DIRECT_TX: OE=1 (bit 19), IWIDTH=2 (bits 17:16), DATA=RESET_ENABLE_CMD
343 "movs {temp}, {reset_enable_cmd}",
344 "orr {temp}, {temp}, #0x80000", // Set OE (bit 19)
345 "orr {temp}, {temp}, #0x20000", // Set IWIDTH=2 (quad, bits 17:16)
346 "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX
347
348 // Wait for BUSY to clear
349 "2:",
350 "ldr {temp}, [{qmi_base}]",
351 "lsls {temp}, {temp}, #30",
352 "bmi 2b",
353
354 // Read and discard RX data
355 "ldr {temp}, [{qmi_base}, #8]",
356
357 // Deassert CS1N
358 "ldr {temp}, [{qmi_base}]",
359 "bic {temp}, {temp}, #8", // Clear ASSERT_CS1N bit
360 "str {temp}, [{qmi_base}]",
361
362 // Assert CS1N again
363 "ldr {temp}, [{qmi_base}]",
364 "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit
365 "str {temp}, [{qmi_base}]",
366
367 // Read ID loop (7 iterations)
368 "movs {counter}, #0", // Initialize counter
369
370 "3:", // Loop start
371 "cmp {counter}, #0",
372 "bne 4f", // If not first iteration, send 0xFF
373
374 // First iteration: send READ_ID_CMD
375 "movs {temp}, {read_id_cmd}",
376 "b 5f",
377 "4:", // Other iterations: send 0xFF
378 "movs {temp}, #0xFF",
379 "5:",
380 "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX
381
382 // Wait for TXEMPTY
383 "6:",
384 "ldr {temp}, [{qmi_base}]",
385 "lsls {temp}, {temp}, #20", // Shift TXEMPTY (bit 11) to bit 31
386 "bpl 6b", // Branch if positive (TXEMPTY = 0)
387
388 // Wait for BUSY to clear
389 "7:",
390 "ldr {temp}, [{qmi_base}]",
391 "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position
392 "bmi 7b", // Branch if negative (BUSY = 1)
393
394 // Read RX data
395 "ldr {temp}, [{qmi_base}, #8]",
396 "uxth {temp}, {temp}", // Extract lower 16 bits
397
398 // Store KGD or EID based on iteration
399 "cmp {counter}, #5",
400 "bne 8f",
401 "mov {kgd}, {temp}", // Store KGD
402 "b 9f",
403 "8:",
404 "cmp {counter}, #6",
405 "bne 9f",
406 "mov {eid}, {temp}", // Store EID
407
408 "9:",
409 "adds {counter}, #1",
410 "cmp {counter}, #7",
411 "blt 3b", // Continue loop if counter < 7
412
413 // Disable direct mode: clear EN and ASSERT_CS1N
414 "movs {temp}, #0",
415 "str {temp}, [{qmi_base}]",
416
417 // Memory barriers
418 "dmb",
419 "dsb",
420 "isb",
421 qmi_base = in(reg) qmi_base,
422 temp = out(reg) _,
423 counter = out(reg) _,
424 kgd = out(reg) kgd,
425 eid = out(reg) eid,
426 reset_enable_cmd = const RESET_ENABLE_CMD as u32,
427 read_id_cmd = const READ_ID_CMD as u32,
428 options(nostack),
429 );
430
431 #[cfg(target_arch = "riscv32")]
432 unimplemented!("APS6404L PSRAM verification not implemented for RISC-V");
433
434 (kgd, eid)
435 }
436
437 /// Initialize PSRAM with proper timing.
438 #[link_section = ".data.ram_func"]
439 #[inline(never)]
440 fn init_psram(qmi: &pac::qmi::Qmi, xip_ctrl: &pac::xip_ctrl::XipCtrl, config: &Config) -> Result<(), Error> {
441 // Set PSRAM timing for APS6404
442 //
443 // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133 MHz.
444 // So: don't allow running at divisor 1 above 100 MHz (because delay of 2 would be too late),
445 // and add an extra 1 to the rxdelay if the divided clock is > 100 MHz (i.e., sys clock > 200 MHz).
446 let clock_hz = config.clock_hz;
447 let max_psram_freq = config.max_mem_freq;
448
449 let mut divisor: u32 = (clock_hz + max_psram_freq - 1) / max_psram_freq;
450 if divisor == 1 && clock_hz > 100_000_000 {
451 divisor = 2;
452 }
453 let mut rxdelay: u32 = divisor;
454 if clock_hz / divisor > 100_000_000 {
455 rxdelay += 1;
456 }
457
458 // - Max select must be <= 8 us. The value is given in multiples of 64 system clocks.
459 // - Min deselect must be >= 18ns. The value is given in system clock cycles - ceil(divisor / 2).
460 let clock_period_fs: u64 = 1_000_000_000_000_000_u64 / u64::from(clock_hz);
461 let max_select: u8 = ((config.max_select_us as u64 * 1_000_000) / clock_period_fs) as u8;
462 let min_deselect: u32 = ((config.min_deselect_ns as u64 * 1_000_000 + (clock_period_fs - 1)) / clock_period_fs
463 - u64::from(divisor + 1) / 2) as u32;
464
465 crate::multicore::pause_core1();
466 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
467
468 if let Some(enter_quad_cmd) = config.enter_quad_cmd {
469 // Helper for making sure `release` is called even if `f` panics.
470 struct Guard {
471 state: RestoreState,
472 }
473
474 impl Drop for Guard {
475 #[inline(always)]
476 fn drop(&mut self) {
477 unsafe { release(self.state) }
478 }
479 }
480
481 let state = unsafe { acquire() };
482 let _guard = Guard { state };
483
484 let _cs = unsafe { CriticalSection::new() };
485
486 unsafe { Self::direct_csr_send_init_command(config, enter_quad_cmd) };
487
488 qmi.mem(1).timing().write(|w| {
489 w.set_cooldown(config.cooldown);
490 w.set_pagebreak(match config.page_break {
491 PageBreak::None => pac::qmi::vals::Pagebreak::NONE,
492 PageBreak::_256 => pac::qmi::vals::Pagebreak::_256,
493 PageBreak::_1024 => pac::qmi::vals::Pagebreak::_1024,
494 PageBreak::_4096 => pac::qmi::vals::Pagebreak::_4096,
495 });
496 w.set_max_select(max_select);
497 w.set_min_deselect(min_deselect as u8);
498 w.set_rxdelay(rxdelay as u8);
499 w.set_clkdiv(divisor as u8);
500 });
501
502 // Set PSRAM commands and formats
503 qmi.mem(1).rfmt().write(|w| {
504 let width_to_pac = |w: Width| match w {
505 Width::Single => pac::qmi::vals::PrefixWidth::S,
506 Width::Dual => pac::qmi::vals::PrefixWidth::D,
507 Width::Quad => pac::qmi::vals::PrefixWidth::Q,
508 };
509
510 w.set_prefix_width(width_to_pac(config.read_format.prefix_width));
511 w.set_addr_width(match config.read_format.addr_width {
512 Width::Single => pac::qmi::vals::AddrWidth::S,
513 Width::Dual => pac::qmi::vals::AddrWidth::D,
514 Width::Quad => pac::qmi::vals::AddrWidth::Q,
515 });
516 w.set_suffix_width(match config.read_format.suffix_width {
517 Width::Single => pac::qmi::vals::SuffixWidth::S,
518 Width::Dual => pac::qmi::vals::SuffixWidth::D,
519 Width::Quad => pac::qmi::vals::SuffixWidth::Q,
520 });
521 w.set_dummy_width(match config.read_format.dummy_width {
522 Width::Single => pac::qmi::vals::DummyWidth::S,
523 Width::Dual => pac::qmi::vals::DummyWidth::D,
524 Width::Quad => pac::qmi::vals::DummyWidth::Q,
525 });
526 w.set_data_width(match config.read_format.data_width {
527 Width::Single => pac::qmi::vals::DataWidth::S,
528 Width::Dual => pac::qmi::vals::DataWidth::D,
529 Width::Quad => pac::qmi::vals::DataWidth::Q,
530 });
531 w.set_prefix_len(if config.read_format.prefix_len {
532 pac::qmi::vals::PrefixLen::_8
533 } else {
534 pac::qmi::vals::PrefixLen::NONE
535 });
536 w.set_suffix_len(if config.read_format.suffix_len {
537 pac::qmi::vals::SuffixLen::_8
538 } else {
539 pac::qmi::vals::SuffixLen::NONE
540 });
541 w.set_dummy_len(match config.dummy_cycles {
542 0 => pac::qmi::vals::DummyLen::NONE,
543 4 => pac::qmi::vals::DummyLen::_4,
544 8 => pac::qmi::vals::DummyLen::_8,
545 12 => pac::qmi::vals::DummyLen::_12,
546 16 => pac::qmi::vals::DummyLen::_16,
547 20 => pac::qmi::vals::DummyLen::_20,
548 24 => pac::qmi::vals::DummyLen::_24,
549 28 => pac::qmi::vals::DummyLen::_28,
550 _ => pac::qmi::vals::DummyLen::_24, // Default to 24
551 });
552 });
553
554 qmi.mem(1).rcmd().write(|w| w.set_prefix(config.quad_read_cmd));
555
556 if let Some(ref write_format) = config.write_format {
557 qmi.mem(1).wfmt().write(|w| {
558 w.set_prefix_width(match write_format.prefix_width {
559 Width::Single => pac::qmi::vals::PrefixWidth::S,
560 Width::Dual => pac::qmi::vals::PrefixWidth::D,
561 Width::Quad => pac::qmi::vals::PrefixWidth::Q,
562 });
563 w.set_addr_width(match write_format.addr_width {
564 Width::Single => pac::qmi::vals::AddrWidth::S,
565 Width::Dual => pac::qmi::vals::AddrWidth::D,
566 Width::Quad => pac::qmi::vals::AddrWidth::Q,
567 });
568 w.set_suffix_width(match write_format.suffix_width {
569 Width::Single => pac::qmi::vals::SuffixWidth::S,
570 Width::Dual => pac::qmi::vals::SuffixWidth::D,
571 Width::Quad => pac::qmi::vals::SuffixWidth::Q,
572 });
573 w.set_dummy_width(match write_format.dummy_width {
574 Width::Single => pac::qmi::vals::DummyWidth::S,
575 Width::Dual => pac::qmi::vals::DummyWidth::D,
576 Width::Quad => pac::qmi::vals::DummyWidth::Q,
577 });
578 w.set_data_width(match write_format.data_width {
579 Width::Single => pac::qmi::vals::DataWidth::S,
580 Width::Dual => pac::qmi::vals::DataWidth::D,
581 Width::Quad => pac::qmi::vals::DataWidth::Q,
582 });
583 w.set_prefix_len(if write_format.prefix_len {
584 pac::qmi::vals::PrefixLen::_8
585 } else {
586 pac::qmi::vals::PrefixLen::NONE
587 });
588 w.set_suffix_len(if write_format.suffix_len {
589 pac::qmi::vals::SuffixLen::_8
590 } else {
591 pac::qmi::vals::SuffixLen::NONE
592 });
593 });
594 }
595
596 if let Some(quad_write_cmd) = config.quad_write_cmd {
597 qmi.mem(1).wcmd().write(|w| w.set_prefix(quad_write_cmd));
598 }
599
600 if config.xip_writable {
601 // Enable XIP writable mode for PSRAM
602 xip_ctrl.ctrl().modify(|w| w.set_writable_m1(true));
603 } else {
604 // Disable XIP writable mode
605 xip_ctrl.ctrl().modify(|w| w.set_writable_m1(false));
606 }
607 }
608 crate::multicore::resume_core1();
609
610 Ok(())
611 }
612
613 #[link_section = ".data.ram_func"]
614 #[inline(never)]
615 unsafe fn direct_csr_send_init_command(config: &Config, init_cmd: u8) {
616 #[cfg(target_arch = "arm")]
617 core::arch::asm!(
618 // Full memory barrier
619 "dmb",
620 "dsb",
621 "isb",
622
623 // Configure QMI Direct CSR register
624 // Load base address of QMI (0x400D0000)
625 "movw {base}, #0x0000",
626 "movt {base}, #0x400D",
627
628 // Load init_clkdiv and shift to bits 29:22
629 "lsl {temp}, {clkdiv}, #22",
630
631 // OR with EN (bit 0) and AUTO_CS1N (bit 7)
632 "orr {temp}, {temp}, #0x81",
633
634 // Store to DIRECT_CSR register
635 "str {temp}, [{base}, #0]",
636
637 // Memory barrier
638 "dmb",
639
640 // First busy wait loop
641 "1:",
642 "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR
643 "tst {temp}, #0x2", // Test BUSY bit (bit 1)
644 "bne 1b", // Branch if busy
645
646 // Write to Direct TX register
647 "mov {temp}, {enter_quad_cmd}",
648
649 // OR with NOPUSH (bit 20)
650 "orr {temp}, {temp}, #0x100000",
651
652 // Store to DIRECT_TX register (offset 0x4)
653 "str {temp}, [{base}, #4]",
654
655 // Memory barrier
656 "dmb",
657
658 // Second busy wait loop
659 "2:",
660 "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR
661 "tst {temp}, #0x2", // Test BUSY bit (bit 1)
662 "bne 2b", // Branch if busy
663
664 // Disable Direct CSR
665 "mov {temp}, #0",
666 "str {temp}, [{base}, #0]", // Clear DIRECT_CSR register
667
668 // Full memory barrier to ensure no prefetching
669 "dmb",
670 "dsb",
671 "isb",
672 base = out(reg) _,
673 temp = out(reg) _,
674 clkdiv = in(reg) config.init_clkdiv as u32,
675 enter_quad_cmd = in(reg) u32::from(init_cmd),
676 options(nostack),
677 );
678
679 #[cfg(target_arch = "riscv32")]
680 unimplemented!("Direct CSR command sending is not implemented for RISC-V yet");
681 }
682}