diff options
Diffstat (limited to 'embassy-rp/src/psram.rs')
| -rw-r--r-- | embassy-rp/src/psram.rs | 220 |
1 files changed, 112 insertions, 108 deletions
diff --git a/embassy-rp/src/psram.rs b/embassy-rp/src/psram.rs index 621f9300d..190a7eb09 100644 --- a/embassy-rp/src/psram.rs +++ b/embassy-rp/src/psram.rs | |||
| @@ -10,10 +10,10 @@ | |||
| 10 | 10 | ||
| 11 | #![cfg(feature = "_rp235x")] | 11 | #![cfg(feature = "_rp235x")] |
| 12 | 12 | ||
| 13 | use critical_section::{acquire, release, CriticalSection, RestoreState}; | ||
| 14 | use embassy_hal_internal::Peri; | ||
| 15 | use crate::qmi_cs1::QmiCs1; | 13 | use crate::qmi_cs1::QmiCs1; |
| 16 | use crate::{pac, peripherals}; | 14 | use crate::{pac, peripherals}; |
| 15 | use critical_section::{acquire, release, CriticalSection, RestoreState}; | ||
| 16 | use embassy_hal_internal::Peri; | ||
| 17 | 17 | ||
| 18 | /// PSRAM errors. | 18 | /// PSRAM errors. |
| 19 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 19 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| @@ -127,25 +127,25 @@ impl Config { | |||
| 127 | /// Create configuration for APS6404L PSRAM. | 127 | /// Create configuration for APS6404L PSRAM. |
| 128 | pub fn aps6404l() -> Self { | 128 | pub fn aps6404l() -> Self { |
| 129 | Self { | 129 | Self { |
| 130 | clock_hz: 125_000_000, // Default to 125MHz | 130 | clock_hz: 125_000_000, // Default to 125MHz |
| 131 | max_mem_freq: 133_000_000, // APS6404L max frequency | 131 | max_mem_freq: 133_000_000, // APS6404L max frequency |
| 132 | max_select_us: 8, // 8 microseconds max CS assert | 132 | max_select_us: 8, // 8 microseconds max CS assert |
| 133 | min_deselect_ns: 18, // 18 nanoseconds min CS deassert | 133 | min_deselect_ns: 18, // 18 nanoseconds min CS deassert |
| 134 | cooldown: 1, // 1 SCLK cycle cooldown | 134 | cooldown: 1, // 1 SCLK cycle cooldown |
| 135 | page_break: PageBreak::_1024, // 1024-byte page boundaries | 135 | page_break: PageBreak::_1024, // 1024-byte page boundaries |
| 136 | init_clkdiv: 10, // Medium clock for initialization | 136 | init_clkdiv: 10, // Medium clock for initialization |
| 137 | enter_quad_cmd: Some(0x35), // Enter Quad Mode | 137 | enter_quad_cmd: Some(0x35), // Enter Quad Mode |
| 138 | quad_read_cmd: 0xEB, // Fast Quad Read | 138 | quad_read_cmd: 0xEB, // Fast Quad Read |
| 139 | quad_write_cmd: Some(0x38), // Quad Page Program | 139 | quad_write_cmd: Some(0x38), // Quad Page Program |
| 140 | dummy_cycles: 24, // 24 dummy cycles for quad read | 140 | dummy_cycles: 24, // 24 dummy cycles for quad read |
| 141 | read_format: FormatConfig { | 141 | read_format: FormatConfig { |
| 142 | prefix_width: Width::Quad, | 142 | prefix_width: Width::Quad, |
| 143 | addr_width: Width::Quad, | 143 | addr_width: Width::Quad, |
| 144 | suffix_width: Width::Quad, | 144 | suffix_width: Width::Quad, |
| 145 | dummy_width: Width::Quad, | 145 | dummy_width: Width::Quad, |
| 146 | data_width: Width::Quad, | 146 | data_width: Width::Quad, |
| 147 | prefix_len: true, // 8-bit prefix | 147 | prefix_len: true, // 8-bit prefix |
| 148 | suffix_len: false, // No suffix | 148 | suffix_len: false, // No suffix |
| 149 | }, | 149 | }, |
| 150 | write_format: Some(FormatConfig { | 150 | write_format: Some(FormatConfig { |
| 151 | prefix_width: Width::Quad, | 151 | prefix_width: Width::Quad, |
| @@ -153,15 +153,15 @@ impl Config { | |||
| 153 | suffix_width: Width::Quad, | 153 | suffix_width: Width::Quad, |
| 154 | dummy_width: Width::Quad, | 154 | dummy_width: Width::Quad, |
| 155 | data_width: Width::Quad, | 155 | data_width: Width::Quad, |
| 156 | prefix_len: true, // 8-bit prefix | 156 | prefix_len: true, // 8-bit prefix |
| 157 | suffix_len: false, // No suffix | 157 | suffix_len: false, // No suffix |
| 158 | }), | 158 | }), |
| 159 | mem_size: 8 * 1024 * 1024, // 8MB for APS6404L | 159 | mem_size: 8 * 1024 * 1024, // 8MB for APS6404L |
| 160 | verification_type: VerificationType::Aps6404l, | 160 | verification_type: VerificationType::Aps6404l, |
| 161 | xip_writable: true, // PSRAM is writable | 161 | xip_writable: true, // PSRAM is writable |
| 162 | } | 162 | } |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | /// Create a custom memory configuration. | 165 | /// Create a custom memory configuration. |
| 166 | pub fn custom( | 166 | pub fn custom( |
| 167 | clock_hz: u32, | 167 | clock_hz: u32, |
| @@ -236,7 +236,10 @@ impl<'d> Psram<'d> { | |||
| 236 | // Initialize PSRAM with proper timing | 236 | // Initialize PSRAM with proper timing |
| 237 | Self::init_psram(&qmi, &xip, &config)?; | 237 | Self::init_psram(&qmi, &xip, &config)?; |
| 238 | 238 | ||
| 239 | Ok(Self { qmi_cs1, size: config.mem_size }) | 239 | Ok(Self { |
| 240 | qmi_cs1, | ||
| 241 | size: config.mem_size, | ||
| 242 | }) | ||
| 240 | } | 243 | } |
| 241 | 244 | ||
| 242 | /// Get the detected PSRAM size in bytes. | 245 | /// Get the detected PSRAM size in bytes. |
| @@ -245,7 +248,7 @@ impl<'d> Psram<'d> { | |||
| 245 | } | 248 | } |
| 246 | 249 | ||
| 247 | /// Get the base address for memory-mapped access. | 250 | /// Get the base address for memory-mapped access. |
| 248 | /// | 251 | /// |
| 249 | /// After initialization, PSRAM can be accessed directly through memory mapping. | 252 | /// After initialization, PSRAM can be accessed directly through memory mapping. |
| 250 | /// The base address for CS1 is typically 0x11000000. | 253 | /// The base address for CS1 is typically 0x11000000. |
| 251 | pub fn base_address(&self) -> *mut u8 { | 254 | pub fn base_address(&self) -> *mut u8 { |
| @@ -280,14 +283,14 @@ impl<'d> Psram<'d> { | |||
| 280 | let _guard = Guard { state }; | 283 | let _guard = Guard { state }; |
| 281 | 284 | ||
| 282 | let _cs = unsafe { CriticalSection::new() }; | 285 | let _cs = unsafe { CriticalSection::new() }; |
| 283 | 286 | ||
| 284 | let qmi_base = qmi.as_ptr() as usize; | 287 | let qmi_base = qmi.as_ptr() as usize; |
| 285 | 288 | ||
| 286 | #[allow(unused_assignments)] | 289 | #[allow(unused_assignments)] |
| 287 | let mut kgd: u32 = 0; | 290 | let mut kgd: u32 = 0; |
| 288 | #[allow(unused_assignments)] | 291 | #[allow(unused_assignments)] |
| 289 | let mut eid: u32 = 0; | 292 | let mut eid: u32 = 0; |
| 290 | 293 | ||
| 291 | unsafe { | 294 | unsafe { |
| 292 | core::arch::asm!( | 295 | core::arch::asm!( |
| 293 | // Configure DIRECT_CSR: shift clkdiv (30) to bits 29:22 and set EN (bit 0) | 296 | // Configure DIRECT_CSR: shift clkdiv (30) to bits 29:22 and set EN (bit 0) |
| @@ -295,102 +298,102 @@ impl<'d> Psram<'d> { | |||
| 295 | "lsls {temp}, {temp}, #22", | 298 | "lsls {temp}, {temp}, #22", |
| 296 | "orr {temp}, {temp}, #1", // Set EN bit | 299 | "orr {temp}, {temp}, #1", // Set EN bit |
| 297 | "str {temp}, [{qmi_base}]", | 300 | "str {temp}, [{qmi_base}]", |
| 298 | 301 | ||
| 299 | // Poll for BUSY to clear before first operation | 302 | // Poll for BUSY to clear before first operation |
| 300 | "1:", | 303 | "1:", |
| 301 | "ldr {temp}, [{qmi_base}]", | 304 | "ldr {temp}, [{qmi_base}]", |
| 302 | "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position | 305 | "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position |
| 303 | "bmi 1b", // Branch if negative (BUSY = 1) | 306 | "bmi 1b", // Branch if negative (BUSY = 1) |
| 304 | 307 | ||
| 305 | // Assert CS1N (bit 3) | 308 | // Assert CS1N (bit 3) |
| 306 | "ldr {temp}, [{qmi_base}]", | 309 | "ldr {temp}, [{qmi_base}]", |
| 307 | "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit (bit 3) | 310 | "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit (bit 3) |
| 308 | "str {temp}, [{qmi_base}]", | 311 | "str {temp}, [{qmi_base}]", |
| 309 | 312 | ||
| 310 | // Transmit RESET_ENABLE_CMD as quad | 313 | // Transmit RESET_ENABLE_CMD as quad |
| 311 | // DIRECT_TX: OE=1 (bit 19), IWIDTH=2 (bits 17:16), DATA=RESET_ENABLE_CMD | 314 | // DIRECT_TX: OE=1 (bit 19), IWIDTH=2 (bits 17:16), DATA=RESET_ENABLE_CMD |
| 312 | "movs {temp}, {reset_enable_cmd}", | 315 | "movs {temp}, {reset_enable_cmd}", |
| 313 | "orr {temp}, {temp}, #0x80000", // Set OE (bit 19) | 316 | "orr {temp}, {temp}, #0x80000", // Set OE (bit 19) |
| 314 | "orr {temp}, {temp}, #0x20000", // Set IWIDTH=2 (quad, bits 17:16) | 317 | "orr {temp}, {temp}, #0x20000", // Set IWIDTH=2 (quad, bits 17:16) |
| 315 | "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX | 318 | "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX |
| 316 | 319 | ||
| 317 | // Wait for BUSY to clear | 320 | // Wait for BUSY to clear |
| 318 | "2:", | 321 | "2:", |
| 319 | "ldr {temp}, [{qmi_base}]", | 322 | "ldr {temp}, [{qmi_base}]", |
| 320 | "lsls {temp}, {temp}, #30", | 323 | "lsls {temp}, {temp}, #30", |
| 321 | "bmi 2b", | 324 | "bmi 2b", |
| 322 | 325 | ||
| 323 | // Read and discard RX data | 326 | // Read and discard RX data |
| 324 | "ldr {temp}, [{qmi_base}, #8]", | 327 | "ldr {temp}, [{qmi_base}, #8]", |
| 325 | 328 | ||
| 326 | // Deassert CS1N | 329 | // Deassert CS1N |
| 327 | "ldr {temp}, [{qmi_base}]", | 330 | "ldr {temp}, [{qmi_base}]", |
| 328 | "bic {temp}, {temp}, #8", // Clear ASSERT_CS1N bit | 331 | "bic {temp}, {temp}, #8", // Clear ASSERT_CS1N bit |
| 329 | "str {temp}, [{qmi_base}]", | 332 | "str {temp}, [{qmi_base}]", |
| 330 | 333 | ||
| 331 | // Assert CS1N again | 334 | // Assert CS1N again |
| 332 | "ldr {temp}, [{qmi_base}]", | 335 | "ldr {temp}, [{qmi_base}]", |
| 333 | "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit | 336 | "orr {temp}, {temp}, #8", // Set ASSERT_CS1N bit |
| 334 | "str {temp}, [{qmi_base}]", | 337 | "str {temp}, [{qmi_base}]", |
| 335 | 338 | ||
| 336 | // Read ID loop (7 iterations) | 339 | // Read ID loop (7 iterations) |
| 337 | "movs {counter}, #0", // Initialize counter | 340 | "movs {counter}, #0", // Initialize counter |
| 338 | 341 | ||
| 339 | "3:", // Loop start | 342 | "3:", // Loop start |
| 340 | "cmp {counter}, #0", | 343 | "cmp {counter}, #0", |
| 341 | "bne 4f", // If not first iteration, send 0xFF | 344 | "bne 4f", // If not first iteration, send 0xFF |
| 342 | 345 | ||
| 343 | // First iteration: send READ_ID_CMD | 346 | // First iteration: send READ_ID_CMD |
| 344 | "movs {temp}, {read_id_cmd}", | 347 | "movs {temp}, {read_id_cmd}", |
| 345 | "b 5f", | 348 | "b 5f", |
| 346 | 349 | ||
| 347 | "4:", // Other iterations: send 0xFF | 350 | "4:", // Other iterations: send 0xFF |
| 348 | "movs {temp}, #0xFF", | 351 | "movs {temp}, #0xFF", |
| 349 | 352 | ||
| 350 | "5:", | 353 | "5:", |
| 351 | "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX | 354 | "str {temp}, [{qmi_base}, #4]", // Store to DIRECT_TX |
| 352 | 355 | ||
| 353 | // Wait for TXEMPTY | 356 | // Wait for TXEMPTY |
| 354 | "6:", | 357 | "6:", |
| 355 | "ldr {temp}, [{qmi_base}]", | 358 | "ldr {temp}, [{qmi_base}]", |
| 356 | "lsls {temp}, {temp}, #20", // Shift TXEMPTY (bit 11) to bit 31 | 359 | "lsls {temp}, {temp}, #20", // Shift TXEMPTY (bit 11) to bit 31 |
| 357 | "bpl 6b", // Branch if positive (TXEMPTY = 0) | 360 | "bpl 6b", // Branch if positive (TXEMPTY = 0) |
| 358 | 361 | ||
| 359 | // Wait for BUSY to clear | 362 | // Wait for BUSY to clear |
| 360 | "7:", | 363 | "7:", |
| 361 | "ldr {temp}, [{qmi_base}]", | 364 | "ldr {temp}, [{qmi_base}]", |
| 362 | "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position | 365 | "lsls {temp}, {temp}, #30", // Shift BUSY bit to sign position |
| 363 | "bmi 7b", // Branch if negative (BUSY = 1) | 366 | "bmi 7b", // Branch if negative (BUSY = 1) |
| 364 | 367 | ||
| 365 | // Read RX data | 368 | // Read RX data |
| 366 | "ldr {temp}, [{qmi_base}, #8]", | 369 | "ldr {temp}, [{qmi_base}, #8]", |
| 367 | "uxth {temp}, {temp}", // Extract lower 16 bits | 370 | "uxth {temp}, {temp}", // Extract lower 16 bits |
| 368 | 371 | ||
| 369 | // Store KGD or EID based on iteration | 372 | // Store KGD or EID based on iteration |
| 370 | "cmp {counter}, #5", | 373 | "cmp {counter}, #5", |
| 371 | "bne 8f", | 374 | "bne 8f", |
| 372 | "mov {kgd}, {temp}", // Store KGD | 375 | "mov {kgd}, {temp}", // Store KGD |
| 373 | "b 9f", | 376 | "b 9f", |
| 374 | 377 | ||
| 375 | "8:", | 378 | "8:", |
| 376 | "cmp {counter}, #6", | 379 | "cmp {counter}, #6", |
| 377 | "bne 9f", | 380 | "bne 9f", |
| 378 | "mov {eid}, {temp}", // Store EID | 381 | "mov {eid}, {temp}", // Store EID |
| 379 | 382 | ||
| 380 | "9:", | 383 | "9:", |
| 381 | "adds {counter}, #1", | 384 | "adds {counter}, #1", |
| 382 | "cmp {counter}, #7", | 385 | "cmp {counter}, #7", |
| 383 | "blt 3b", // Continue loop if counter < 7 | 386 | "blt 3b", // Continue loop if counter < 7 |
| 384 | 387 | ||
| 385 | // Disable direct mode: clear EN and ASSERT_CS1N | 388 | // Disable direct mode: clear EN and ASSERT_CS1N |
| 386 | "movs {temp}, #0", | 389 | "movs {temp}, #0", |
| 387 | "str {temp}, [{qmi_base}]", | 390 | "str {temp}, [{qmi_base}]", |
| 388 | 391 | ||
| 389 | // Memory barriers | 392 | // Memory barriers |
| 390 | "dmb", | 393 | "dmb", |
| 391 | "dsb", | 394 | "dsb", |
| 392 | "isb", | 395 | "isb", |
| 393 | 396 | ||
| 394 | qmi_base = in(reg) qmi_base, | 397 | qmi_base = in(reg) qmi_base, |
| 395 | temp = out(reg) _, | 398 | temp = out(reg) _, |
| 396 | counter = out(reg) _, | 399 | counter = out(reg) _, |
| @@ -406,7 +409,8 @@ impl<'d> Psram<'d> { | |||
| 406 | if kgd == EXPECTED_KGD as u32 { | 409 | if kgd == EXPECTED_KGD as u32 { |
| 407 | detected_size = 1024 * 1024; | 410 | detected_size = 1024 * 1024; |
| 408 | let size_id = eid >> 5; | 411 | let size_id = eid >> 5; |
| 409 | if eid == 0x26 || size_id == 2 { // APS6404L-3SQR-SN or 8MB variants | 412 | if eid == 0x26 || size_id == 2 { |
| 413 | // APS6404L-3SQR-SN or 8MB variants | ||
| 410 | detected_size *= 8; | 414 | detected_size *= 8; |
| 411 | } else if size_id == 0 { | 415 | } else if size_id == 0 { |
| 412 | detected_size *= 2; | 416 | detected_size *= 2; |
| @@ -476,70 +480,70 @@ impl<'d> Psram<'d> { | |||
| 476 | let _guard = Guard { state }; | 480 | let _guard = Guard { state }; |
| 477 | 481 | ||
| 478 | let _cs = unsafe { CriticalSection::new() }; | 482 | let _cs = unsafe { CriticalSection::new() }; |
| 479 | 483 | ||
| 480 | unsafe { | 484 | unsafe { |
| 481 | core::arch::asm!( | 485 | core::arch::asm!( |
| 482 | // Full memory barrier | 486 | // Full memory barrier |
| 483 | "dmb", | 487 | "dmb", |
| 484 | "dsb", | 488 | "dsb", |
| 485 | "isb", | 489 | "isb", |
| 486 | 490 | ||
| 487 | // Configure QMI Direct CSR register | 491 | // Configure QMI Direct CSR register |
| 488 | // Load base address of QMI (0x400D0000) | 492 | // Load base address of QMI (0x400D0000) |
| 489 | "movw {base}, #0x0000", | 493 | "movw {base}, #0x0000", |
| 490 | "movt {base}, #0x400D", | 494 | "movt {base}, #0x400D", |
| 491 | 495 | ||
| 492 | // Load init_clkdiv and shift to bits 29:22 | 496 | // Load init_clkdiv and shift to bits 29:22 |
| 493 | "lsl {temp}, {clkdiv}, #22", | 497 | "lsl {temp}, {clkdiv}, #22", |
| 494 | 498 | ||
| 495 | // OR with EN (bit 0) and AUTO_CS1N (bit 7) | 499 | // OR with EN (bit 0) and AUTO_CS1N (bit 7) |
| 496 | "orr {temp}, {temp}, #0x81", | 500 | "orr {temp}, {temp}, #0x81", |
| 497 | 501 | ||
| 498 | // Store to DIRECT_CSR register | 502 | // Store to DIRECT_CSR register |
| 499 | "str {temp}, [{base}, #0]", | 503 | "str {temp}, [{base}, #0]", |
| 500 | 504 | ||
| 501 | // Memory barrier | 505 | // Memory barrier |
| 502 | "dmb", | 506 | "dmb", |
| 503 | 507 | ||
| 504 | // First busy wait loop | 508 | // First busy wait loop |
| 505 | "1:", | 509 | "1:", |
| 506 | "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR | 510 | "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR |
| 507 | "tst {temp}, #0x2", // Test BUSY bit (bit 1) | 511 | "tst {temp}, #0x2", // Test BUSY bit (bit 1) |
| 508 | "bne 1b", // Branch if busy | 512 | "bne 1b", // Branch if busy |
| 509 | 513 | ||
| 510 | // Write to Direct TX register | 514 | // Write to Direct TX register |
| 511 | "mov {temp}, {enter_quad_cmd}", | 515 | "mov {temp}, {enter_quad_cmd}", |
| 512 | 516 | ||
| 513 | // OR with NOPUSH (bit 20) | 517 | // OR with NOPUSH (bit 20) |
| 514 | "orr {temp}, {temp}, #0x100000", | 518 | "orr {temp}, {temp}, #0x100000", |
| 515 | 519 | ||
| 516 | // Store to DIRECT_TX register (offset 0x4) | 520 | // Store to DIRECT_TX register (offset 0x4) |
| 517 | "str {temp}, [{base}, #4]", | 521 | "str {temp}, [{base}, #4]", |
| 518 | 522 | ||
| 519 | // Memory barrier | 523 | // Memory barrier |
| 520 | "dmb", | 524 | "dmb", |
| 521 | 525 | ||
| 522 | // Second busy wait loop | 526 | // Second busy wait loop |
| 523 | "2:", | 527 | "2:", |
| 524 | "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR | 528 | "ldr {temp}, [{base}, #0]", // Load DIRECT_CSR |
| 525 | "tst {temp}, #0x2", // Test BUSY bit (bit 1) | 529 | "tst {temp}, #0x2", // Test BUSY bit (bit 1) |
| 526 | "bne 2b", // Branch if busy | 530 | "bne 2b", // Branch if busy |
| 527 | 531 | ||
| 528 | // Disable Direct CSR | 532 | // Disable Direct CSR |
| 529 | "mov {temp}, #0", | 533 | "mov {temp}, #0", |
| 530 | "str {temp}, [{base}, #0]", // Clear DIRECT_CSR register | 534 | "str {temp}, [{base}, #0]", // Clear DIRECT_CSR register |
| 531 | 535 | ||
| 532 | // Full memory barrier to ensure no prefetching | 536 | // Full memory barrier to ensure no prefetching |
| 533 | "dmb", | 537 | "dmb", |
| 534 | "dsb", | 538 | "dsb", |
| 535 | "isb", | 539 | "isb", |
| 536 | 540 | ||
| 537 | base = out(reg) _, | 541 | base = out(reg) _, |
| 538 | temp = out(reg) _, | 542 | temp = out(reg) _, |
| 539 | clkdiv = in(reg) config.init_clkdiv as u32, | 543 | clkdiv = in(reg) config.init_clkdiv as u32, |
| 540 | enter_quad_cmd = in(reg) u32::from(enter_quad_cmd), | 544 | enter_quad_cmd = in(reg) u32::from(enter_quad_cmd), |
| 541 | options(nostack), | 545 | options(nostack), |
| 542 | ); | 546 | ); |
| 543 | } | 547 | } |
| 544 | 548 | ||
| 545 | qmi.mem(1).timing().write(|w| { | 549 | qmi.mem(1).timing().write(|w| { |
| @@ -666,4 +670,4 @@ impl<'d> Psram<'d> { | |||
| 666 | 670 | ||
| 667 | Ok(()) | 671 | Ok(()) |
| 668 | } | 672 | } |
| 669 | } \ No newline at end of file | 673 | } |
