diff --git a/Cargo.lock b/Cargo.lock index 56a52b2..b405680 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,6 +128,12 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bit-set" version = "0.5.3" @@ -310,6 +316,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "const-default" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" + [[package]] name = "cortex-m" version = "0.7.7" @@ -880,6 +892,18 @@ dependencies = [ "embedded-io-async", ] +[[package]] +name = "embedded-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd" +dependencies = [ + "const-default", + "critical-section", + "linked_list_allocator", + "rlsf", +] + [[package]] name = "embedded-graphics" version = "0.8.1" @@ -1459,6 +1483,7 @@ dependencies = [ "embassy-sync 0.7.2", "embassy-time 0.5.0", "embassy-usb", + "embedded-alloc", "embedded-graphics", "embedded-hal 0.2.7", "embedded-hal 1.0.0", @@ -1580,6 +1605,12 @@ dependencies = [ "libc", ] +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -2065,6 +2096,18 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "rlsf" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf" +dependencies = [ + "cfg-if", + "const-default", + "libc", + "svgbobdoc", +] + [[package]] name = "rp-pac" version = "7.0.0" @@ -2351,6 +2394,19 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "svgbobdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" +dependencies = [ + "base64", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-width", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 5f3e4be..ef81558 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "3" members = [ "kernel", + "abi_sys", "abi", "user-apps/calculator", "user-apps/snake", diff --git a/README.md b/README.md index 9bffec0..2d4d4e0 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,6 @@ git clone https://github.com/LegitCamper/picocalc-os-rs.git cd picocalc-os-rs just userapps # copy the build applications from target/thumbv8m.main-none-eabihf/release-binary/application to the sdcard and rename them to app.bin -just kernel-release # keep in mind that https://github.com/StripedMonkey/elf2uf2-rs version is required until https://github.com/JoNil/elf2uf2-rs/pull/41 is merged + +# has builds for the official rp2350 board and the pimoroni2w board +just kernel-release rp235x # keep in mind that https://github.com/StripedMonkey/elf2uf2-rs version is required until https://github.com/JoNil/elf2uf2-rs/pull/41 is merged diff --git a/justfile b/justfile index 0f1a419..2175ae6 100644 --- a/justfile +++ b/justfile @@ -1,7 +1,7 @@ -kernel-dev: - cargo run --bin kernel -kernel-release: - cargo build --bin kernel --release +kernel-dev board: + cargo run --bin kernel --features {{board}} +kernel-release board: + cargo build --bin kernel --release --no-default-features --features {{board}} elf2uf2-rs -d target/thumbv8m.main-none-eabihf/release/kernel binary-args := "RUSTFLAGS=\"-C link-arg=-pie -C relocation-model=pic\"" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 299866d..b2f237a 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -12,6 +12,7 @@ bench = false [features] default = ["rp235x", "defmt"] +pimoroni2w = ["rp235x"] rp2040 = ["embassy-rp/rp2040"] rp235x = ["embassy-rp/rp235xb"] trouble = ["dep:bt-hci", "dep:cyw43", "dep:cyw43-pio", "dep:trouble-host"] @@ -89,6 +90,7 @@ spin = "0.10.0" num_enum = { version = "0.7.4", default-features = false } goblin = { version = "0.10.1", default-features = false, features = ["elf32"] } talc = "4.4.3" +embedded-alloc = "0.6.0" bumpalo = "3.19.0" abi_sys = { path = "../abi_sys" } diff --git a/kernel/build.rs b/kernel/build.rs index 30691aa..8220df0 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -13,13 +13,18 @@ use std::fs::File; use std::io::Write; use std::path::PathBuf; +#[cfg(all(feature = "rp235x", not(feature = "pimoroni2w")))] +const MEMORY: &'static [u8] = include_bytes!("rp2350.x"); +#[cfg(feature = "pimoroni2w")] +const MEMORY: &'static [u8] = include_bytes!("rp2350.x"); + fn main() { // Put `memory.x` in our output directory and ensure it's // on the linker search path. let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); File::create(out.join("memory.x")) .unwrap() - .write_all(include_bytes!("memory.x")) + .write_all(MEMORY) .unwrap(); println!("cargo:rustc-link-search={}", out.display()); diff --git a/kernel/pimoroni2w.x b/kernel/pimoroni2w.x new file mode 100644 index 0000000..56e741f --- /dev/null +++ b/kernel/pimoroni2w.x @@ -0,0 +1,60 @@ +MEMORY { + FLASH : ORIGIN = 0x10000000, LENGTH = 16M - 4K + + RAM : ORIGIN = 0x20000000, LENGTH = 512K + SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K + SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K +} + +SECTIONS { + /* ### Boot ROM info + * + * Goes after .vector_table, to keep it in the first 4K of flash + * where the Boot ROM (and picotool) can find it + */ + .start_block : ALIGN(4) + { + __start_block_addr = .; + KEEP(*(.start_block)); + KEEP(*(.boot_info)); + } > FLASH + +} INSERT AFTER .vector_table; + +/* move .text to start /after/ the boot info */ +_stext = ADDR(.start_block) + SIZEOF(.start_block); + +SECTIONS { + /* ### Picotool 'Binary Info' Entries + * + * Picotool looks through this block (as we have pointers to it in our + * header) to find interesting information. + */ + .bi_entries : ALIGN(4) + { + /* We put this in the header */ + __bi_entries_start = .; + /* Here are the entries */ + KEEP(*(.bi_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __bi_entries_end = .; + } > FLASH +} INSERT AFTER .text; + +SECTIONS { + /* ### Boot ROM extra info + * + * Goes after everything in our program, so it can contain a signature. + */ + .end_block : ALIGN(4) + { + __end_block_addr = .; + KEEP(*(.end_block)); + } > FLASH + +} INSERT AFTER .uninit; + +PROVIDE(start_to_end = __end_block_addr - __start_block_addr); +PROVIDE(end_to_start = __start_block_addr - __end_block_addr); diff --git a/kernel/memory.x b/kernel/rp2350.x similarity index 100% rename from kernel/memory.x rename to kernel/rp2350.x diff --git a/kernel/src/abi.rs b/kernel/src/abi.rs index 3bed7c0..027b021 100644 --- a/kernel/src/abi.rs +++ b/kernel/src/abi.rs @@ -20,9 +20,11 @@ pub extern "C" fn print(ptr: *const u8, len: usize) { // SAFETY: caller guarantees `ptr` is valid for `len` bytes let slice = unsafe { core::slice::from_raw_parts(ptr, len) }; - if let Ok(msg) = core::str::from_utf8(slice) { - defmt::info!("print: {}", msg); + if let Ok(_msg) = core::str::from_utf8(slice) { + #[cfg(feature = "defmt")] + defmt::info!("print: {}", _msg); } else { + #[cfg(feature = "defmt")] defmt::warn!("print: "); } } diff --git a/kernel/src/heap.rs b/kernel/src/heap.rs new file mode 100644 index 0000000..c4f444c --- /dev/null +++ b/kernel/src/heap.rs @@ -0,0 +1,132 @@ +// This whole file was taken from: +// https://github.com/wezterm/picocalc-wezterm/blob/main/src/heap.rs + +use core::alloc::{GlobalAlloc, Layout}; +use core::mem::MaybeUninit; +use core::sync::atomic::{AtomicUsize, Ordering}; +use embedded_alloc::LlffHeap as Heap; + +#[global_allocator] +pub static HEAP: DualHeap = DualHeap::empty(); +const HEAP_SIZE: usize = 64 * 1024; +static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + +struct Region { + start: AtomicUsize, + size: AtomicUsize, +} + +impl Region { + const fn default() -> Self { + Self { + start: AtomicUsize::new(0), + size: AtomicUsize::new(0), + } + } + + fn contains(&self, address: usize) -> bool { + let start = self.start.load(Ordering::Relaxed); + let end = self.start.load(Ordering::Relaxed); + (start..start + end).contains(&address) + } + + fn new(start: usize, size: usize) -> Self { + Self { + start: AtomicUsize::new(start), + size: AtomicUsize::new(size), + } + } +} + +/// This is an allocator that combines two regions of memory. +/// The intent is to use some of the directly connected RAM +/// for this, and if we find some XIP capable PSRAM, add that +/// as a secondary region. +/// Allocation from the primary region is always preferred, +/// as it is expected to be a bit faster than PSRAM. +/// FIXME: PSRAM-allocated memory isn't compatible with +/// CAS atomics, so we might need a bit of a think about this! +pub struct DualHeap { + primary: Heap, + primary_region: Region, + secondary: Heap, +} + +impl DualHeap { + pub const fn empty() -> Self { + Self { + primary: Heap::empty(), + primary_region: Region::default(), + secondary: Heap::empty(), + } + } + + unsafe fn add_primary(&self, region: Region) { + let start = region.start.load(Ordering::SeqCst); + let size = region.size.load(Ordering::SeqCst); + unsafe { + self.primary.init(start, size); + } + self.primary_region.start.store(start, Ordering::SeqCst); + self.primary_region.size.store(size, Ordering::SeqCst); + } + + unsafe fn add_secondary(&self, region: Region) { + let start = region.start.load(Ordering::SeqCst); + let size = region.size.load(Ordering::SeqCst); + unsafe { + self.secondary.init(start, size); + } + } + + pub fn used(&self) -> usize { + self.primary.used() + self.secondary.used() + } + + pub fn free(&self) -> usize { + self.primary.free() + self.secondary.free() + } +} + +unsafe impl GlobalAlloc for DualHeap { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + unsafe { + let ptr = self.primary.alloc(layout); + if !ptr.is_null() { + return ptr; + } + // start using secondary area when primary heap is full + self.secondary.alloc(layout) + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + unsafe { + let ptr_usize = ptr as usize; + if self.primary_region.contains(ptr_usize) { + self.primary.dealloc(ptr, layout); + } else { + self.secondary.dealloc(ptr, layout); + } + } + } +} + +pub fn init_heap() { + let primary_start = &raw mut HEAP_MEM as usize; + unsafe { HEAP.add_primary(Region::new(primary_start, HEAP_SIZE)) } +} + +pub fn init_qmi_psram_heap(size: u32) { + unsafe { HEAP.add_secondary(Region::new(0x11000000, size as usize)) } +} + +pub async fn free_command(_args: &[&str]) { + let ram_used = HEAP.primary.used(); + let ram_free = HEAP.primary.free(); + let ram_total = ram_used + ram_free; + + let qmi_used = HEAP.secondary.used(); + let qmi_free = HEAP.secondary.free(); + let qmi_total = qmi_used + qmi_free; +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index a98bd02..887b65d 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -10,6 +10,8 @@ mod abi; mod display; mod elf; mod framebuffer; +#[cfg(feature = "pimoroni2w")] +mod heap; mod peripherals; mod psram; mod scsi; @@ -18,6 +20,9 @@ mod ui; mod usb; mod utils; +#[cfg(feature = "pimoroni2w")] +use crate::{heap::init_qmi_psram_heap, psram::init_psram_qmi}; + use crate::{ abi::{KEY_CACHE, MS_SINCE_LAUNCH}, display::{FRAMEBUFFER, display_handler, init_display}, @@ -40,7 +45,6 @@ use embedded_graphics::{ use {defmt_rtt as _, panic_probe as _}; use core::sync::atomic::{AtomicBool, Ordering}; -use defmt::unwrap; use embassy_executor::{Executor, Spawner}; use embassy_futures::{join::join, select::select}; use embassy_rp::{ @@ -76,8 +80,10 @@ static mut CORE1_STACK: Stack<16384> = Stack::new(); static EXECUTOR0: StaticCell = StaticCell::new(); static EXECUTOR1: StaticCell = StaticCell::new(); +#[cfg(not(feature = "pimoroni2w"))] static mut ARENA: [u8; 200 * 1024] = [0; 200 * 1024]; +#[cfg(not(feature = "pimoroni2w"))] #[global_allocator] static ALLOCATOR: Talck, ClaimOnOom> = Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) }) @@ -95,7 +101,7 @@ async fn main(_spawner: Spawner) { unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, move || { let executor1 = EXECUTOR1.init(Executor::new()); - executor1.run(|spawner| unwrap!(spawner.spawn(userland_task()))); + executor1.run(|spawner| spawner.spawn(userland_task()).unwrap()); }, ); @@ -134,7 +140,9 @@ async fn main(_spawner: Spawner) { }; let executor0 = EXECUTOR0.init(Executor::new()); executor0.run(|spawner| { - unwrap!(spawner.spawn(kernel_task(spawner, display, sd, psram, mcu, p.USB))) + spawner + .spawn(kernel_task(spawner, display, sd, psram, mcu, p.USB)) + .unwrap() }); } @@ -159,6 +167,7 @@ async fn userland_task() { } unsafe { MS_SINCE_LAUNCH = Some(Instant::now()) }; + #[cfg(feature = "defmt")] defmt::info!("Executing Binary"); entry(); @@ -238,11 +247,21 @@ async fn setup_psram(psram: Psram) { ) .await; + #[cfg(feature = "defmt")] defmt::info!("psram size: {}", psram.size); if psram.size == 0 { + #[cfg(feature = "defmt")] defmt::info!("\u{1b}[1mExternal PSRAM was NOT found!\u{1b}[0m"); } + + #[cfg(feature = "pimoroni2w")] + { + let psram_qmi_size = init_psram_qmi(&embassy_rp::pac::QMI, &embassy_rp::pac::XIP_CTRL); + if psram_qmi_size > 0 { + init_qmi_psram_heap(psram_qmi_size); + } + } } async fn setup_sd(sd: Sd) { diff --git a/kernel/src/psram.rs b/kernel/src/psram.rs index 881b3d7..9145090 100644 --- a/kernel/src/psram.rs +++ b/kernel/src/psram.rs @@ -4,6 +4,7 @@ use crate::Irqs; use embassy_futures::yield_now; use embassy_rp::Peri; +use embassy_rp::clocks::clk_peri_freq; use embassy_rp::gpio::{Drive, SlewRate}; use embassy_rp::peripherals::{DMA_CH3, DMA_CH4, PIN_2, PIN_3, PIN_20, PIN_21, PIO0}; use embassy_rp::pio::program::pio_asm; @@ -20,6 +21,10 @@ use embassy_time::{Duration, Instant, Timer}; // RAM_IO2 - PIN_4 SIO2 (QPI Mode) // RAM_IO3 - PIN_5 SIO3 (QPI Mode) +#[allow(unused)] +const PSRAM_CMD_QUAD_END: u8 = 0xf5; +#[allow(unused)] +const PSRAM_CMD_QUAD_ENABLE: u8 = 0x35; #[allow(unused)] const PSRAM_CMD_READ_ID: u8 = 0x9F; const PSRAM_CMD_RSTEN: u8 = 0x66; @@ -27,11 +32,15 @@ const PSRAM_CMD_RST: u8 = 0x99; const PSRAM_CMD_WRITE: u8 = 0x02; const PSRAM_CMD_FAST_READ: u8 = 0x0B; #[allow(unused)] +const PSRAM_CMD_QUAD_READ: u8 = 0xEB; +#[allow(unused)] +const PSRAM_CMD_QUAD_WRITE: u8 = 0x38; +#[allow(unused)] const PSRAM_CMD_NOOP: u8 = 0xFF; #[allow(unused)] const PSRAM_KNOWN_GOOD_DIE_PASS: u8 = 0x5d; -const SPEED: u32 = 133_000_000; +const MAX_PSRAM_FREQ: u32 = 133_000_000; pub struct PsRam { sm: embassy_rp::pio::StateMachine<'static, PIO0, 0>, @@ -179,7 +188,7 @@ pub async fn init_psram( ) -> PsRam { let mut pio = Pio::new(pio, Irqs); - let divider = calculate_pio_clock_divider(SPEED); + let divider = calculate_pio_clock_divider(MAX_PSRAM_FREQ); // This pio program was taken from // @@ -257,6 +266,7 @@ done: psram.send_command(&[8, 0, PSRAM_CMD_RST], &mut []).await; Timer::after(Duration::from_micros(100)).await; + #[cfg(feature = "defmt")] defmt::info!("Verifying 1 byte write and read..."); for i in 0..10u8 { psram.write8(i as u32, i).await; @@ -265,18 +275,22 @@ done: let n = psram.read8(i as u32).await; if n as u32 != i {} } + #[cfg(feature = "defmt")] defmt::info!("testing read again @ 0"); let mut got = [0u8; 8]; psram.read(0, &mut got).await; const EXPECT: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7]; if got != EXPECT { + #[cfg(feature = "defmt")] defmt::warn!("Got Read error"); } const DEADBEEF: &[u8] = &[0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf]; + #[cfg(feature = "defmt")] defmt::info!("testing write of deadbeef at 0"); psram.write(0, DEADBEEF).await; + #[cfg(feature = "defmt")] defmt::info!("testing read of deadbeef from 0"); psram.read(0, &mut got).await; if got != DEADBEEF { @@ -284,6 +298,7 @@ done: let bad = got[addr]; if bad != DEADBEEF[addr] { let x = psram.read8(addr as u32).await; + #[cfg(feature = "defmt")] defmt::info!("read addr: {}, got: {:X}", addr, x); } } @@ -299,9 +314,11 @@ done: if got != TEST_STRING {} + #[cfg(feature = "defmt")] defmt::info!("PSRAM test complete"); let id = psram.read_id().await; + #[cfg(feature = "defmt")] defmt::info!("psram id: {}", id); // id: [d, 5d, 53, 15, 49, e3, 7c, 7b] // id[0] -- manufacturer id @@ -355,6 +372,7 @@ pub async fn test_psram(psram: &mut PsRam) -> bool { } let writes_took = start.elapsed(); + #[cfg(feature = "defmt")] defmt::info!("Starting reads..."); Timer::after(Duration::from_millis(200)).await; @@ -383,3 +401,223 @@ pub async fn test_psram(psram: &mut PsRam) -> bool { bad_count == 0 } + +// The origin of the code in this file is: +// +// which is MIT/Apache-2 licensed. +#[unsafe(link_section = ".data")] +#[inline(never)] +pub fn detect_psram_qmi(qmi: &embassy_rp::pac::qmi::Qmi) -> u32 { + const GPIO_FUNC_XIP_CS1: u8 = 9; + const XIP_CS_PIN: usize = 47; + embassy_rp::pac::PADS_BANK0.gpio(XIP_CS_PIN).modify(|w| { + w.set_iso(true); + }); + embassy_rp::pac::PADS_BANK0.gpio(XIP_CS_PIN).modify(|w| { + w.set_ie(true); + w.set_od(false); + }); + embassy_rp::pac::IO_BANK0 + .gpio(XIP_CS_PIN) + .ctrl() + .write(|w| w.set_funcsel(GPIO_FUNC_XIP_CS1)); + embassy_rp::pac::PADS_BANK0.gpio(XIP_CS_PIN).modify(|w| { + w.set_iso(false); + }); + + critical_section::with(|_cs| { + // Try and read the PSRAM ID via direct_csr. + qmi.direct_csr().write(|w| { + w.set_clkdiv(30); + w.set_en(true); + }); + + // Need to poll for the cooldown on the last XIP transfer to expire + // (via direct-mode BUSY flag) before it is safe to perform the first + // direct-mode operation + while qmi.direct_csr().read().busy() { + // rp235x_hal::arch::nop(); + } + + // Exit out of QMI in case we've inited already + qmi.direct_csr().modify(|w| w.set_assert_cs1n(true)); + + // Transmit the command to exit QPI quad mode - read ID as standard SPI + // Transmit as quad. + qmi.direct_tx().write(|w| { + w.set_oe(true); + w.set_iwidth(embassy_rp::pac::qmi::vals::Iwidth::Q); + w.set_data(PSRAM_CMD_QUAD_END.into()); + }); + + while qmi.direct_csr().read().busy() { + // rp235x_hal::arch::nop(); + } + + let _ = qmi.direct_rx().read(); + + qmi.direct_csr().modify(|w| { + w.set_assert_cs1n(false); + }); + + // Read the id + qmi.direct_csr().modify(|w| { + w.set_assert_cs1n(true); + }); + + // kgd is "known good die" + let mut kgd: u16 = 0; + let mut eid: u16 = 0; + for i in 0usize..7 { + qmi.direct_tx().write(|w| { + w.set_data(if i == 0 { + PSRAM_CMD_READ_ID.into() + } else { + PSRAM_CMD_NOOP.into() + }) + }); + + while !qmi.direct_csr().read().txempty() { + // rp235x_hal::arch::nop(); + } + + while qmi.direct_csr().read().busy() { + // rp235x_hal::arch::nop(); + } + + let value = qmi.direct_rx().read().direct_rx(); + match i { + 5 => { + kgd = value; + } + 6 => { + eid = value; + } + _ => {} + } + } + + qmi.direct_csr().modify(|w| { + w.set_assert_cs1n(false); + w.set_en(false); + }); + let mut param_size: u32 = 0; + if kgd == PSRAM_KNOWN_GOOD_DIE_PASS as u16 { + param_size = 1024 * 1024; + let size_id = eid >> 5; + if eid == 0x26 || size_id == 2 { + param_size *= 8; + } else if size_id == 0 { + param_size *= 2; + } else if size_id == 1 { + param_size *= 4; + } + } + param_size + }) +} + +#[unsafe(link_section = ".data")] +#[inline(never)] +pub fn init_psram_qmi( + qmi: &embassy_rp::pac::qmi::Qmi, + xip: &embassy_rp::pac::xip_ctrl::XipCtrl, +) -> u32 { + let psram_size = detect_psram_qmi(qmi); + + if psram_size == 0 { + return 0; + } + + // Set PSRAM timing for APS6404 + // + // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133MHz. + // So: don't allow running at divisor 1 above 100MHz (because delay of 2 would be too late), + // and add an extra 1 to the rxdelay if the divided clock is > 100MHz (i.e. sys clock > 200MHz). + const MAX_PSRAM_FREQ: u32 = 133_000_000; + + let clock_hz = clk_peri_freq(); + + let mut divisor: u32 = (clock_hz + MAX_PSRAM_FREQ - 1) / MAX_PSRAM_FREQ; + if divisor == 1 && clock_hz > 100_000_000 { + divisor = 2; + } + let mut rxdelay: u32 = divisor; + if clock_hz / divisor > 100_000_000 { + rxdelay += 1; + } + + // - Max select must be <= 8us. The value is given in multiples of 64 system clocks. + // - Min deselect must be >= 18ns. The value is given in system clock cycles - ceil(divisor / 2). + let clock_period_fs: u64 = 1_000_000_000_000_000_u64 / u64::from(clock_hz); + let max_select: u8 = ((125 * 1_000_000) / clock_period_fs) as u8; + let min_deselect: u32 = ((18 * 1_000_000 + (clock_period_fs - 1)) / clock_period_fs + - u64::from(divisor + 1) / 2) as u32; + + #[cfg(feature = "defmt")] + defmt::info!( + "clock_period_fs={} max_select={} min_deselect={}", + clock_period_fs, + max_select, + min_deselect + ); + + qmi.direct_csr().write(|w| { + w.set_clkdiv(10); + w.set_en(true); + w.set_auto_cs1n(true); + }); + + while qmi.direct_csr().read().busy() { + // rp235x_hal::arch::nop(); + } + + qmi.direct_tx().write(|w| { + w.set_nopush(true); + w.0 = 0x35; + }); + + while qmi.direct_csr().read().busy() { + // rp235x_hal::arch::nop(); + } + + qmi.mem(1).timing().write(|w| { + w.set_cooldown(1); + w.set_pagebreak(embassy_rp::pac::qmi::vals::Pagebreak::_1024); + w.set_max_select(max_select as u8); + w.set_min_deselect(min_deselect as u8); + w.set_rxdelay(rxdelay as u8); + w.set_clkdiv(divisor as u8); + }); + + // // Set PSRAM commands and formats + qmi.mem(1).rfmt().write(|w| { + w.set_prefix_width(embassy_rp::pac::qmi::vals::PrefixWidth::Q); + w.set_addr_width(embassy_rp::pac::qmi::vals::AddrWidth::Q); + w.set_suffix_width(embassy_rp::pac::qmi::vals::SuffixWidth::Q); + w.set_dummy_width(embassy_rp::pac::qmi::vals::DummyWidth::Q); + w.set_data_width(embassy_rp::pac::qmi::vals::DataWidth::Q); + w.set_prefix_len(embassy_rp::pac::qmi::vals::PrefixLen::_8); + w.set_dummy_len(embassy_rp::pac::qmi::vals::DummyLen::_24); + }); + + qmi.mem(1).rcmd().write(|w| w.0 = 0xEB); + + qmi.mem(1).wfmt().write(|w| { + w.set_prefix_width(embassy_rp::pac::qmi::vals::PrefixWidth::Q); + w.set_addr_width(embassy_rp::pac::qmi::vals::AddrWidth::Q); + w.set_suffix_width(embassy_rp::pac::qmi::vals::SuffixWidth::Q); + w.set_dummy_width(embassy_rp::pac::qmi::vals::DummyWidth::Q); + w.set_data_width(embassy_rp::pac::qmi::vals::DataWidth::Q); + w.set_prefix_len(embassy_rp::pac::qmi::vals::PrefixLen::_8); + }); + + qmi.mem(1).wcmd().write(|w| w.0 = 0x38); + + // Disable direct mode + qmi.direct_csr().write(|w| w.0 = 0); + + // Enable writes to PSRAM + xip.ctrl().modify(|w| w.set_writable_m1(true)); + psram_size +} diff --git a/kernel/src/scsi/mod.rs b/kernel/src/scsi/mod.rs index b61acee..9618563 100644 --- a/kernel/src/scsi/mod.rs +++ b/kernel/src/scsi/mod.rs @@ -54,6 +54,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { select(self.handle_cbw(), MSC_SHUTDOWN.wait()).await; if MSC_SHUTDOWN.signaled() { + #[cfg(feature = "defmt")] defmt::info!("MSC shutting down"); if self.temp_sd.is_some() { @@ -80,6 +81,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { if let Some(sd) = guard.take() { self.temp_sd = Some(sd); } else { + #[cfg(feature = "defmt")] defmt::warn!("Tried to take SDCARD but it was already taken"); return; } @@ -363,6 +365,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { } pub async fn send_csw_fail(&mut self, tag: u32) { + #[cfg(feature = "defmt")] defmt::error!("Command Failed: {}", tag); self.send_csw(tag, 0x01, 0).await; // 0x01 = Command Failed }