From 914a00553c175f6cf1180633781af1f369e48e17 Mon Sep 17 00:00:00 2001 From: Ivan-Velickovic Date: Fri, 28 Feb 2025 14:58:48 +1100 Subject: [PATCH 01/48] SDMMC: Recreated this branch for breaking changes in network subsystem impacting my fileio compiling Signed-off-by: Cheng --- .gitignore | 2 + drivers/blk/sdmmc/Cargo.toml | 12 + drivers/blk/sdmmc/build.rs | 10 + drivers/blk/sdmmc/rust-toolchain.toml | 9 + drivers/blk/sdmmc/sdmmc_driver.mk | 43 +++ drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml | 12 + drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs | 3 + .../blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 317 ++++++++++++++++++ drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml | 8 + drivers/blk/sdmmc/sdmmc_protocol/lib.rs | 3 + drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 311 +++++++++++++++++ .../sdmmc_protocol/sdmmc/sdmmc_constant.rs | 49 +++ drivers/blk/sdmmc/src/main.rs | 220 ++++++++++++ drivers/blk/sdmmc/src/sddf_blk/mod.rs | 26 ++ drivers/blk/sdmmc/src/sddf_helper.c | 43 +++ .../aarch64-sel4-microkit-minimal.json | 37 ++ 16 files changed, 1105 insertions(+) create mode 100644 drivers/blk/sdmmc/Cargo.toml create mode 100644 drivers/blk/sdmmc/build.rs create mode 100644 drivers/blk/sdmmc/rust-toolchain.toml create mode 100644 drivers/blk/sdmmc/sdmmc_driver.mk create mode 100644 drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml create mode 100644 drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs create mode 100644 drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/lib.rs create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs create mode 100644 drivers/blk/sdmmc/src/main.rs create mode 100644 drivers/blk/sdmmc/src/sddf_blk/mod.rs create mode 100644 drivers/blk/sdmmc/src/sddf_helper.c create mode 100644 drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json diff --git a/.gitignore b/.gitignore index 607bbf772..7781b50be 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,8 @@ ci_logs/ zig-out/ .zig-cache/ venv/ +target/ +Cargo.lock __pycache__ .*.sw* *.dtb diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml new file mode 100644 index 000000000..92dfd157c --- /dev/null +++ b/drivers/blk/sdmmc/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "sdmmc_driver" +version = "0.1.0" +edition = "2021" + +[dependencies] +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d3790bfd15512e18659f9491c319867fabf9552d", package = "sel4-microkit" } +sdmmc_hal = { path = "sdmmc_hal/meson" } +sdmmc_protocol = { path = "sdmmc_protocol" } + +[build] +build = "build.rs" \ No newline at end of file diff --git a/drivers/blk/sdmmc/build.rs b/drivers/blk/sdmmc/build.rs new file mode 100644 index 000000000..66c23f2cb --- /dev/null +++ b/drivers/blk/sdmmc/build.rs @@ -0,0 +1,10 @@ +fn main() { + // Specify the path where the C library is located + println!("cargo:rustc-link-search=native=./"); + + // Link the C library (static or dynamic). Adjust "static" or "dylib" as needed. + println!("cargo:rustc-link-lib=static=sddfblk"); + + // If you need to specify the include directory for C headers: + // println!("cargo:include=path/to/your/c/include"); +} \ No newline at end of file diff --git a/drivers/blk/sdmmc/rust-toolchain.toml b/drivers/blk/sdmmc/rust-toolchain.toml new file mode 100644 index 000000000..84a9735bf --- /dev/null +++ b/drivers/blk/sdmmc/rust-toolchain.toml @@ -0,0 +1,9 @@ +# +# Copyright 2023, Colias Group, LLC +# +# SPDX-License-Identifier: BSD-2-Clause +# + +[toolchain] +channel = "nightly-2024-05-01" +components = [ "rustfmt", "rust-src", "rustc-dev", "llvm-tools-preview" ] diff --git a/drivers/blk/sdmmc/sdmmc_driver.mk b/drivers/blk/sdmmc/sdmmc_driver.mk new file mode 100644 index 000000000..ca169eb8a --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_driver.mk @@ -0,0 +1,43 @@ +# +# Copyright 2023, Colias Group, LLC +# +# SPDX-License-Identifier: BSD-2-Clause +# + + +# Get current dir +SDMMC_DRIVER_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) + +# Allow for different build configurations (default is debug) +microkit_sdk_config_dir := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) +sel4_include_dirs := $(microkit_sdk_config_dir)/include + +# Ensure build directory exists +$(BUILD_DIR): + @echo "Creating build directory $(BUILD_DIR)..." + mkdir -p $@ + +blk/sdmmc/meson/sddf_helper.o: $(SDMMC_DRIVER_DIR)/src/sddf_helper.c |blk/sdmmc/meson + $(CC) -c $(CFLAGS) $< -o $@ + +blk/sdmmc/meson/libsddfblk.a: blk/sdmmc/meson/sddf_helper.o |blk/sdmmc/meson + ar rcs $@ $< + +blk/sdmmc/meson: + mkdir -p $@ + +# Main build target +sdmmc_driver.elf: $(BUILD_DIR) blk/sdmmc/meson/libsddfblk.a + cp blk/sdmmc/meson/libsddfblk.a $(abspath ${SDMMC_DRIVER_DIR}) + @cd $(abspath ${SDMMC_DRIVER_DIR}) && \ + echo "Building sdmmc_driver.elf for board $(MICROKIT_BOARD)..." && \ + echo "MICROKIT SDK config directory: $(microkit_sdk_config_dir)" && \ + echo "SEl4 include directories: $(sel4_include_dirs)" && \ + SEL4_INCLUDE_DIRS=$(abspath $(sel4_include_dirs)) \ + cargo build \ + -Z build-std=core,alloc,compiler_builtins \ + -Z build-std-features=compiler-builtins-mem \ + --target-dir $(BUILD_DIR)/blk/sdmmc/meson/ \ + --target support/targets/aarch64-sel4-microkit-minimal.json && \ + cp $(BUILD_DIR)/blk/sdmmc/meson/aarch64-sel4-microkit-minimal/debug/sdmmc_driver.elf $(BUILD_DIR) + echo "Build complete: $(TARGET_ELF)" \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml b/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml new file mode 100644 index 000000000..6bbae1511 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "sdmmc_hal" +version = "0.1.0" +edition = "2021" + +[lib] +name = "sdmmc_hal" +path = "lib.rs" + +[dependencies] +sdmmc_protocol = { path = "../../sdmmc_protocol" } +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d3790bfd15512e18659f9491c319867fabf9552d", package = "sel4-microkit" } \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs new file mode 100644 index 000000000..9ed5d73d3 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs @@ -0,0 +1,3 @@ +#![no_std] // Don't link the standard library + +pub mod meson_gx_mmc; \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs new file mode 100644 index 000000000..1801feca6 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs @@ -0,0 +1,317 @@ +use core::ptr; + +use sdmmc_protocol::sdmmc::{MmcData, MmcDataFlag, SdmmcCmd, SdmmcHalError, SdmmcHardware}; +use sel4_microkit::debug_println; + +const SDIO_BASE: u64 = 0xffe05000; // Base address from DTS + +// Constants translated from the C version +// Clock related constant +const SD_EMMC_CLKSRC_24M: u32 = 24000000; // 24 MHz +const SD_EMMC_CLKSRC_DIV2: u32 = 1000000000; // 1 GHz + +const CLK_MAX_DIV: u32 = 63; +const CLK_SRC_24M: u32 = 0 << 6; +const CLK_SRC_DIV2: u32 = 1 << 6; +const CLK_CO_PHASE_000: u32 = 0 << 8; +const CLK_CO_PHASE_090: u32 = 1 << 8; +const CLK_CO_PHASE_180: u32 = 2 << 8; +const CLK_CO_PHASE_270: u32 = 3 << 8; +const CLK_TX_PHASE_000: u32 = 0 << 10; +const CLK_TX_PHASE_180: u32 = 2 << 10; +const CLK_ALWAYS_ON: u32 = 1 << 24; + +macro_rules! div_round_up { + ($n:expr, $d:expr) => { + (($n + $d - 1) / $d) + }; +} + +// CMD_CFG constants +const CMD_CFG_CMD_INDEX_SHIFT: u32 = 24; +const CMD_CFG_RESP_128: u32 = 1 << 21; +const CMD_CFG_R1B: u32 = 1 << 10; +const CMD_CFG_RESP_NOCRC: u32 = 1 << 20; +const CMD_CFG_NO_RESP: u32 = 1 << 16; +const CMD_CFG_DATA_WR: u32 = 1 << 19; +const CMD_CFG_DATA_IO: u32 = 1 << 18; +const CMD_CFG_BLOCK_MODE: u32 = 1 << 9; +const CMD_CFG_TIMEOUT_4S: u32 = 12 << 12; +const CMD_CFG_OWNER: u32 = 1 << 31; +const CMD_CFG_END_OF_CHAIN: u32 = 1 << 11; + +// MMC_RSP constants +const MMC_RSP_PRESENT: u32 = 1 << 0; +const MMC_RSP_136: u32 = 1 << 1; +const MMC_RSP_CRC: u32 = 1 << 2; +const MMC_RSP_BUSY: u32 = 1 << 3; + +// STATUS register masks and flags +const STATUS_MASK: u32 = 0xFFFF; // GENMASK(15, 0) +const STATUS_ERR_MASK: u32 = 0x1FFF; // GENMASK(12, 0) +const STATUS_RXD_ERR_MASK: u32 = 0xFF; // GENMASK(7, 0) +const STATUS_TXD_ERR: u32 = 1 << 8; // BIT(8) +const STATUS_DESC_ERR: u32 = 1 << 9; // BIT(9) +const STATUS_RESP_ERR: u32 = 1 << 10; // BIT(10) +const STATUS_RESP_TIMEOUT: u32 = 1 << 11; // BIT(11) +const STATUS_DESC_TIMEOUT: u32 = 1 << 12; // BIT(12) +const STATUS_END_OF_CHAIN: u32 = 1 << 13; // BIT(13) + +// Configuration constants (assuming based on context) +const CFG_BL_LEN_MASK: u32 = 0xF << 4; // Bits 4-7 +const CFG_BL_LEN_SHIFT: u32 = 4; + +const MESON_SDCARD_SECTOR_SIZE: u32 = 512; + +pub const MAX_BLOCK_PER_TRANSFER:u32 = 0xFF; + +const WRITE_ADDR_UPPER: u32 = 0xFFFE0000; + +// const VALID_DMA_ADDR_LOWER: u32 = 0x2000000; +// const VALID_DMA_ADDR_UPPER: u32 = 0x10000000; +// const VALID_DMA_ADDR_MASK: u32 = 0x80000003; + +fn ilog2(x: u32) -> u32 { + assert!(x > 0); + 31 - x.leading_zeros() +} + +// Structure representing the SDIO controller's registers +/* + * Those register mapping are taken from meson-gx-mmc.c in Linux source code, + * meson_gx_mmc.h in uboot source code and S905X3 datasheet. + * Despite Odroid C4 belong to Meson GX Family, the sdmmc register mapping + * seems to be the same with the register mapping for meson_axg according to documentation + * and the register mapping defined in Linux kernel. + * + * #define MESON_SD_EMMC_CLOCK 0x00 + * #define SD_EMMC_START 0x40 + * #define MESON_SD_EMMC_CFG 0x44 + * #define MESON_SD_EMMC_STATUS 0x48 + * #define MESON_SD_EMMC_IRQ_EN 0x4c + * #define MESON_SD_EMMC_CMD_CFG 0x50 + * #define MESON_SD_EMMC_CMD_ARG 0x54 + * #define MESON_SD_EMMC_CMD_DAT 0x58 + * #define MESON_SD_EMMC_CMD_RSP 0x5c + * #define MESON_SD_EMMC_CMD_RSP1 0x60 + * #define MESON_SD_EMMC_CMD_RSP2 0x64 + * #define MESON_SD_EMMC_CMD_RSP3 0x68 + * #define SD_EMMC_RXD 0x94 + * #define SD_EMMC_TXD 0x94 + * #define SD_EMMC_LAST_REG SD_EMMC_TXD + */ +// I think I find a bug in Linux, the odroid C4 delay register mapping are the same with meson-axg but it belongs to meson-gx +/* + * There are some assumptions that I have made for this driver: + * 1. The card is already powered on by U-Boot, so I do not need to manually manipulate + * gpio pins or regulator to turn it on or off. + * 2. The clocks are already enabled by U-Boot and there is no implicit clock shutdown when the uboot start to run + * my image, so I do not need to turn on the clocks that the sd card needs by myself. + * + */ +#[repr(C)] +pub struct MesonSdmmcRegisters { + pub clock: u32, // 0x00: Clock control register + _reserved0: [u32; 15], // Padding for other unused registers (0x04 - 0x3C) + pub start: u32, // 0x40: Start register + pub cfg: u32, // 0x44: Configuration register + pub status: u32, // 0x48: Status register + pub irq_en: u32, // 0x4C: Interrupt enable register + pub cmd_cfg: u32, // 0x50: Command configuration register + pub cmd_arg: u32, // 0x54: Command argument register + pub cmd_dat: u32, // 0x58: Command data register (for DMA address) + pub cmd_rsp: u32, // 0x5C: Command response register + pub cmd_rsp1: u32, // 0x60: Command response register 1 + pub cmd_rsp2: u32, // 0x64: Command response register 2 + pub cmd_rsp3: u32, // 0x68: Command response register 3 + _reserved1: [u32; 9], // Padding for other unused registers (0x6C - 0x90) + pub rxd: u32, // 0x94: Receive data register (not used) + pub txd: u32, // 0x94: Transmit data register (not used, same as RXD) + // Add other registers as needed +} + +impl Unpin for MesonSdmmcRegisters {} + +impl MesonSdmmcRegisters { + /// This new use unsafe under the hood, ensure correct memory page is mapped into + /// the respective virtual memory address and do not do things stupid + pub fn new() -> &'static mut MesonSdmmcRegisters { + unsafe { &mut *(SDIO_BASE as *mut MesonSdmmcRegisters) } + } + + /// Configures the SDIO clock based on the requested clock frequency and SoC type. + /// + /// # Arguments + /// + /// * `mmc_clock` - The desired clock frequency in Hz. + /// * `is_sm1_soc` - A boolean indicating whether the SoC is an SM1 variant. + /// * For odorid C4, this is_sm1_soc is true + pub fn meson_mmc_config_clock(&mut self) { + // #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + let mut meson_mmc_clk:u32 = 0; + + // Valid clock freq range: + // f_min = div_round_up!(SD_EMMC_CLKSRC_24M, CLK_MAX_DIV); + // f_max = 100000000; /* 100 MHz */ + let clk: u32; + let clk_src: u32; + // 400 khz for init the card + let clock_freq: u32 = 400000; + if clock_freq > 16000000 { + clk = SD_EMMC_CLKSRC_DIV2; + clk_src = CLK_SRC_DIV2; + } else { + clk = SD_EMMC_CLKSRC_24M; + clk_src = CLK_SRC_24M; + } + + let clk_div = div_round_up!(clk, clock_freq); + /* + * From uboot meson_gx_mmc.c + * SM1 SoCs doesn't work fine over 50MHz with CLK_CO_PHASE_180 + * If CLK_CO_PHASE_270 is used, it's more stable than other. + * Other SoCs use CLK_CO_PHASE_180 by default. + * It needs to find what is a proper value about each SoCs. + * Since we are using Odroid C4, we set phase to 270 + */ + meson_mmc_clk |= CLK_CO_PHASE_270; + meson_mmc_clk |= CLK_TX_PHASE_000; + + meson_mmc_clk |= clk_src; + meson_mmc_clk |= clk_div; + + unsafe { ptr::write_volatile(&mut self.clock, meson_mmc_clk); } + } + + // This function can be seen as a Rust version of meson_mmc_setup_cmd function in uboot + fn meson_mmc_set_up_cmd_cfg_and_cfg(&mut self, cmd: &SdmmcCmd, data: Option<&MmcData>) { + let mut meson_mmc_cmd: u32 = 0u32; + + meson_mmc_cmd |= (cmd.cmdidx & 0x3F) << CMD_CFG_CMD_INDEX_SHIFT; + + if cmd.resp_type & MMC_RSP_PRESENT != 0 { + if cmd.resp_type & MMC_RSP_136 != 0 { + meson_mmc_cmd |= CMD_CFG_RESP_128; + } + + if cmd.resp_type & MMC_RSP_BUSY != 0 { + meson_mmc_cmd |= CMD_CFG_R1B; + } + + if cmd.resp_type & MMC_RSP_CRC == 0 { + meson_mmc_cmd |= CMD_CFG_RESP_NOCRC; + } + } + else { + meson_mmc_cmd |= CMD_CFG_NO_RESP; + } + + if let Some(data) = data { + let mut cfg: u32 = unsafe { ptr::read_volatile(&self.cfg) }; + + cfg &= !CFG_BL_LEN_MASK; + + cfg |= ilog2(data.blocksize) << CFG_BL_LEN_SHIFT; + + // This value should only be 9 + assert!(ilog2(data.blocksize) == 9); + + unsafe { ptr::write_volatile(&mut self.cfg, cfg); }; + + if let MmcDataFlag::SdmmcDataWrite = data.flags { + meson_mmc_cmd |= CMD_CFG_DATA_WR; + } + + meson_mmc_cmd |= CMD_CFG_DATA_IO | CMD_CFG_BLOCK_MODE | data.blockcnt; + } + + meson_mmc_cmd |= CMD_CFG_TIMEOUT_4S | CMD_CFG_OWNER | CMD_CFG_END_OF_CHAIN; + + unsafe { ptr::write_volatile(&mut self.cmd_cfg, meson_mmc_cmd); } + } + + fn meson_read_response(&self, cmd: &SdmmcCmd, response: &mut [u32; 4]) { + let [rsp0, rsp1, rsp2, rsp3] = response; + + // Assign values by reading the respective registers + if cmd.resp_type & MMC_RSP_136 != 0 { + unsafe { + // Yes, this is in a reverse order as rsp0 and self.cmd_rsp3 is the least significant + // Check uboot read response code for more details + *rsp0 = ptr::read_volatile(&self.cmd_rsp3); + *rsp1 = ptr::read_volatile(&self.cmd_rsp2); + *rsp2 = ptr::read_volatile(&self.cmd_rsp1); + *rsp3 = ptr::read_volatile(&self.cmd_rsp); + } + // debug_println!("Meson received 4 response back!"); + } else if cmd.resp_type & MMC_RSP_PRESENT != 0 { + unsafe { + *rsp0 = ptr::read_volatile(&self.cmd_rsp); + // debug_println!("Meson response value: {:#034b} (binary), {:#X} (hex)", *rsp0, *rsp0); + // debug_println!("Meson received 1 response back!"); + } + } + } +} + +impl SdmmcHardware for MesonSdmmcRegisters { + fn sdmmc_send_command(&mut self, cmd: &SdmmcCmd, data: Option<&MmcData>) -> Result<(), SdmmcHalError> { + // It seems that let Some(mmc_data) = data && mmc_data.blocksize != 512 + // is not stable on this nightly 2024.05.01 compiler + // Set up the data addr + let mut data_addr: u32 = 0u32; + if let Some(mmc_data) = data { + // TODO: Check what if the addr is u32::MAX, will the sdcard still working? + if mmc_data.blocksize != MESON_SDCARD_SECTOR_SIZE || mmc_data.addr >= (WRITE_ADDR_UPPER as u64) + || mmc_data.blockcnt == 0 || mmc_data.blockcnt > MAX_BLOCK_PER_TRANSFER { + debug_println!("SDMMC: INVALID INPUT VARIABLE!"); + return Err(SdmmcHalError::EINVAL); + } + // Depend on the flag and hardware, the cache should be flushed accordingly + data_addr = mmc_data.addr as u32; + } + + // Stop data transfer + unsafe { ptr::write_volatile(&mut self.start, 0u32); } + + unsafe { ptr::write_volatile(&mut self.cmd_dat, data_addr); } + + self.meson_mmc_set_up_cmd_cfg_and_cfg(&cmd, data); + + // Reset status register before executing the cmd + unsafe { ptr::write_volatile(&mut self.status, STATUS_MASK); } + + // For testing + unsafe { ptr::write_volatile(&mut self.cmd_rsp, 0u32); } + + unsafe { ptr::write_volatile(&mut self.cmd_arg, cmd.cmdarg); } + + Ok(()) + } + + fn sdmmc_receive_response(&self, cmd: &SdmmcCmd, response: &mut [u32; 4]) -> Result<(), SdmmcHalError> { + let status: u32; + + unsafe { status = ptr::read_volatile(&self.status); } + + if (status & STATUS_END_OF_CHAIN) == 0 { + return Err(SdmmcHalError::EBUSY); + } + + if (status & STATUS_RESP_TIMEOUT) != 0 { + debug_println!("SDMMC: CARD TIMEOUT!"); + return Err(SdmmcHalError::ETIMEDOUT); + } + + let mut return_val: Result<(), SdmmcHalError> = Ok(()); + + if (status & STATUS_ERR_MASK) != 0 { + debug_println!("SDMMC: CARD IO ERROR!"); + return_val = Err(SdmmcHalError::EIO); + } + + self.meson_read_response(cmd, response); + + return_val + } +} \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml b/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml new file mode 100644 index 000000000..c17254306 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "sdmmc_protocol" +version = "0.1.0" +edition = "2021" + +[lib] +name = "sdmmc_protocol" +path = "lib.rs" \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_protocol/lib.rs b/drivers/blk/sdmmc/sdmmc_protocol/lib.rs new file mode 100644 index 000000000..8a12c3789 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/lib.rs @@ -0,0 +1,3 @@ +#![no_std] // Don't link the standard library + +pub mod sdmmc; \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs new file mode 100644 index 000000000..8bf775962 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -0,0 +1,311 @@ +use core::{future::Future, pin::Pin, task::{Context, Poll, Waker}}; + +use sdmmc_constant::{MMC_CMD_READ_MULTIPLE_BLOCK, MMC_CMD_READ_SINGLE_BLOCK, MMC_CMD_STOP_TRANSMISSION, MMC_CMD_WRITE_MULTIPLE_BLOCK, MMC_CMD_WRITE_SINGLE_BLOCK}; +mod sdmmc_constant; +pub struct SdmmcCmd { + pub cmdidx: u32, + pub resp_type: u32, + pub cmdarg: u32, +} + +pub struct MmcData { + // The size of the block(sector size), for sdcard should almost always be 512 + pub blocksize: u32, + // Number of blocks to transfer + pub blockcnt: u32, + pub flags: MmcDataFlag, + pub addr: u64, +} + +pub enum MmcDataFlag { + SdmmcDataRead, + SdmmcDataWrite, +} +pub enum SdmmcHalError { + // Error for result not ready yet + EBUSY, + ETIMEDOUT, + EINVAL, + EIO, + ENOTIMPLEMENTED, + // This error should not be triggered unless there are bugs in program + EUNDEFINED, + // The block transfer succeed, but fail to stop the read/write process + ESTOPCMD, +} + +// Define the MMC response flags +const MMC_RSP_PRESENT: u32 = 1 << 0; +const MMC_RSP_136: u32 = 1 << 1; // 136-bit response +const MMC_RSP_CRC: u32 = 1 << 2; // Expect valid CRC +const MMC_RSP_BUSY: u32 = 1 << 3; // Card may send busy +const MMC_RSP_OPCODE: u32 = 1 << 4; // Response contains opcode + +// Define the MMC response types +pub const MMC_RSP_NONE: u32 = 0; +pub const MMC_RSP_R1: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; +pub const MMC_RSP_R1B: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY; +pub const MMC_RSP_R2: u32 = MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC; +pub const MMC_RSP_R3: u32 = MMC_RSP_PRESENT; +pub const MMC_RSP_R4: u32 = MMC_RSP_PRESENT; +pub const MMC_RSP_R5: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; +pub const MMC_RSP_R6: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; +pub const MMC_RSP_R7: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; + + +/// Program async Rust can be very dangerous if you do not know what is happening understand the hood +/// Power up and power off cannot be properly implemented if I do not have access to control gpio/ regulator and timer +pub trait SdmmcHardware { + fn sdmmc_power_up(&mut self) -> Result<(), SdmmcHalError> { + return Err(SdmmcHalError::ENOTIMPLEMENTED); + } + + fn sdmmc_set_ios(&mut self) -> Result<(), SdmmcHalError> { + return Err(SdmmcHalError::ENOTIMPLEMENTED); + } + + fn sdmmc_send_command(&mut self, cmd: &SdmmcCmd, data: Option<&MmcData>) -> Result<(), SdmmcHalError> { + return Err(SdmmcHalError::ENOTIMPLEMENTED); + } + + fn sdmmc_receive_response(&self, cmd: &SdmmcCmd, response: &mut [u32; 4]) -> Result<(), SdmmcHalError> { + return Err(SdmmcHalError::ENOTIMPLEMENTED); + } + + fn sdmmc_set_interrupt(&mut self, status: bool) -> Result<(), SdmmcHalError> { + return Err(SdmmcHalError::ENOTIMPLEMENTED); + } + + fn sdmmc_ack_interrupt(&mut self) -> Result<(), SdmmcHalError> { + return Err(SdmmcHalError::ENOTIMPLEMENTED); + } + + fn sdmmc_power_off(&mut self) -> Result<(), SdmmcHalError> { + return Err(SdmmcHalError::ENOTIMPLEMENTED); + } +} + +/// TODO: Add more variables for SdmmcProtocol to track the state of the sdmmc controller and card correctly +pub struct SdmmcProtocol<'a, T: SdmmcHardware> { + pub hardware: &'a mut T, +} + +impl Unpin for SdmmcProtocol<'_, T> where T: Unpin + SdmmcHardware {} + +impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { + pub fn new(hardware: &'a mut T) -> Self { + SdmmcProtocol { hardware } + } + + pub async fn read_block(self, blockcnt: u32, start_idx: u64, destination: u64) -> (Result<(), SdmmcHalError>, Option>) { + let mut cmd: SdmmcCmd; + let mut res: Result<(), SdmmcHalError>; + // TODO: Figure out a way to support cards with 4 KB sector size + let data: MmcData = MmcData { + blocksize: 512, + blockcnt, + flags: MmcDataFlag::SdmmcDataRead, + addr: destination, + }; + let mut resp: [u32; 4] = [0; 4]; + // TODO: Add more validation check in the future + // Like sdmmc card usually cannot transfer arbitrary number of blocks at once + + // The cmd arg for read operation is different between some card variation as showed by uboot code below + /* + if (mmc->high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * mmc->read_bl_len; + */ + // For now we default to assume the card is high_capacity + // TODO: Fix it when we properly implement card boot up + // TODO: If we boot the card by ourself or reset the card, remember to send block len cmd + let cmd_arg: u64 = start_idx; + if blockcnt == 1 { + cmd = SdmmcCmd { + cmdidx: MMC_CMD_READ_SINGLE_BLOCK, + resp_type: MMC_RSP_R1, + cmdarg: cmd_arg as u32, + }; + let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); + res = future.await; + return (res, Some(self)); + } + else { + cmd = SdmmcCmd { + cmdidx: MMC_CMD_READ_MULTIPLE_BLOCK, + resp_type: MMC_RSP_R1, + cmdarg: cmd_arg as u32, + }; + let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); + res = future.await; + if let Ok(()) = res { + // Uboot code for determine response type in this case + // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; + // TODO: Add mmc checks here + cmd = SdmmcCmd { + cmdidx: MMC_CMD_STOP_TRANSMISSION, + resp_type: MMC_RSP_R1B, + cmdarg: 0, + }; + let future = SdmmcCmdFuture::new(self.hardware, &cmd, None, &mut resp); + res = future.await; + return (res.map_err(|_| SdmmcHalError::ESTOPCMD), Some(self)); + } else { + return (res, Some(self)); + } + } + } + + // Almost the same with read_block aside from the cmd being sent is a bit different + // For any future code add to read_block/write_block, remember to change both + // Should read_block/write_block be the same function? + pub async fn write_block(self, blockcnt: u32, start_idx: u64, source: u64) -> (Result<(), SdmmcHalError>, Option>) { + let mut cmd: SdmmcCmd; + let mut res: Result<(), SdmmcHalError>; + // TODO: Figure out a way to support cards with 4 KB sector size + let data: MmcData = MmcData { + blocksize: 512, + blockcnt, + flags: MmcDataFlag::SdmmcDataWrite, + addr: source, + }; + let mut resp: [u32; 4] = [0; 4]; + // TODO: Add more validation check in the future + + let cmd_arg: u64 = start_idx; + if blockcnt == 1 { + cmd = SdmmcCmd { + cmdidx: MMC_CMD_WRITE_SINGLE_BLOCK, + resp_type: MMC_RSP_R1, + cmdarg: cmd_arg as u32, + }; + let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); + res = future.await; + return (res, Some(self)); + } + else { + cmd = SdmmcCmd { + cmdidx: MMC_CMD_WRITE_MULTIPLE_BLOCK, + resp_type: MMC_RSP_R1, + cmdarg: cmd_arg as u32, + }; + let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); + res = future.await; + if let Ok(()) = res { + // Uboot code for determine response type in this case + // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; + // TODO: Add mmc checks here + cmd = SdmmcCmd { + cmdidx: MMC_CMD_STOP_TRANSMISSION, + resp_type: MMC_RSP_R1B, + cmdarg: 0, + }; + let future = SdmmcCmdFuture::new(self.hardware, &cmd, None, &mut resp); + res = future.await; + return (res.map_err(|_| SdmmcHalError::ESTOPCMD), Some(self)); + } else { + return (res, Some(self)); + } + } + } +} + +enum CmdState { + // Currently sending the command + // State at the start + NotSent, + // Waiting for the response + WaitingForResponse, + // Error encountered + Error, + // Finished + Finished, +} + +pub struct SdmmcCmdFuture<'a, 'b, 'c> { + hardware: &'a mut dyn SdmmcHardware, + cmd: &'b SdmmcCmd, + data: Option<&'b MmcData>, + waker: Option, + state: CmdState, + response: &'c mut [u32; 4], +} + +impl<'a, 'b, 'c> SdmmcCmdFuture<'a, 'b, 'c> { + pub fn new( + hardware: &'a mut dyn SdmmcHardware, + cmd: &'b SdmmcCmd, + data: Option<&'b MmcData>, + response: &'c mut [u32; 4]) -> SdmmcCmdFuture<'a, 'b, 'c> { + SdmmcCmdFuture { + hardware, + cmd, + data, + waker: None, + state: CmdState::NotSent, + response, + } + } +} + +/// SdmmcCmdFuture serves as the basic building block for async fn above +/// In the context of Sdmmc device, since the requests are executed linearly under the hood +/// We actually do not need an executor to execute the request +/// The context can be ignored unless someone insist to use an executor for the requests +/// So for now, the context is being stored in waker but this waker will not be used +/// Beside, we are in no std environment and can barely use any locks +impl<'a, 'b, 'c> Future for SdmmcCmdFuture<'a, 'b, 'c> { + type Output = Result<(), SdmmcHalError>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // As I have said above, this waker is not being used so it do not need to be shared data + // But store the waker provided anyway + // Beware you need to update waker every polling, for more details about why + // read Asynchronous Programming in Rust + self.waker = Some(cx.waker().clone()); + + match self.state { + CmdState::NotSent => { + let res: Result<(), SdmmcHalError>; + { + let this: &mut SdmmcCmdFuture<'a, 'b, 'c> = self.as_mut().get_mut(); + + // Now, you can pass `&SdmmcCmd` to `sdmmc_send_command` + let cmd: &SdmmcCmd = this.cmd; + let data: Option<&MmcData> = this.data; + res = this.hardware.sdmmc_send_command(cmd, data); + } + if let Ok(()) = res { + self.state = CmdState::WaitingForResponse; + return Poll::Pending; + } else { + self.state = CmdState::Error; + return Poll::Ready(res); + } + } + CmdState::WaitingForResponse => { + let res; + { + let this: &mut SdmmcCmdFuture<'a, 'b, 'c> = self.as_mut().get_mut(); + let cmd: &SdmmcCmd = this.cmd; + let response: &mut [u32; 4] = this.response; + let hardware: &mut dyn SdmmcHardware = this.hardware; + res = hardware.sdmmc_receive_response(cmd, response); + } + if let Err(SdmmcHalError::EBUSY) = res { + return Poll::Pending; + } else if let Ok(()) = res { + self.state = CmdState::Finished; + return Poll::Ready(res); + } else { + self.state = CmdState::Error; + return Poll::Ready(res); + } + } + CmdState::Error => return Poll::Ready(Err(SdmmcHalError::EUNDEFINED)), + CmdState::Finished => return Poll::Ready(Err(SdmmcHalError::EUNDEFINED)), + } + } +} \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs new file mode 100644 index 000000000..18995645d --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs @@ -0,0 +1,49 @@ +// Define constants for MMC data flags +pub const MMC_DATA_READ: u32 = 1; +pub const MMC_DATA_WRITE: u32 = 2; + +// Define constants for MMC commands +pub const MMC_CMD_GO_IDLE_STATE: u32 = 0; +pub const MMC_CMD_SEND_OP_COND: u32 = 1; +pub const MMC_CMD_ALL_SEND_CID: u32 = 2; +pub const MMC_CMD_SET_RELATIVE_ADDR: u32 = 3; +pub const MMC_CMD_SET_DSR: u32 = 4; +pub const MMC_CMD_SWITCH: u32 = 6; +pub const MMC_CMD_SELECT_CARD: u32 = 7; +pub const MMC_CMD_SEND_EXT_CSD: u32 = 8; +pub const MMC_CMD_SEND_CSD: u32 = 9; +pub const MMC_CMD_SEND_CID: u32 = 10; +pub const MMC_CMD_STOP_TRANSMISSION: u32 = 12; +pub const MMC_CMD_SEND_STATUS: u32 = 13; +pub const MMC_CMD_SET_BLOCKLEN: u32 = 16; +pub const MMC_CMD_READ_SINGLE_BLOCK: u32 = 17; +pub const MMC_CMD_READ_MULTIPLE_BLOCK: u32 = 18; +pub const MMC_CMD_SEND_TUNING_BLOCK: u32 = 19; +pub const MMC_CMD_SEND_TUNING_BLOCK_HS200: u32 = 21; +pub const MMC_CMD_SET_BLOCK_COUNT: u32 = 23; +pub const MMC_CMD_WRITE_SINGLE_BLOCK: u32 = 24; +pub const MMC_CMD_WRITE_MULTIPLE_BLOCK: u32 = 25; +pub const MMC_CMD_ERASE_GROUP_START: u32 = 35; +pub const MMC_CMD_ERASE_GROUP_END: u32 = 36; +pub const MMC_CMD_ERASE: u32 = 38; +pub const MMC_CMD_APP_CMD: u32 = 55; +pub const MMC_CMD_SPI_READ_OCR: u32 = 58; +pub const MMC_CMD_SPI_CRC_ON_OFF: u32 = 59; +pub const MMC_CMD_RES_MAN: u32 = 62; + +// Define constants for MMC command 62 arguments +pub const MMC_CMD62_ARG1: u32 = 0xefac62ec; +pub const MMC_CMD62_ARG2: u32 = 0xcbaea7; + +// Define constants for SD commands +pub const SD_CMD_SEND_RELATIVE_ADDR: u32 = 3; +pub const SD_CMD_SWITCH_FUNC: u32 = 6; +pub const SD_CMD_SEND_IF_COND: u32 = 8; +pub const SD_CMD_SWITCH_UHS18V: u32 = 11; + +pub const SD_CMD_APP_SET_BUS_WIDTH: u32 = 6; +pub const SD_CMD_APP_SD_STATUS: u32 = 13; +pub const SD_CMD_ERASE_WR_BLK_START: u32 = 32; +pub const SD_CMD_ERASE_WR_BLK_END: u32 = 33; +pub const SD_CMD_APP_SEND_OP_COND: u32 = 41; +pub const SD_CMD_APP_SEND_SCR: u32 = 51; diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs new file mode 100644 index 000000000..1f1a8a1bc --- /dev/null +++ b/drivers/blk/sdmmc/src/main.rs @@ -0,0 +1,220 @@ +#![no_std] // Don't link the standard library +#![no_main] // Don't use the default entry point + +extern crate alloc; + +mod sddf_blk; + +use core::{future::Future, pin::Pin, task::{Context, Poll, RawWaker, RawWakerVTable, Waker}}; + +use alloc::boxed::Box; +use sddf_blk::{blk_dequeue_req_helper, blk_enqueue_resp_helper, blk_queue_empty_req_helper, blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkStatus}; +use sdmmc_hal::meson_gx_mmc::MesonSdmmcRegisters; + +use sdmmc_protocol::sdmmc::{SdmmcHalError, SdmmcHardware, SdmmcProtocol}; +use sel4_microkit::{debug_print, debug_println, protection_domain, Channel, Handler, Infallible}; + +const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(0); + +const SDCARD_SECTOR_SIZE: u32 = 512; +const SDDF_TRANSFER_SIZE: u32 = 4096; +const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE/SDCARD_SECTOR_SIZE; + +const RETRY_CHANCE: u32 = 5; + +fn print_one_block(ptr: *const u8) { + unsafe { + // Iterate over the 512 bytes and print each one in hexadecimal format + for i in 0..512 { + let byte = *ptr.add(i); + if i % 16 == 0 { + debug_print!("\n{:04x}: ", i); + } + debug_print!("{:02x} ", byte); + } + debug_println!(); + } +} + +// No-op waker implementations, they do nothing. +unsafe fn noop(_data: *const ()) {} +unsafe fn noop_clone(_data: *const ()) -> RawWaker { + RawWaker::new(_data, &VTABLE) +} + +// A VTable that points to the no-op functions +static VTABLE: RawWakerVTable = RawWakerVTable::new( + noop_clone, + noop, + noop, + noop, +); + +// Function to create a dummy Waker +fn create_dummy_waker() -> Waker { + let raw_waker = RawWaker::new(core::ptr::null(), &VTABLE); + unsafe { Waker::from_raw(raw_waker) } +} + +#[protection_domain(heap_size = 0x10000)] +fn init() -> HandlerImpl<'static, MesonSdmmcRegisters> { + // debug_println!("Driver init!"); + unsafe { + blk_queue_init_helper(); + } + let meson_hal: &mut MesonSdmmcRegisters = MesonSdmmcRegisters::new(); + let protocol: SdmmcProtocol<'static, MesonSdmmcRegisters> = SdmmcProtocol::new(meson_hal); + + // Code block to test read ability + /* + { + let future = Box::pin(protocol.read_block(count as u32, block_number as u64, io_or_offset)); + } + */ + HandlerImpl { + future: None, + sdmmc: Some(protocol), + } +} + +struct HandlerImpl<'a, T: SdmmcHardware> { + future: Option, Option>)> + 'a>>>, + sdmmc: Option>, +} + +impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { + type Error = Infallible; + + /// In Rust, it is actually very hard to manage long live future object that must be created + /// by borrowing + fn notified(&mut self, channel: Channel) -> Result<(), Self::Error> { + match channel { + BLK_VIRTUALIZER => { + // This while loop is to dequeue every command + while unsafe { blk_queue_empty_req_helper() == 0 && blk_queue_full_resp_helper() == 0 } { + // Assume we magically get the value from sddf + let mut request_code: BlkOp = BlkOp::BlkReqRead; + let mut io_or_offset: u64 = 0; + let mut block_number: u32 = 0; + let mut count: u16 = 0; + let mut id: u32 = 0; + unsafe { + blk_dequeue_req_helper( + &mut request_code as *mut BlkOp, + &mut io_or_offset as *mut u64, + &mut block_number as *mut u32, + &mut count as *mut u16, + &mut id as *mut u32, + ); + } + // Translate block from sddf_block and sddf_count to real block number and count + // Since the real block size is 512 byte and sddf_block_transfer size is 4KB + // multiply 8 here + // But I do not like this design as why block driver need to know the real transfer size??? + block_number = block_number * SDDF_TO_REAL_SECTOR; + count = count * SDDF_TO_REAL_SECTOR as u16; + + // Print the retrieved values + /* + debug_println!("io_or_offset: 0x{:x}", io_or_offset);// Simple u64 + debug_println!("block_number: {}", block_number); // Simple u32 + debug_println!("count: {}", count); // Simple u16 + debug_println!("id: {}", id); // Simple u32 + */ + let mut retry: u32 = RETRY_CHANCE; + let mut success_count: u64 = 0; + let resp_status: BlkStatus; + loop { + // This value should + let count_to_do: u32; + + match request_code { + BlkOp::BlkReqRead => { + // If the future is some, block itself + // Since we polling on + // This match in the loop might seems to be ineffient here as the correct way is create future first and polling on that + // future. But as the driver would soon change into poll on interrupt instead of polling until finish, so just leave it for now + + // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer + count_to_do = core::cmp::min(count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); + if let Some(sdmmc) = self.sdmmc.take() { + self.future = Some(Box::pin(sdmmc.read_block(count_to_do as u32, block_number as u64 + success_count, io_or_offset + success_count * SDCARD_SECTOR_SIZE as u64))); + } + else { + panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") + } + } + BlkOp::BlkReqWrite => { + count_to_do = core::cmp::min(count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); + if let Some(sdmmc) = self.sdmmc.take() { + self.future = Some(Box::pin(sdmmc.write_block(count_to_do as u32, block_number as u64 + success_count, io_or_offset + success_count * SDCARD_SECTOR_SIZE as u64))); + } + else { + panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") + } + } + _ => { + // For other request, enqueue response and exit loop + unsafe { + resp_status = BlkStatus::BlkRespOk; + blk_enqueue_resp_helper(BlkStatus::BlkRespOk, 0, id); + break; + } + } + } + // Notify the virtualizer when there are results available + // TODO: Add retry if the sdcard return an error + if let Some(future) = &mut self.future { + let waker = create_dummy_waker(); + let mut cx = Context::from_waker(&waker); + // TODO: I can get rid of this loop once I configure out how to enable interrupt from Linux kernel driver + loop { + match future.as_mut().poll(&mut cx) { + Poll::Ready((result, sdmmc)) => { + // debug_println!("SDMMC_DRIVER: Future completed with result"); + self.future = None; // Reset the future once done + self.sdmmc = sdmmc; + if result.is_err() { + debug_println!("SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possiblely retry!"); + retry -= 1; + } + else { + // Reset retry chance here + retry = RETRY_CHANCE; + + success_count += count_to_do as u64; + count -= count_to_do as u16; + } + break; + } + Poll::Pending => { + // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); + } + } + } + } + // Exit when all block transferred or used all retry chances + if count == 0 { + resp_status = BlkStatus::BlkRespOk; + break; + } + if retry == 0 { + resp_status = BlkStatus::BlkRespSeekError; + break; + } + } + // Enqueue resp + unsafe { + blk_enqueue_resp_helper(resp_status, success_count as u32, id); + } + // debug_println!("SDMMC_DRIVER: Notify BLK_VIRTUALIZER"); + BLK_VIRTUALIZER.notify(); + } + } + _ => { + debug_println!("SDMMC_DRIVER: MESSAGE FROM UNKNOWN CHANNEL: {}", channel.index()); + } + } + Ok(()) + } +} \ No newline at end of file diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs new file mode 100644 index 000000000..e0026c2fc --- /dev/null +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -0,0 +1,26 @@ +extern "C" { + pub fn blk_queue_init_helper(); + + pub fn blk_queue_empty_req_helper() -> u8; + pub fn blk_queue_full_resp_helper() -> u8; + pub fn blk_enqueue_resp_helper(status: BlkStatus, success: u32, id: u32) -> u8; + pub fn blk_dequeue_req_helper(code: *mut BlkOp, + io_or_offset: *mut u64, + block_number: *mut u32, + count: *mut u16, + id: *mut u32); +} + +#[repr(C)] +pub enum BlkOp { + BlkReqRead, + BlkReqWrite, + BlkReqFlush, + BlkReqBarrier, +} + +#[repr(C)] +pub enum BlkStatus { + BlkRespOk, + BlkRespSeekError, +} diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/sdmmc/src/sddf_helper.c new file mode 100644 index 000000000..0050ab585 --- /dev/null +++ b/drivers/blk/sdmmc/src/sddf_helper.c @@ -0,0 +1,43 @@ +#include +#include +#include + +blk_queue_handle_t queue_handle_memory; +blk_queue_handle_t *queue_handle = &queue_handle_memory; + +blk_req_queue_t *blk_req_queue; +blk_resp_queue_t *blk_resp_queue; + +blk_storage_info_t *blk_config; + +void blk_queue_init_helper() { + blk_queue_init(queue_handle, blk_req_queue, blk_resp_queue, BLK_QUEUE_SIZE_DRIV); + blk_config->sector_size = 512; + blk_config->block_size = 1; + blk_config->capacity = 0xFFFFFFFFFF; + blk_config->ready = true; +} + +uint8_t blk_queue_empty_req_helper() { + return blk_queue_empty_req(queue_handle); +} + +uint8_t blk_queue_full_resp_helper() { + return blk_queue_full_resp(queue_handle); +} + +uint8_t blk_enqueue_resp_helper(uint8_t status, uint16_t success, uint32_t id) { + // It would be better if we do not use int but use int8_t + if (blk_enqueue_resp(queue_handle, status, success, id) == 0) { + return 0; + } + return 1; +} + +uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint16_t *count, uint32_t *id) { + // It would be better if we do not use int but use int8_t + if (blk_dequeue_req(queue_handle, (blk_req_code_t *)code, io_or_offset, block_number, count, id) == 0) { + return 0; + } + return 1; +} \ No newline at end of file diff --git a/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json b/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json new file mode 100644 index 000000000..16ce9ef87 --- /dev/null +++ b/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json @@ -0,0 +1,37 @@ +{ + "arch": "aarch64", + "crt-objects-fallback": "false", + "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + "disable-redzone": true, + "eh-frame-header": false, + "env": "sel4", + "exe-suffix": ".elf", + "features": "+v8a,+strict-align,+neon,+fp-armv8", + "link-script": "__sel4_ipc_buffer_obj = (_end + 4096 - 1) & ~(4096 - 1);", + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "llvm-target": "aarch64-unknown-none", + "max-atomic-width": 128, + "metadata": { + "description": null, + "host_tools": null, + "std": null, + "tier": null + }, + "panic-strategy": "abort", + "pre-link-args": { + "gnu-lld": [ + "-z", + "max-page-size=4096" + ] + }, + "relocation-model": "static", + "stack-probes": { + "kind": "inline" + }, + "supported-sanitizers": [ + "kcfi", + "kernel-address" + ], + "target-pointer-width": "64" +} From ee8fc222a670597c5aaf552dd5750375f67151d5 Mon Sep 17 00:00:00 2001 From: Cheng Date: Tue, 8 Oct 2024 19:52:52 +1100 Subject: [PATCH 02/48] sdmmc: experimentally implement polling based on interrupt Signed-off-by: Cheng --- .../blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 60 +++- drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 35 +- drivers/blk/sdmmc/src/main.rs | 309 +++++++++++------- drivers/blk/sdmmc/src/sddf_blk/mod.rs | 11 + 4 files changed, 282 insertions(+), 133 deletions(-) diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs index 1801feca6..28bd76c67 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs @@ -1,6 +1,6 @@ use core::ptr; -use sdmmc_protocol::sdmmc::{MmcData, MmcDataFlag, SdmmcCmd, SdmmcHalError, SdmmcHardware}; +use sdmmc_protocol::sdmmc::{InterruptType, MmcData, MmcDataFlag, SdmmcCmd, SdmmcHalError, SdmmcHardware}; use sel4_microkit::debug_println; const SDIO_BASE: u64 = 0xffe05000; // Base address from DTS @@ -56,6 +56,27 @@ const STATUS_RESP_ERR: u32 = 1 << 10; // BIT(10) const STATUS_RESP_TIMEOUT: u32 = 1 << 11; // BIT(11) const STATUS_DESC_TIMEOUT: u32 = 1 << 12; // BIT(12) const STATUS_END_OF_CHAIN: u32 = 1 << 13; // BIT(13) +const STATUS_BUSY: u32 = 1 << 31; +const STATUS_DESC_BUSY: u32 = 1 << 30; +const STATUS_DATI: u32 = 0xFF << 16; // Equivalent to GENMASK(23, 16) + +// IRQ enable register masks and flags +const IRQ_RXD_ERR_MASK: u32 = 0xFF; // Equivalent to GENMASK(7, 0) +const IRQ_TXD_ERR: u32 = 1 << 8; +const IRQ_DESC_ERR: u32 = 1 << 9; +const IRQ_RESP_ERR: u32 = 1 << 10; +// Equivalent to (IRQ_RXD_ERR_MASK | IRQ_TXD_ERR | IRQ_DESC_ERR | IRQ_RESP_ERR) +const IRQ_CRC_ERR: u32 = IRQ_RXD_ERR_MASK | IRQ_TXD_ERR | IRQ_DESC_ERR | IRQ_RESP_ERR; +const IRQ_RESP_TIMEOUT: u32 = 1 << 11; +const IRQ_DESC_TIMEOUT: u32 = 1 << 12; +// Equivalent to (IRQ_RESP_TIMEOUT | IRQ_DESC_TIMEOUT) +const IRQ_TIMEOUTS: u32 = IRQ_RESP_TIMEOUT | IRQ_DESC_TIMEOUT; +const IRQ_END_OF_CHAIN: u32 = 1 << 13; +const IRQ_RESP_STATUS: u32 = 1 << 14; +const IRQ_SDIO: u32 = 1 << 15; +const IRQ_ERR_MASK: u32 = IRQ_CRC_ERR | IRQ_TIMEOUTS; +// Equivalent to (IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN) +const IRQ_EN_MASK: u32 = IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN; // Configuration constants (assuming based on context) const CFG_BL_LEN_MASK: u32 = 0xF << 4; // Bits 4-7 @@ -213,8 +234,8 @@ impl MesonSdmmcRegisters { cfg |= ilog2(data.blocksize) << CFG_BL_LEN_SHIFT; - // This value should only be 9 - assert!(ilog2(data.blocksize) == 9); + // This value should only be 512 + assert!(data.blocksize == 512); unsafe { ptr::write_volatile(&mut self.cfg, cfg); }; @@ -279,9 +300,10 @@ impl SdmmcHardware for MesonSdmmcRegisters { self.meson_mmc_set_up_cmd_cfg_and_cfg(&cmd, data); // Reset status register before executing the cmd + // If we keep this line of code, do we still need to manually ack interrupts??? unsafe { ptr::write_volatile(&mut self.status, STATUS_MASK); } - // For testing + // Clear the response register, for testing & debugging unsafe { ptr::write_volatile(&mut self.cmd_rsp, 0u32); } unsafe { ptr::write_volatile(&mut self.cmd_arg, cmd.cmdarg); } @@ -314,4 +336,34 @@ impl SdmmcHardware for MesonSdmmcRegisters { return_val } + + fn sdmmc_enable_interrupt(&mut self, irq_to_enable: &mut u32) -> Result<(), SdmmcHalError> { + let mut irq_bits_to_set: u32 = 0; + if *irq_to_enable & (InterruptType::Success as u32) > 0 { + irq_bits_to_set |= IRQ_END_OF_CHAIN; + } + if *irq_to_enable & (InterruptType::Error as u32) > 0 { + irq_bits_to_set |= IRQ_ERR_MASK; + } + if *irq_to_enable & (InterruptType::SDIO as u32) > 0 { + irq_bits_to_set |= IRQ_SDIO; + } + unsafe { ptr::write_volatile(&mut self.irq_en, irq_bits_to_set); } + return Ok(()); + } + + fn sdmmc_ack_interrupt(&mut self, irq_enabled: &u32) -> Result<(), SdmmcHalError> { + let mut irq_bits_to_set: u32 = 0; + if *irq_enabled & (InterruptType::Success as u32) > 0 { + irq_bits_to_set |= IRQ_END_OF_CHAIN; + } + if *irq_enabled & (InterruptType::Error as u32) > 0 { + irq_bits_to_set |= IRQ_ERR_MASK; + } + if *irq_enabled & (InterruptType::SDIO as u32) > 0 { + irq_bits_to_set |= IRQ_SDIO; + } + unsafe { ptr::write_volatile(&mut self.status, irq_bits_to_set); } + return Ok(()); + } } \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs index 8bf775962..4c0bd047e 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -21,6 +21,7 @@ pub enum MmcDataFlag { SdmmcDataRead, SdmmcDataWrite, } + pub enum SdmmcHalError { // Error for result not ready yet EBUSY, @@ -34,6 +35,15 @@ pub enum SdmmcHalError { ESTOPCMD, } +// Interrupt related define +#[repr(u32)] // Ensures the enum variants are stored as 32-bit integers +pub enum InterruptType { + Success = 0b0001, // 1st bit + Error = 0b0010, // 2nd bit + SDIO = 0b0100, // 3rd bit + // You can add more flags as needed +} + // Define the MMC response flags const MMC_RSP_PRESENT: u32 = 1 << 0; const MMC_RSP_136: u32 = 1 << 1; // 136-bit response @@ -72,11 +82,11 @@ pub trait SdmmcHardware { return Err(SdmmcHalError::ENOTIMPLEMENTED); } - fn sdmmc_set_interrupt(&mut self, status: bool) -> Result<(), SdmmcHalError> { + fn sdmmc_enable_interrupt(&mut self, irq_to_enable: &mut u32) -> Result<(), SdmmcHalError> { return Err(SdmmcHalError::ENOTIMPLEMENTED); } - fn sdmmc_ack_interrupt(&mut self) -> Result<(), SdmmcHalError> { + fn sdmmc_ack_interrupt(&mut self, irq_enabled: &u32) -> Result<(), SdmmcHalError> { return Err(SdmmcHalError::ENOTIMPLEMENTED); } @@ -88,13 +98,32 @@ pub trait SdmmcHardware { /// TODO: Add more variables for SdmmcProtocol to track the state of the sdmmc controller and card correctly pub struct SdmmcProtocol<'a, T: SdmmcHardware> { pub hardware: &'a mut T, + enabled_irq: u32, } impl Unpin for SdmmcProtocol<'_, T> where T: Unpin + SdmmcHardware {} impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { pub fn new(hardware: &'a mut T) -> Self { - SdmmcProtocol { hardware } + SdmmcProtocol { + hardware, + enabled_irq: 0, + } + } + + pub fn reset_card(&mut self) -> Result<(), SdmmcHalError> { + let all: u32 = InterruptType::Success as u32 | InterruptType::Error as u32 | InterruptType::SDIO as u32; + self.hardware.sdmmc_ack_interrupt(&all) + } + + pub fn enable_interrupt(&mut self, irq_to_enable: &mut u32) -> Result<(), SdmmcHalError> { + let res = self.hardware.sdmmc_enable_interrupt(irq_to_enable); + self.enabled_irq = *irq_to_enable; + res + } + + pub fn ack_interrupt(&mut self) -> Result<(), SdmmcHalError> { + self.hardware.sdmmc_ack_interrupt(&self.enabled_irq) } pub async fn read_block(self, blockcnt: u32, start_idx: u64, destination: u64) -> (Result<(), SdmmcHalError>, Option>) { diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 1f1a8a1bc..75cc7346a 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -8,19 +8,21 @@ mod sddf_blk; use core::{future::Future, pin::Pin, task::{Context, Poll, RawWaker, RawWakerVTable, Waker}}; use alloc::boxed::Box; -use sddf_blk::{blk_dequeue_req_helper, blk_enqueue_resp_helper, blk_queue_empty_req_helper, blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkStatus}; +use sddf_blk::{blk_dequeue_req_helper, blk_enqueue_resp_helper, blk_queue_empty_req_helper, blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkRequest, BlkStatus}; use sdmmc_hal::meson_gx_mmc::MesonSdmmcRegisters; -use sdmmc_protocol::sdmmc::{SdmmcHalError, SdmmcHardware, SdmmcProtocol}; +use sdmmc_protocol::sdmmc::{InterruptType, SdmmcHalError, SdmmcHardware, SdmmcProtocol}; use sel4_microkit::{debug_print, debug_println, protection_domain, Channel, Handler, Infallible}; const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(0); +const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(1); + const SDCARD_SECTOR_SIZE: u32 = 512; const SDDF_TRANSFER_SIZE: u32 = 4096; const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE/SDCARD_SECTOR_SIZE; -const RETRY_CHANCE: u32 = 5; +const RETRY_CHANCE: u16 = 5; fn print_one_block(ptr: *const u8) { unsafe { @@ -63,23 +65,23 @@ fn init() -> HandlerImpl<'static, MesonSdmmcRegisters> { blk_queue_init_helper(); } let meson_hal: &mut MesonSdmmcRegisters = MesonSdmmcRegisters::new(); - let protocol: SdmmcProtocol<'static, MesonSdmmcRegisters> = SdmmcProtocol::new(meson_hal); - - // Code block to test read ability - /* - { - let future = Box::pin(protocol.read_block(count as u32, block_number as u64, io_or_offset)); - } - */ + let mut protocol: SdmmcProtocol<'static, MesonSdmmcRegisters> = SdmmcProtocol::new(meson_hal); + let mut irq_to_enable = InterruptType::Success as u32 | InterruptType::Error as u32; + // Should always succeed, at least for odroid C4 + let _ = protocol.enable_interrupt(&mut irq_to_enable); HandlerImpl { future: None, sdmmc: Some(protocol), + request: None, + retry: RETRY_CHANCE, } } struct HandlerImpl<'a, T: SdmmcHardware> { future: Option, Option>)> + 'a>>>, sdmmc: Option>, + request: Option, + retry: u16, } impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { @@ -88,133 +90,188 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { /// In Rust, it is actually very hard to manage long live future object that must be created /// by borrowing fn notified(&mut self, channel: Channel) -> Result<(), Self::Error> { - match channel { - BLK_VIRTUALIZER => { - // This while loop is to dequeue every command - while unsafe { blk_queue_empty_req_helper() == 0 && blk_queue_full_resp_helper() == 0 } { - // Assume we magically get the value from sddf - let mut request_code: BlkOp = BlkOp::BlkReqRead; - let mut io_or_offset: u64 = 0; - let mut block_number: u32 = 0; - let mut count: u16 = 0; - let mut id: u32 = 0; - unsafe { - blk_dequeue_req_helper( - &mut request_code as *mut BlkOp, - &mut io_or_offset as *mut u64, - &mut block_number as *mut u32, - &mut count as *mut u16, - &mut id as *mut u32, - ); - } - // Translate block from sddf_block and sddf_count to real block number and count - // Since the real block size is 512 byte and sddf_block_transfer size is 4KB - // multiply 8 here - // But I do not like this design as why block driver need to know the real transfer size??? - block_number = block_number * SDDF_TO_REAL_SECTOR; - count = count * SDDF_TO_REAL_SECTOR as u16; - - // Print the retrieved values - /* - debug_println!("io_or_offset: 0x{:x}", io_or_offset);// Simple u64 - debug_println!("block_number: {}", block_number); // Simple u32 - debug_println!("count: {}", count); // Simple u16 - debug_println!("id: {}", id); // Simple u32 - */ - let mut retry: u32 = RETRY_CHANCE; - let mut success_count: u64 = 0; - let resp_status: BlkStatus; - loop { - // This value should - let count_to_do: u32; - - match request_code { - BlkOp::BlkReqRead => { - // If the future is some, block itself - // Since we polling on - // This match in the loop might seems to be ineffient here as the correct way is create future first and polling on that - // future. But as the driver would soon change into poll on interrupt instead of polling until finish, so just leave it for now - - // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer - count_to_do = core::cmp::min(count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); - if let Some(sdmmc) = self.sdmmc.take() { - self.future = Some(Box::pin(sdmmc.read_block(count_to_do as u32, block_number as u64 + success_count, io_or_offset + success_count * SDCARD_SECTOR_SIZE as u64))); - } - else { - panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") - } - } - BlkOp::BlkReqWrite => { - count_to_do = core::cmp::min(count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); - if let Some(sdmmc) = self.sdmmc.take() { - self.future = Some(Box::pin(sdmmc.write_block(count_to_do as u32, block_number as u64 + success_count, io_or_offset + success_count * SDCARD_SECTOR_SIZE as u64))); - } - else { - panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") - } + debug_println!("SDMMC_DRIVER: MESSAGE FROM CHANNEL: {}", channel.index()); + let mut notify_virt: bool = false; + loop { + // Polling if receive any notification, it is to poll even the notification is not from the interrupt as polling is cheap + if let Some(request) = &mut self.request { + if let Some(future) = &mut self.future { + let waker = create_dummy_waker(); + let mut cx = Context::from_waker(&waker); + // TODO: I can get rid of this loop once I configure out how to enable interrupt from Linux kernel driver + match future.as_mut().poll(&mut cx) { + Poll::Ready((result, sdmmc)) => { + // debug_println!("SDMMC_DRIVER: Future completed with result"); + self.future = None; // Reset the future once done + self.sdmmc = sdmmc; + if result.is_err() { + debug_println!("SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possibly retry!"); + self.retry -= 1; } - _ => { - // For other request, enqueue response and exit loop - unsafe { - resp_status = BlkStatus::BlkRespOk; - blk_enqueue_resp_helper(BlkStatus::BlkRespOk, 0, id); - break; - } + else { + // Deduct finished count from count + request.success_count += request.count_to_do; + request.count -= request.count_to_do as u16; } - } - // Notify the virtualizer when there are results available - // TODO: Add retry if the sdcard return an error - if let Some(future) = &mut self.future { - let waker = create_dummy_waker(); - let mut cx = Context::from_waker(&waker); - // TODO: I can get rid of this loop once I configure out how to enable interrupt from Linux kernel driver - loop { - match future.as_mut().poll(&mut cx) { - Poll::Ready((result, sdmmc)) => { - // debug_println!("SDMMC_DRIVER: Future completed with result"); - self.future = None; // Reset the future once done - self.sdmmc = sdmmc; - if result.is_err() { - debug_println!("SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possiblely retry!"); - retry -= 1; - } - else { - // Reset retry chance here - retry = RETRY_CHANCE; - - success_count += count_to_do as u64; - count -= count_to_do as u16; - } - break; - } - Poll::Pending => { - // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); - } - } + if request.count == 0 { + let resp_status = BlkStatus::BlkRespOk; + notify_virt = true; + unsafe { blk_enqueue_resp_helper(resp_status, request.success_count, request.id); } + self.request = None; + } else if self.retry == 0 { + let resp_status = BlkStatus::BlkRespSeekError; + notify_virt = true; + unsafe { blk_enqueue_resp_helper(resp_status, request.success_count, request.id); } + self.request = None; } } - // Exit when all block transferred or used all retry chances - if count == 0 { - resp_status = BlkStatus::BlkRespOk; + Poll::Pending => { + // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); + // Since the future is not ready, no other request can be dequeued, exit the big loop break; } - if retry == 0 { - resp_status = BlkStatus::BlkRespSeekError; - break; + } + } + } + + while self.request.is_none() && unsafe { blk_queue_empty_req_helper() == 0 && blk_queue_full_resp_helper() == 0 } { + let mut request: BlkRequest = BlkRequest { + request_code: BlkOp::BlkReqFlush, + io_or_offset: 0, + block_number: 0, + count: 0, + success_count: 0, + count_to_do: 0, + id: 0, + }; + unsafe { + blk_dequeue_req_helper( + &mut request.request_code as *mut BlkOp, + &mut request.io_or_offset as *mut u64, + &mut request.block_number as *mut u32, + &mut request.count as *mut u16, + &mut request.id as *mut u32, + ); + } + // Print the retrieved values + /* + debug_println!("io_or_offset: 0x{:x}", io_or_offset);// Simple u64 + debug_println!("block_number: {}", block_number); // Simple u32 + debug_println!("count: {}", count); // Simple u16 + debug_println!("id: {}", id); // Simple u32 + */ + match request.request_code { + BlkOp::BlkReqRead => { + // Reset retry chance here + self.retry = RETRY_CHANCE; + self.request = Some(request); + break; + } + BlkOp::BlkReqWrite => { + // Reset retry chance here + self.retry = RETRY_CHANCE; + self.request = Some(request); + break; + }, + _ => { + // For other request, enqueue response + notify_virt = true; + unsafe { + blk_enqueue_resp_helper(BlkStatus::BlkRespOk, 0, request.id); } } - // Enqueue resp - unsafe { - blk_enqueue_resp_helper(resp_status, success_count as u32, id); + } + } + // If future is empty + if let Some(request) = &mut self.request { + if let None = self.future { + match request.request_code { + BlkOp::BlkReqRead => { + // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer + request.count_to_do = core::cmp::min(request.count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); + if let Some(sdmmc) = self.sdmmc.take() { + self.future = Some(Box::pin(sdmmc.read_block(request.count_to_do as u32, request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64))); + } + else { + panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") + } + } + BlkOp::BlkReqWrite => { + // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer + request.count_to_do = core::cmp::min(request.count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); + if let Some(sdmmc) = self.sdmmc.take() { + self.future = Some(Box::pin(sdmmc.write_block(request.count_to_do as u32, request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64))); + } + else { + panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") + } + } + _ => { + panic!("SDMMC_DRIVER: You should not reach here!") + } } - // debug_println!("SDMMC_DRIVER: Notify BLK_VIRTUALIZER"); - BLK_VIRTUALIZER.notify(); } } - _ => { - debug_println!("SDMMC_DRIVER: MESSAGE FROM UNKNOWN CHANNEL: {}", channel.index()); + else { + // If Request is empty, means there are no future available, so we do not need to poll again + break; } } + if notify_virt == true { + BLK_VIRTUALIZER.notify(); + } Ok(()) } -} \ No newline at end of file +} + +/* + // Code block to test block read + + { + let test_hal: &mut MesonSdmmcRegisters = MesonSdmmcRegisters::new(); + let test: SdmmcProtocol<'static, MesonSdmmcRegisters> = SdmmcProtocol::new(test_hal); + debug_println!("Read and Print out the content in sector 0, sector 1"); + let mut future = Box::pin(test.read_block(2, 0, 0x50000000)); + let waker = create_dummy_waker(); + let mut cx = Context::from_waker(&waker); + let future_ref = &mut future; + // TODO: I can get rid of this loop once I configure out how to enable interrupt from Linux kernel driver + loop { + match future_ref.as_mut().poll(&mut cx) { + Poll::Ready((result, sdmmc)) => { + // debug_println!("SDMMC_DRIVER: Future completed with result"); + if result.is_err() { + debug_println!("SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possiblely retry!"); + } + else { + debug_println!("Content in sector 0:"); + print_one_block(0x50000000 as *const u8); + debug_println!("Content in sector 1:"); + print_one_block((0x50000000 + 512) as *const u8); + } + break; + } + Poll::Pending => { + // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); + } + } + } + } + + // Poll on the future once to start it up + let waker = create_dummy_waker(); + let mut cx = Context::from_waker(&waker); + if let Some(new_future) = self.future { + let res = new_future.as_mut().poll(&mut cx); + // If the first poll on the future is not pending, why are you even use async then? + match res { + Poll::Pending => { + // The future is pending, this is the desired case + } + Poll::Ready(_) => { + // The future is ready, handle the result here if needed + panic!("Expected Poll::Pending but got Poll::Ready. Why are you even use async if the first poll on the future is not pending?"); + } + } + } +*/ \ No newline at end of file diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs index e0026c2fc..0325bec3c 100644 --- a/drivers/blk/sdmmc/src/sddf_blk/mod.rs +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -24,3 +24,14 @@ pub enum BlkStatus { BlkRespOk, BlkRespSeekError, } + +pub struct BlkRequest { + pub request_code: BlkOp, + pub io_or_offset: u64, + pub block_number: u32, + pub count: u16, + // I suggest use u32 here and change the count to use u32 in sddf_blk + pub success_count: u32, + pub count_to_do: u32, + pub id: u32, +} \ No newline at end of file From 915fb9b30b606f4708dff9e385d3c5aa7d590b0e Mon Sep 17 00:00:00 2001 From: Cheng Date: Tue, 8 Oct 2024 22:07:09 +1100 Subject: [PATCH 03/48] sdmmc: Can only get the first interrupt Signed-off-by: Cheng --- drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 38 ++++++++++++++++++++--- drivers/blk/sdmmc/src/main.rs | 22 +++++++++---- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs index 4c0bd047e..246e6f575 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -95,6 +95,38 @@ pub trait SdmmcHardware { } } +fn send_cmd_and_receive_resp( + hardware: &mut T, + cmd: &SdmmcCmd, + data: Option<&MmcData>, + resp: &mut [u32; 4] +) -> Result<(), SdmmcHalError> { + // Send the command using the hardware layer + let mut res = hardware.sdmmc_send_command(cmd, data); + if res.is_err() { + return res; + } + + // TODO: Change it to use the sleep function provided by the hardware layer + let mut retry: u32 = 1000000; + + while retry > 0 { + // Try to receive the response + res = hardware.sdmmc_receive_response(cmd, resp); + + if let Err(SdmmcHalError::EBUSY) = res { + // Busy response, retry + retry -= 1; + // hardware.sleep(1); // Placeholder: Implement a sleep function in SdmmcHardware trait + } else { + // If any other error or success, break the loop + break; + } + } + + res // Return the final result (Ok or Err) +} + /// TODO: Add more variables for SdmmcProtocol to track the state of the sdmmc controller and card correctly pub struct SdmmcProtocol<'a, T: SdmmcHardware> { pub hardware: &'a mut T, @@ -178,8 +210,7 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { resp_type: MMC_RSP_R1B, cmdarg: 0, }; - let future = SdmmcCmdFuture::new(self.hardware, &cmd, None, &mut resp); - res = future.await; + res = send_cmd_and_receive_resp(self.hardware, &cmd, None, &mut resp); return (res.map_err(|_| SdmmcHalError::ESTOPCMD), Some(self)); } else { return (res, Some(self)); @@ -231,8 +262,7 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { resp_type: MMC_RSP_R1B, cmdarg: 0, }; - let future = SdmmcCmdFuture::new(self.hardware, &cmd, None, &mut resp); - res = future.await; + res = send_cmd_and_receive_resp(self.hardware, &cmd, None, &mut resp); return (res.map_err(|_| SdmmcHalError::ESTOPCMD), Some(self)); } else { return (res, Some(self)); diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 75cc7346a..e54f434ab 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -92,6 +92,11 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { fn notified(&mut self, channel: Channel) -> Result<(), Self::Error> { debug_println!("SDMMC_DRIVER: MESSAGE FROM CHANNEL: {}", channel.index()); let mut notify_virt: bool = false; + if channel.index() == 1 { + if let Some(sdmmc) = &mut self.sdmmc { + sdmmc.ack_interrupt(); + } + } loop { // Polling if receive any notification, it is to poll even the notification is not from the interrupt as polling is cheap if let Some(request) = &mut self.request { @@ -99,6 +104,7 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { let waker = create_dummy_waker(); let mut cx = Context::from_waker(&waker); // TODO: I can get rid of this loop once I configure out how to enable interrupt from Linux kernel driver + debug_println!("SDMMC_DRIVER: Polling future!"); match future.as_mut().poll(&mut cx) { Poll::Ready((result, sdmmc)) => { // debug_println!("SDMMC_DRIVER: Future completed with result"); @@ -153,13 +159,15 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { &mut request.id as *mut u32, ); } + request.block_number = request.block_number * SDDF_TO_REAL_SECTOR; + request.count = request.count * SDDF_TO_REAL_SECTOR as u16; // Print the retrieved values - /* - debug_println!("io_or_offset: 0x{:x}", io_or_offset);// Simple u64 - debug_println!("block_number: {}", block_number); // Simple u32 - debug_println!("count: {}", count); // Simple u16 - debug_println!("id: {}", id); // Simple u32 - */ + + debug_println!("io_or_offset: 0x{:x}", request.io_or_offset);// Simple u64 + debug_println!("block_number: {}", request.block_number); // Simple u32 + debug_println!("count: {}", request.count); // Simple u16 + debug_println!("id: {}", request.id); // Simple u32 + match request.request_code { BlkOp::BlkReqRead => { // Reset retry chance here @@ -188,6 +196,7 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { match request.request_code { BlkOp::BlkReqRead => { // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer + debug_println!("Read future created here!"); request.count_to_do = core::cmp::min(request.count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); if let Some(sdmmc) = self.sdmmc.take() { self.future = Some(Box::pin(sdmmc.read_block(request.count_to_do as u32, request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64))); @@ -218,6 +227,7 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { } } if notify_virt == true { + debug_println!("SDMMC_DRIVER: Notify the BLK_VIRTUALIZER!"); BLK_VIRTUALIZER.notify(); } Ok(()) From 0b5b909d1ad312040a03fbf1217ee8bce5c84fad Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 9 Oct 2024 12:55:26 +1100 Subject: [PATCH 04/48] sdmmc: Interrupt is working, but the code definitely need some polishing Signed-off-by: Cheng --- drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 36 +++++++++++++++++++++-- drivers/blk/sdmmc/src/main.rs | 34 ++++++++++++--------- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs index 246e6f575..efb974edf 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -191,6 +191,11 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { }; let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); res = future.await; + + // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt + // for read/write requests? + let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + return (res, Some(self)); } else { @@ -201,6 +206,11 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { }; let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); res = future.await; + + // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt + // for read/write requests? + let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + if let Ok(()) = res { // Uboot code for determine response type in this case // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; @@ -210,7 +220,13 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { resp_type: MMC_RSP_R1B, cmdarg: 0, }; - res = send_cmd_and_receive_resp(self.hardware, &cmd, None, &mut resp); + let future = SdmmcCmdFuture::new(self.hardware, &cmd, None, &mut resp); + res = future.await; + + // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt + // for read/write requests? + let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + return (res.map_err(|_| SdmmcHalError::ESTOPCMD), Some(self)); } else { return (res, Some(self)); @@ -243,6 +259,11 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { }; let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); res = future.await; + + // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt + // for read/write requests? + let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + return (res, Some(self)); } else { @@ -253,6 +274,11 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { }; let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); res = future.await; + + // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt + // for read/write requests? + let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + if let Ok(()) = res { // Uboot code for determine response type in this case // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; @@ -262,7 +288,13 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { resp_type: MMC_RSP_R1B, cmdarg: 0, }; - res = send_cmd_and_receive_resp(self.hardware, &cmd, None, &mut resp); + let future = SdmmcCmdFuture::new(self.hardware, &cmd, None, &mut resp); + res = future.await; + + // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt + // for read/write requests? + let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + return (res.map_err(|_| SdmmcHalError::ESTOPCMD), Some(self)); } else { return (res, Some(self)); diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index e54f434ab..9a4ad576f 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -90,13 +90,20 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { /// In Rust, it is actually very hard to manage long live future object that must be created /// by borrowing fn notified(&mut self, channel: Channel) -> Result<(), Self::Error> { - debug_println!("SDMMC_DRIVER: MESSAGE FROM CHANNEL: {}", channel.index()); - let mut notify_virt: bool = false; - if channel.index() == 1 { - if let Some(sdmmc) = &mut self.sdmmc { - sdmmc.ack_interrupt(); + // debug_println!("SDMMC_DRIVER: MESSAGE FROM CHANNEL: {}", channel.index()); + + if channel.index() != INTERRUPT.index() && channel.index() != BLK_VIRTUALIZER.index() { + debug_println!("SDMMC_DRIVER: Unknown channel sent me message: {}", channel.index()); + } + + if channel.index() == INTERRUPT.index() { + let err = channel.irq_ack(); + if err.is_err() { + panic!("SDMMC: Cannot acknowledge interrupt for CPU!") } } + + let mut notify_virt: bool = false; loop { // Polling if receive any notification, it is to poll even the notification is not from the interrupt as polling is cheap if let Some(request) = &mut self.request { @@ -104,7 +111,7 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { let waker = create_dummy_waker(); let mut cx = Context::from_waker(&waker); // TODO: I can get rid of this loop once I configure out how to enable interrupt from Linux kernel driver - debug_println!("SDMMC_DRIVER: Polling future!"); + // debug_println!("SDMMC_DRIVER: Polling future!"); match future.as_mut().poll(&mut cx) { Poll::Ready((result, sdmmc)) => { // debug_println!("SDMMC_DRIVER: Future completed with result"); @@ -162,12 +169,12 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { request.block_number = request.block_number * SDDF_TO_REAL_SECTOR; request.count = request.count * SDDF_TO_REAL_SECTOR as u16; // Print the retrieved values - - debug_println!("io_or_offset: 0x{:x}", request.io_or_offset);// Simple u64 - debug_println!("block_number: {}", request.block_number); // Simple u32 - debug_println!("count: {}", request.count); // Simple u16 - debug_println!("id: {}", request.id); // Simple u32 - + /* + debug_println!("io_or_offset: 0x{:x}", io_or_offset);// Simple u64 + debug_println!("block_number: {}", block_number); // Simple u32 + debug_println!("count: {}", count); // Simple u16 + debug_println!("id: {}", id); // Simple u32 + */ match request.request_code { BlkOp::BlkReqRead => { // Reset retry chance here @@ -196,7 +203,6 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { match request.request_code { BlkOp::BlkReqRead => { // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer - debug_println!("Read future created here!"); request.count_to_do = core::cmp::min(request.count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); if let Some(sdmmc) = self.sdmmc.take() { self.future = Some(Box::pin(sdmmc.read_block(request.count_to_do as u32, request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64))); @@ -227,7 +233,7 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { } } if notify_virt == true { - debug_println!("SDMMC_DRIVER: Notify the BLK_VIRTUALIZER!"); + // debug_println!("SDMMC_DRIVER: Notify the BLK_VIRTUALIZER!"); BLK_VIRTUALIZER.notify(); } Ok(()) From e89d382fd0b2f7974805a75ae5e0620205de2c54 Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 14 Oct 2024 15:37:02 +1100 Subject: [PATCH 05/48] sdmmc: Add support for blk example on odroidc4 Signed-off-by: Cheng --- .../sdmmc/{sdmmc_driver.mk => blk_driver.mk} | 6 +- drivers/blk/sdmmc/src/main.rs | 5 +- examples/blk/blk.mk | 11 ++- examples/blk/blk_config.h | 97 +++++++++++++++++++ examples/blk/board/odroidc4/blk.system | 66 +++++++++++++ 5 files changed, 178 insertions(+), 7 deletions(-) rename drivers/blk/sdmmc/{sdmmc_driver.mk => blk_driver.mk} (89%) create mode 100644 examples/blk/blk_config.h create mode 100644 examples/blk/board/odroidc4/blk.system diff --git a/drivers/blk/sdmmc/sdmmc_driver.mk b/drivers/blk/sdmmc/blk_driver.mk similarity index 89% rename from drivers/blk/sdmmc/sdmmc_driver.mk rename to drivers/blk/sdmmc/blk_driver.mk index ca169eb8a..2e47f0d0d 100644 --- a/drivers/blk/sdmmc/sdmmc_driver.mk +++ b/drivers/blk/sdmmc/blk_driver.mk @@ -27,10 +27,10 @@ blk/sdmmc/meson: mkdir -p $@ # Main build target -sdmmc_driver.elf: $(BUILD_DIR) blk/sdmmc/meson/libsddfblk.a +blk_driver.elf: $(BUILD_DIR) blk/sdmmc/meson/libsddfblk.a cp blk/sdmmc/meson/libsddfblk.a $(abspath ${SDMMC_DRIVER_DIR}) @cd $(abspath ${SDMMC_DRIVER_DIR}) && \ - echo "Building sdmmc_driver.elf for board $(MICROKIT_BOARD)..." && \ + echo "Building blk_driver.elf for board $(MICROKIT_BOARD)..." && \ echo "MICROKIT SDK config directory: $(microkit_sdk_config_dir)" && \ echo "SEl4 include directories: $(sel4_include_dirs)" && \ SEL4_INCLUDE_DIRS=$(abspath $(sel4_include_dirs)) \ @@ -39,5 +39,5 @@ sdmmc_driver.elf: $(BUILD_DIR) blk/sdmmc/meson/libsddfblk.a -Z build-std-features=compiler-builtins-mem \ --target-dir $(BUILD_DIR)/blk/sdmmc/meson/ \ --target support/targets/aarch64-sel4-microkit-minimal.json && \ - cp $(BUILD_DIR)/blk/sdmmc/meson/aarch64-sel4-microkit-minimal/debug/sdmmc_driver.elf $(BUILD_DIR) + cp $(BUILD_DIR)/blk/sdmmc/meson/aarch64-sel4-microkit-minimal/debug/blk_driver.elf $(BUILD_DIR) echo "Build complete: $(TARGET_ELF)" \ No newline at end of file diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 9a4ad576f..eebe889e9 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -129,12 +129,13 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { if request.count == 0 { let resp_status = BlkStatus::BlkRespOk; notify_virt = true; - unsafe { blk_enqueue_resp_helper(resp_status, request.success_count, request.id); } + // Have to divide the SDDF_TO_REAL_SECTOR here, we should really use real sector + unsafe { blk_enqueue_resp_helper(resp_status, request.success_count / SDDF_TO_REAL_SECTOR, request.id); } self.request = None; } else if self.retry == 0 { let resp_status = BlkStatus::BlkRespSeekError; notify_virt = true; - unsafe { blk_enqueue_resp_helper(resp_status, request.success_count, request.id); } + unsafe { blk_enqueue_resp_helper(resp_status, request.success_count / SDDF_TO_REAL_SECTOR, request.id); } self.request = None; } } diff --git a/examples/blk/blk.mk b/examples/blk/blk.mk index f9c4fb27a..1361547e5 100644 --- a/examples/blk/blk.mk +++ b/examples/blk/blk.mk @@ -60,6 +60,10 @@ else ifeq ($(strip $(MICROKIT_BOARD)), maaxboard) SERIAL_DRIVER_DIR := imx BLK_DRIVER_DIR := mmc/imx TIMER_DRIVER_DIR := imx +else ifeq ($(strip $(MICROKIT_BOARD)), odroidc4) + ARCH := aarch64 + CPU := cortex-a55 + BLK_DRIVER_DIR := sdmmc else $(error Unsupported MICROKIT_BOARD given) endif @@ -79,8 +83,11 @@ BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) ARCH := ${shell grep 'CONFIG_SEL4_ARCH ' $(BOARD_DIR)/include/kernel/gen_config.h | cut -d' ' -f4} SDDF_CUSTOM_LIBC := 1 -IMAGES := blk_driver.elf client.elf blk_virt.elf serial_virt_tx.elf serial_driver.elf -CFLAGS := -nostdlib \ +IMAGES := client.elf blk_virt.elf serial_virt_tx.elf serial_driver.elf + +CFLAGS := -mcpu=$(CPU) \ + -mstrict-align \ + -nostdlib \ -ffreestanding \ -g3 \ -O3 \ diff --git a/examples/blk/blk_config.h b/examples/blk/blk_config.h new file mode 100644 index 000000000..ebfc15f63 --- /dev/null +++ b/examples/blk/blk_config.h @@ -0,0 +1,97 @@ +/* + * Copyright 2024, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +#define BLK_NUM_CLIENTS 1 + +#define BLK_NAME_CLI0 "client" + +#define BLK_QUEUE_CAPACITY_CLI0 1024 +#define BLK_QUEUE_CAPACITY_DRIV BLK_QUEUE_CAPACITY_CLI0 + +#define BLK_QUEUE_REGION_SIZE 0x200000 +#define BLK_DATA_REGION_SIZE_CLI0 BLK_QUEUE_REGION_SIZE +#define BLK_DATA_REGION_SIZE_DRIV BLK_QUEUE_REGION_SIZE + +#define BLK_QUEUE_REGION_SIZE_CLI0 BLK_QUEUE_REGION_SIZE +#define BLK_QUEUE_REGION_SIZE_DRIV BLK_QUEUE_REGION_SIZE + +/* Mapping from client index to disk partition that the client will have access to. */ +// Please ensure this point to a partition that you do not mind to courrupt as the example involves write data to that component +static const int blk_partition_mapping[BLK_NUM_CLIENTS] = { 0 }; + +static inline blk_storage_info_t *blk_virt_cli_storage_info(blk_storage_info_t *info, unsigned int id) +{ + switch (id) { + case 0: + return info; + default: + return NULL; + } +} + +static inline uintptr_t blk_virt_cli_data_region(uintptr_t data, unsigned int id) +{ + switch (id) { + case 0: + return data; + default: + return 0; + } +} + +static inline uint64_t blk_virt_cli_data_region_size(unsigned int id) +{ + switch (id) { + case 0: + return BLK_DATA_REGION_SIZE_CLI0; + default: + return 0; + } +} + +static inline blk_req_queue_t *blk_virt_cli_req_queue(blk_req_queue_t *req, unsigned int id) +{ + switch (id) { + case 0: + return req; + default: + return NULL; + } +} + +static inline blk_resp_queue_t *blk_virt_cli_resp_queue(blk_resp_queue_t *resp, unsigned int id) +{ + switch (id) { + case 0: + return resp; + default: + return NULL; + } +} + +static inline uint32_t blk_virt_cli_queue_capacity(unsigned int id) +{ + switch (id) { + case 0: + return BLK_QUEUE_CAPACITY_CLI0; + default: + return 0; + } +} + +static inline uint32_t blk_cli_queue_capacity(char *pd_name) +{ + if (!sddf_strcmp(pd_name, BLK_NAME_CLI0)) { + return BLK_QUEUE_CAPACITY_CLI0; + } else { + return 0; + } +} diff --git a/examples/blk/board/odroidc4/blk.system b/examples/blk/board/odroidc4/blk.system new file mode 100644 index 000000000..c260a6a68 --- /dev/null +++ b/examples/blk/board/odroidc4/blk.system @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ade5c2e2b9c8693e643aa79dee3f75f713d78500 Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 21 Oct 2024 13:20:40 +1100 Subject: [PATCH 06/48] sdmmc: some clean up before pull request Signed-off-by: Cheng --- .../blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 26 ++- drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 3 + .../sdmmc_protocol/sdmmc/sdmmc_constant.rs | 2 +- drivers/blk/sdmmc/src/main.rs | 157 ++++++++---------- drivers/blk/sdmmc/src/sddf_blk/mod.rs | 14 +- 5 files changed, 100 insertions(+), 102 deletions(-) diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs index 28bd76c67..5828ff69c 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs @@ -5,11 +5,16 @@ use sel4_microkit::debug_println; const SDIO_BASE: u64 = 0xffe05000; // Base address from DTS +macro_rules! div_round_up { + ($n:expr, $d:expr) => { + (($n + $d - 1) / $d) + }; +} + // Constants translated from the C version // Clock related constant const SD_EMMC_CLKSRC_24M: u32 = 24000000; // 24 MHz const SD_EMMC_CLKSRC_DIV2: u32 = 1000000000; // 1 GHz - const CLK_MAX_DIV: u32 = 63; const CLK_SRC_24M: u32 = 0 << 6; const CLK_SRC_DIV2: u32 = 1 << 6; @@ -21,12 +26,6 @@ const CLK_TX_PHASE_000: u32 = 0 << 10; const CLK_TX_PHASE_180: u32 = 2 << 10; const CLK_ALWAYS_ON: u32 = 1 << 24; -macro_rules! div_round_up { - ($n:expr, $d:expr) => { - (($n + $d - 1) / $d) - }; -} - // CMD_CFG constants const CMD_CFG_CMD_INDEX_SHIFT: u32 = 24; const CMD_CFG_RESP_128: u32 = 1 << 21; @@ -167,7 +166,7 @@ impl MesonSdmmcRegisters { /// * `mmc_clock` - The desired clock frequency in Hz. /// * `is_sm1_soc` - A boolean indicating whether the SoC is an SM1 variant. /// * For odorid C4, this is_sm1_soc is true - pub fn meson_mmc_config_clock(&mut self) { + fn meson_mmc_config_clock(&mut self, frequency: u32) { // #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) let mut meson_mmc_clk:u32 = 0; @@ -177,7 +176,7 @@ impl MesonSdmmcRegisters { let clk: u32; let clk_src: u32; // 400 khz for init the card - let clock_freq: u32 = 400000; + let clock_freq: u32 = frequency; if clock_freq > 16000000 { clk = SD_EMMC_CLKSRC_DIV2; clk_src = CLK_SRC_DIV2; @@ -194,6 +193,7 @@ impl MesonSdmmcRegisters { * Other SoCs use CLK_CO_PHASE_180 by default. * It needs to find what is a proper value about each SoCs. * Since we are using Odroid C4, we set phase to 270 + * TODO: Config it as what Linux driver are doing */ meson_mmc_clk |= CLK_CO_PHASE_270; meson_mmc_clk |= CLK_TX_PHASE_000; @@ -204,6 +204,14 @@ impl MesonSdmmcRegisters { unsafe { ptr::write_volatile(&mut self.clock, meson_mmc_clk); } } + // Incomplete placeholder function, need regulator system to configure voltage + pub fn meson_set_ios(&mut self) { + /* + * This function should be able to adjust the voltage, frequency and number of data lanes in use + * + */ + } + // This function can be seen as a Rust version of meson_mmc_setup_cmd function in uboot fn meson_mmc_set_up_cmd_cfg_and_cfg(&mut self, cmd: &SdmmcCmd, data: Option<&MmcData>) { let mut meson_mmc_cmd: u32 = 0u32; diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs index efb974edf..52cf6adf3 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -95,6 +95,7 @@ pub trait SdmmcHardware { } } +// Not used right now, but would be useful in the future once we want to execute some command synchronously fn send_cmd_and_receive_resp( hardware: &mut T, cmd: &SdmmcCmd, @@ -143,8 +144,10 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { } } + // Funtion that is not completed pub fn reset_card(&mut self) -> Result<(), SdmmcHalError> { let all: u32 = InterruptType::Success as u32 | InterruptType::Error as u32 | InterruptType::SDIO as u32; + todo!(); self.hardware.sdmmc_ack_interrupt(&all) } diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs index 18995645d..962c43a29 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs @@ -46,4 +46,4 @@ pub const SD_CMD_APP_SD_STATUS: u32 = 13; pub const SD_CMD_ERASE_WR_BLK_START: u32 = 32; pub const SD_CMD_ERASE_WR_BLK_END: u32 = 33; pub const SD_CMD_APP_SEND_OP_COND: u32 = 41; -pub const SD_CMD_APP_SEND_SCR: u32 = 51; +pub const SD_CMD_APP_SEND_SCR: u32 = 51; \ No newline at end of file diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index eebe889e9..3f2e5dd5f 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -1,14 +1,21 @@ -#![no_std] // Don't link the standard library +#![no_std] // Don't link the standard library #![no_main] // Don't use the default entry point extern crate alloc; mod sddf_blk; -use core::{future::Future, pin::Pin, task::{Context, Poll, RawWaker, RawWakerVTable, Waker}}; +use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, +}; use alloc::boxed::Box; -use sddf_blk::{blk_dequeue_req_helper, blk_enqueue_resp_helper, blk_queue_empty_req_helper, blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkRequest, BlkStatus}; +use sddf_blk::{ + blk_dequeue_req_helper, blk_enqueue_resp_helper, blk_queue_empty_req_helper, + blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkRequest, BlkStatus, +}; use sdmmc_hal::meson_gx_mmc::MesonSdmmcRegisters; use sdmmc_protocol::sdmmc::{InterruptType, SdmmcHalError, SdmmcHardware, SdmmcProtocol}; @@ -20,10 +27,12 @@ const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(1); const SDCARD_SECTOR_SIZE: u32 = 512; const SDDF_TRANSFER_SIZE: u32 = 4096; -const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE/SDCARD_SECTOR_SIZE; +const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE; const RETRY_CHANCE: u16 = 5; +// Debug function for printing out content in one block +#[allow(dead_code)] fn print_one_block(ptr: *const u8) { unsafe { // Iterate over the 512 bytes and print each one in hexadecimal format @@ -45,12 +54,7 @@ unsafe fn noop_clone(_data: *const ()) -> RawWaker { } // A VTable that points to the no-op functions -static VTABLE: RawWakerVTable = RawWakerVTable::new( - noop_clone, - noop, - noop, - noop, -); +static VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); // Function to create a dummy Waker fn create_dummy_waker() -> Waker { @@ -78,7 +82,13 @@ fn init() -> HandlerImpl<'static, MesonSdmmcRegisters> { } struct HandlerImpl<'a, T: SdmmcHardware> { - future: Option, Option>)> + 'a>>>, + future: Option< + Pin< + Box< + dyn Future, Option>)> + 'a, + >, + >, + >, sdmmc: Option>, request: Option, retry: u16, @@ -87,13 +97,15 @@ struct HandlerImpl<'a, T: SdmmcHardware> { impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { type Error = Infallible; - /// In Rust, it is actually very hard to manage long live future object that must be created - /// by borrowing fn notified(&mut self, channel: Channel) -> Result<(), Self::Error> { // debug_println!("SDMMC_DRIVER: MESSAGE FROM CHANNEL: {}", channel.index()); if channel.index() != INTERRUPT.index() && channel.index() != BLK_VIRTUALIZER.index() { - debug_println!("SDMMC_DRIVER: Unknown channel sent me message: {}", channel.index()); + debug_println!( + "SDMMC_DRIVER: Unknown channel sent me message: {}", + channel.index() + ); + return Ok(()); } if channel.index() == INTERRUPT.index() { @@ -110,18 +122,17 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { if let Some(future) = &mut self.future { let waker = create_dummy_waker(); let mut cx = Context::from_waker(&waker); - // TODO: I can get rid of this loop once I configure out how to enable interrupt from Linux kernel driver - // debug_println!("SDMMC_DRIVER: Polling future!"); match future.as_mut().poll(&mut cx) { Poll::Ready((result, sdmmc)) => { // debug_println!("SDMMC_DRIVER: Future completed with result"); self.future = None; // Reset the future once done self.sdmmc = sdmmc; if result.is_err() { - debug_println!("SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possibly retry!"); + debug_println!( + "SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possibly retry!" + ); self.retry -= 1; - } - else { + } else { // Deduct finished count from count request.success_count += request.count_to_do; request.count -= request.count_to_do as u16; @@ -129,13 +140,24 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { if request.count == 0 { let resp_status = BlkStatus::BlkRespOk; notify_virt = true; - // Have to divide the SDDF_TO_REAL_SECTOR here, we should really use real sector - unsafe { blk_enqueue_resp_helper(resp_status, request.success_count / SDDF_TO_REAL_SECTOR, request.id); } + unsafe { + blk_enqueue_resp_helper( + resp_status, + request.success_count / SDDF_TO_REAL_SECTOR, + request.id, + ); + } self.request = None; } else if self.retry == 0 { let resp_status = BlkStatus::BlkRespSeekError; notify_virt = true; - unsafe { blk_enqueue_resp_helper(resp_status, request.success_count / SDDF_TO_REAL_SECTOR, request.id); } + unsafe { + blk_enqueue_resp_helper( + resp_status, + request.success_count / SDDF_TO_REAL_SECTOR, + request.id, + ); + } self.request = None; } } @@ -148,7 +170,9 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { } } - while self.request.is_none() && unsafe { blk_queue_empty_req_helper() == 0 && blk_queue_full_resp_helper() == 0 } { + while self.request.is_none() + && unsafe { blk_queue_empty_req_helper() == 0 && blk_queue_full_resp_helper() == 0 } + { let mut request: BlkRequest = BlkRequest { request_code: BlkOp::BlkReqFlush, io_or_offset: 0, @@ -188,7 +212,7 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { self.retry = RETRY_CHANCE; self.request = Some(request); break; - }, + } _ => { // For other request, enqueue response notify_virt = true; @@ -204,21 +228,35 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { match request.request_code { BlkOp::BlkReqRead => { // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer - request.count_to_do = core::cmp::min(request.count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); + request.count_to_do = core::cmp::min( + request.count as u32, + sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER, + ); if let Some(sdmmc) = self.sdmmc.take() { - self.future = Some(Box::pin(sdmmc.read_block(request.count_to_do as u32, request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64))); - } - else { + self.future = Some(Box::pin(sdmmc.read_block( + request.count_to_do as u32, + request.block_number as u64 + request.success_count as u64, + request.io_or_offset + + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64, + ))); + } else { panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") } } BlkOp::BlkReqWrite => { // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer - request.count_to_do = core::cmp::min(request.count as u32, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER); + request.count_to_do = core::cmp::min( + request.count as u32, + sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER, + ); if let Some(sdmmc) = self.sdmmc.take() { - self.future = Some(Box::pin(sdmmc.write_block(request.count_to_do as u32, request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64))); - } - else { + self.future = Some(Box::pin(sdmmc.write_block( + request.count_to_do as u32, + request.block_number as u64 + request.success_count as u64, + request.io_or_offset + + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64, + ))); + } else { panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") } } @@ -227,8 +265,7 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { } } } - } - else { + } else { // If Request is empty, means there are no future available, so we do not need to poll again break; } @@ -240,55 +277,3 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { Ok(()) } } - -/* - // Code block to test block read - - { - let test_hal: &mut MesonSdmmcRegisters = MesonSdmmcRegisters::new(); - let test: SdmmcProtocol<'static, MesonSdmmcRegisters> = SdmmcProtocol::new(test_hal); - debug_println!("Read and Print out the content in sector 0, sector 1"); - let mut future = Box::pin(test.read_block(2, 0, 0x50000000)); - let waker = create_dummy_waker(); - let mut cx = Context::from_waker(&waker); - let future_ref = &mut future; - // TODO: I can get rid of this loop once I configure out how to enable interrupt from Linux kernel driver - loop { - match future_ref.as_mut().poll(&mut cx) { - Poll::Ready((result, sdmmc)) => { - // debug_println!("SDMMC_DRIVER: Future completed with result"); - if result.is_err() { - debug_println!("SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possiblely retry!"); - } - else { - debug_println!("Content in sector 0:"); - print_one_block(0x50000000 as *const u8); - debug_println!("Content in sector 1:"); - print_one_block((0x50000000 + 512) as *const u8); - } - break; - } - Poll::Pending => { - // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); - } - } - } - } - - // Poll on the future once to start it up - let waker = create_dummy_waker(); - let mut cx = Context::from_waker(&waker); - if let Some(new_future) = self.future { - let res = new_future.as_mut().poll(&mut cx); - // If the first poll on the future is not pending, why are you even use async then? - match res { - Poll::Pending => { - // The future is pending, this is the desired case - } - Poll::Ready(_) => { - // The future is ready, handle the result here if needed - panic!("Expected Poll::Pending but got Poll::Ready. Why are you even use async if the first poll on the future is not pending?"); - } - } - } -*/ \ No newline at end of file diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs index 0325bec3c..e58937601 100644 --- a/drivers/blk/sdmmc/src/sddf_blk/mod.rs +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -4,11 +4,13 @@ extern "C" { pub fn blk_queue_empty_req_helper() -> u8; pub fn blk_queue_full_resp_helper() -> u8; pub fn blk_enqueue_resp_helper(status: BlkStatus, success: u32, id: u32) -> u8; - pub fn blk_dequeue_req_helper(code: *mut BlkOp, - io_or_offset: *mut u64, - block_number: *mut u32, - count: *mut u16, - id: *mut u32); + pub fn blk_dequeue_req_helper( + code: *mut BlkOp, + io_or_offset: *mut u64, + block_number: *mut u32, + count: *mut u16, + id: *mut u32, + ); } #[repr(C)] @@ -34,4 +36,4 @@ pub struct BlkRequest { pub success_count: u32, pub count_to_do: u32, pub id: u32, -} \ No newline at end of file +} From fdbbb8318071891d56d7142f04d5722d70dfa695 Mon Sep 17 00:00:00 2001 From: Ivan-Velickovic Date: Mon, 21 Oct 2024 22:11:53 +1100 Subject: [PATCH 07/48] ci: add odroidc4 for blk example Signed-off-by: Ivan-Velickovic --- drivers/blk/sdmmc/libsddfblk.a | Bin 0 -> 15714 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 drivers/blk/sdmmc/libsddfblk.a diff --git a/drivers/blk/sdmmc/libsddfblk.a b/drivers/blk/sdmmc/libsddfblk.a new file mode 100644 index 0000000000000000000000000000000000000000..935cd539c7233c86463e804bd08b419e1e88a7c0 GIT binary patch literal 15714 zcmeI34RD;rdB^wt+Pjm^vQDzBldO-kEn!QxC7msd1jL+e$+kW)vMgg82&a?oBpp8K z#NEk3#4#G;;P8>gVQ8R)VlzN$5~pzjEisb@cS_1fDYz|PDRk=4mWI$$6JTl+0)3u+ zpS^pvk}}Ms%}i&!Gw-|m{Pw@Q&+hxRw<|5aG?E$|S(n!sm>!xOrk!@41A)~ot4YzH z#~90JOuP289s`Lz;p-=26R~i7ES?FE#1iAN6dO#A4aJ99HdCacvAyxZSU43MPKWnK zqR|w~l5}z+H5f}fWum%hBoiSm)Ubv>oPW9*A)ZV>G*EAShJ80~2ma`;f zY|DkYbVwvatQ?dc#tzkK>>KkS{vO2c&{S`|Cv$fP4yo0 zu@lR`!Y1o)Ws`wAcCOyiT2C6`SE>WX%kpqTJq*f7{#k4{=-suUy2~{8p3|j=%Vk-t z6U)27FAr)wp%Zm=p}G#u(>0qG^>B+F+5`GH==XqrCFr{~Z`WZhe@}$5z5w(C>+kmb zO}V>*6`eM!>u-M~f6*Pn(Od`egePI_H6U z!eNCfR(PH(k3XEZ$W!9Ey?&k;%M68t-XV_UZ>bOPJHjQ3rl_G`r2*RXd+LKLY1R;QR=jAA$2D@cSQuI7})m=4QpvbT=HmaO{Eu zrgJO`2Ykoam*MyU94Fy;5sp{k_&FRVlr4s12^>KkNJUiRx=;edJdaswFA1=gBfSEdE@Npcc-S<(eJ|vv!-+f zP3gyUZI`Xqwt)i7ZDjV^W-l{^3L3i$)W#Rxhnaa?e+F7HpV6P!?Pj0Xrwjm7#t}mR zlsttJJA=$^U>-5B1(Dlpo6v$Vx7+4+GW%?^kJ&MJd0PKiHzo@YGtH;f))revZK|}e zbWyEuQ4vpIRBD--4BDgkaZLrwP?cCD%OnJ ze9JcYJRwD+1@c@D(_+l6X&rE3J&U3EMUb6`ZRF>#gJgcTO3-;lmHBp+^X9{)Etn5Q z7@LnJ87nlQ9xQ1qe9NJl3Xl~wfGfzBl?4{VMK87>18pp{ph3e9hWs~{Nzr9xjFnhS z_vcg#o0+~OXO>oK!QvfDcjfQS-w9s&LA)dgm6bKH>6PK|Fjk&(prcs0ut;sy7FJZU zU@_JpYp%pGmabN*g_f1s#j3IWoGyUQqC4!@FlrXlVAcpfdst;|YOWhcU?7}9zsjZQ zyIrPXtOIOv{Q+ifg47~yiDhizS_L=12&Puaxa;efsIqvTNK|v4e;<;7+g17rCKkIn zb0371=XN-ObCz4n<8D{}J;?MtO?SIB_faVb!7T~th1x=IiC-(`-ev1yo?vTjfr_~HqU$yQib|}KHufe$MgcdUUTO`AB79hLJ_3P zwQ_Hnrun&FI1X3Fs8|WybmiAy(@-w%fA(I3#wZ$s*G z-sSD~^Ub{aX|VVf#seuG5*=>d^$C*BLRRoR7I_IypW~nNURuaoc%8^=WqhSbTx{}H zd06ZcH*YM!#HBE591<(^zt-zrrrw}y?lQ3F<0WC{Vn|-X8@v|>_;TLVhb8J5zX%7E z*SmR(NUSL2t=Jj95{68)v#Nrb*MN8}haN+Gp~;JULFQYn`PS&ZW`h-c0jPNsoH}`% z7djClSJz8dH<-Nf2;}pPZsz_jB*>#jA-RQL=-uq)LB8%KRIFxvlW2d9N$oeob$A1^ z+Ig*Lzs=<3zRQ>|r1{qCz72+Nqsa=eH$_|dj^Zo$j%ov*_a1&F4sP)_z7xl`xQp+? z-mrrE;k>|TgQS+c+RQJ}db-Sf?D|5p$}E_Ir>FEID8NjwasQb-Yp*{&}cL3EFfVz*rvdk29zasj*j=zfISiFh9hQnCw z=Rd=KS6<2ASOL1qo&5DCh?%~_G7IpU72rjv@?0a@h?sTQMzyuhPLycd#>_gbs6^}a zo8k^EeHR?i%oQF#{{&Kg7l-=%?gBUmJPYxhR|V$^^wWbK<^SY?LD1f7q{?_{te?+{?RWn_VbVa3u18mHy>cUlYe{;Vpf1J2=NvuK)(vGcLn&I z6yW_`l{XIAT5`Y)ijL$rnZa{#O`#(fnL%s_I?`eWv8fVmd#f45#!9q4zZt~lO7Dh~ zZZ>=T-amnqf8O`;T<|!a=Rb+(!l&UJ@V$uVqF3Q8E?*vAasCN(wBReyocT34jk*Hf z#4eYQ5B?`ut|G>xcwt=&LOdoC3)k}@?1Rg{fe(vBc^e-QiA9@uTqG*D@aybEC*NZy zx_KAgR<7zE#~U}q3b4a$EgU#&g289#6Mhnw!eP1>m^DrIL(fli?3me0u<7`Xv0TAKa%`Ak!L`j$(iY;HsaT#>ZBWFMVX**Q{m*$P&$U)PY&)0!@V^i7W3f&k!58} zeaQ{5tj=CH$LkQuOr*($?LFZux^n6E-rn$zt=oIrH%@0ATd!Q--O(2AS>L<Gswu!fowWc5G}Xhv4c`i?$2{%I;S;WevgpcY3n&ixwfaPAS6+cOTD+j6mA8?$EpUUy?+sdywI zZ_yMyOyT5MViLwU5}jmecy8ft7B4sZUKUCFszhVsnGrTPnTU@?@g0CqB5pmwEpP8Y zO?!o4Pn{rlKQWyD;k@DhEPTdK6HLy*g-;=v;ElGg%3Dn?A$Nv0GE|PU9t^)L$Ehm@ zwVoOq&)_(M7*st@NHJUvb3Yv6zlXG@X}>O*hY?J=7#xrglYJD5U})9)e5=a7Adl|6 z?588Q-+5}^H(hHsK5)?vRA}yhKjMAeqxUboKCjVyy84e1>i=jgdhu7M{_>Tp9%)z{q{^(E zb~87=ebddD&kb*<)yF%n;ZAEk?-u#bvrfx*0CKC1M{nx1s=BO-E~~7|(z>kHybmpl z-)yz=yjw(KH6@_ne4b}K$8NUDA-}m3id)=x0$PEbJmZ(_3F}h+gte&C3Upd)Df?cT z{TBO`BiqA`zk?sH#)JFOM~xeeolwg%<53OEU0^)BA2N3c#*H^M@D@5SpZ8ck$0qFYkn6NH=f>)QgEWxb`GllrUo_KEURzD{FTOYP}4GS-iNYotEj^&?&`Mk zH(TZ1R$(_c&VH%W@^a&#-f0zgSe|Z26Mwq@3EhM{X0BP>EHkk=q^xlwooY^PHif~m4?;G3oj^1B8iC-4 zq!N}3KJyn@rQlpq+or)>GkB5E&*UgHW_jbggns6iKo=Z-;m#5a{6h;R{03ibvv8z_ z_-%@*TsSNA^6j1Dn1V&7=>V+vg?=Wt_48-wTZI2J`6|JEGxT;n`1^T!SR(W@IjRg+ zGF=Q-snE~lwjS2kmEaZnnfz;@D{|ad^85DyjDyed%RNN(9ThxxU&-&88#va}LpnL) z5wLwMcVBIxWEJWA6@4pl`F)f1JP10he~R?7-Is|UAznlA8RDnNmfz8z5O0EZFvxan z3syFOSAZe!RN2!Kh0A=@SIWW=OMg-CI-d? z^$@Qlz2wp_9k}ytBcD*@_|!|Ce6o-`pBg!DL|)|3<7yZNS>QaElOVQ90Xtlog}#<0bXvfM|! zTjAxzdlX(zyiehm65plpHsbOOw_$h@yN&F>+|1I&?6#hE#Hx&L3@zV-FOZ;CI?lNrf3xzKr z{vQfoO#F<(ml6L!;f=)4D!i5W#|qy}9DiRClxzF(z&9r2K&-$q=%ccs3c_!dP!KwQo%r2abMa$X^Mns}d5&q3n56#gyZ*C_lL z@qUFLCmvP!pAjEX_zB_(g+EJtT;XpJ&nWy|;x{PVq@RrY6kbkzO5qn0KcMh!#J{TW z1o1l*evtSfg&!tELV6B z*-Z-XC%Z-A<79^vK1KExg&!eXwuAnoKc|TAqFC}K8W-6|e17HJ9;E?APbO=ogsNBl zmwt55p_lp6pMKIs=g^N3m-=zC_syZ7QuNZkYYzQAiv9@M&UuS-e2y#nQ)E9yu{u7_ zD0(^mOB{OmbE#~&K;bLMu2=X*vhgnwFkrh_Ph41xbx|z&!({g>{HJ73DEtMoZ&UcY zWXs=AvVQz4Fbt2`QP!W8#Gh4oJMp)O%j-)fV-3;Rz{GGkGBA*e?PU$ASR&GpiQSN4 z4Y*htgKaHRq{fC5@pL9FZP=9|Ta>Y@NyApNy;BLgfwU0Q{}##rB%~s&u=wv(P9V0w8^)5ESi|P6 zJ66Ho=-?jKfJ@e5#~%Jr07edX{luRyIJc}tm!{PP8)dO+U?>$Cjj`!}D~Ry?)f=^{L>#nXj&3O3_eoPujGDNe!Fm=vdAP{_ooiPhZD(3HKCey7C0 zzr7t+Uz%#OU*rVl*5<)oy`2m7)AKD*u#{shDgdXlL{P4EaBjhqc+?9xueI z|0!Ttr}{YFM@#z`D-VWjU&dEKLZ+dbY8uD6*HU>W!?db>W{&dxRQ@%k0_nfRxK1@&|NE&! zQ`8{#A1{e4FYmv{=V-rWk=?>JdX8m#RGl>059etAbkLRr9PQ&gOKty4z)+%&|E)9` zm+OLdZMpH2e&8Q-8r`2JA%9A0L`?5*$>?^PG5+sBd8Lb@$eAj; Date: Tue, 22 Oct 2024 16:51:38 +1100 Subject: [PATCH 08/48] sdmmc: update to latest sddf Signed-off-by: Cheng --- drivers/blk/sdmmc/src/sddf_helper.c | 2 +- examples/blk/board/odroidc4/blk.system | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/sdmmc/src/sddf_helper.c index 0050ab585..4d5e039b1 100644 --- a/drivers/blk/sdmmc/src/sddf_helper.c +++ b/drivers/blk/sdmmc/src/sddf_helper.c @@ -11,7 +11,7 @@ blk_resp_queue_t *blk_resp_queue; blk_storage_info_t *blk_config; void blk_queue_init_helper() { - blk_queue_init(queue_handle, blk_req_queue, blk_resp_queue, BLK_QUEUE_SIZE_DRIV); + blk_queue_init(queue_handle, blk_req_queue, blk_resp_queue, BLK_QUEUE_CAPACITY_DRIV); blk_config->sector_size = 512; blk_config->block_size = 1; blk_config->capacity = 0xFFFFFFFFFF; diff --git a/examples/blk/board/odroidc4/blk.system b/examples/blk/board/odroidc4/blk.system index c260a6a68..64a1d1116 100644 --- a/examples/blk/board/odroidc4/blk.system +++ b/examples/blk/board/odroidc4/blk.system @@ -31,23 +31,24 @@ - - - - - + + + + + - - - - + + + + + - + From 5df9b71ed2be9b988a8864dba6cbc7ec3d478515 Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 2 Dec 2024 12:19:36 +1100 Subject: [PATCH 09/48] sdmmc: Update blk_benchmark client and point sdmmc driver to latest version of sdmmc library on my repo(temporary and only for testing) Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 9 +- drivers/blk/sdmmc/blk_driver.mk | 2 +- drivers/blk/sdmmc/src/sddf_helper.c | 27 ++- examples/blk_benchmark/Makefile | 29 ++++ examples/blk_benchmark/blk.mk | 136 ++++++++++++++++ examples/blk_benchmark/blk_config.h | 97 +++++++++++ .../blk_benchmark/board/odroidc4/blk.system | 68 ++++++++ examples/blk_benchmark/client.c | 154 ++++++++++++++++++ 8 files changed, 516 insertions(+), 6 deletions(-) create mode 100644 examples/blk_benchmark/Makefile create mode 100644 examples/blk_benchmark/blk.mk create mode 100644 examples/blk_benchmark/blk_config.h create mode 100644 examples/blk_benchmark/board/odroidc4/blk.system create mode 100644 examples/blk_benchmark/client.c diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 92dfd157c..34294306b 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -4,9 +4,12 @@ version = "0.1.0" edition = "2021" [dependencies] -sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d3790bfd15512e18659f9491c319867fabf9552d", package = "sel4-microkit" } -sdmmc_hal = { path = "sdmmc_hal/meson" } -sdmmc_protocol = { path = "sdmmc_protocol" } +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d3790bfd15512e18659f9491c319867fabf9552d" } +# Change it to based on +# sdmmc_hal = { path = "sdmmc_hal/meson", features = ["sel4-microkit"] } +# sdmmc_protocol = { path = "sdmmc_protocol" } +sdmmc_hal = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "8f6a7b95c757eed67745772274144b734c5f384d" } +sdmmc_protocol = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "8f6a7b95c757eed67745772274144b734c5f384d" } [build] build = "build.rs" \ No newline at end of file diff --git a/drivers/blk/sdmmc/blk_driver.mk b/drivers/blk/sdmmc/blk_driver.mk index 2e47f0d0d..5bab1dbe7 100644 --- a/drivers/blk/sdmmc/blk_driver.mk +++ b/drivers/blk/sdmmc/blk_driver.mk @@ -34,7 +34,7 @@ blk_driver.elf: $(BUILD_DIR) blk/sdmmc/meson/libsddfblk.a echo "MICROKIT SDK config directory: $(microkit_sdk_config_dir)" && \ echo "SEl4 include directories: $(sel4_include_dirs)" && \ SEL4_INCLUDE_DIRS=$(abspath $(sel4_include_dirs)) \ - cargo build \ + cargo build --release \ -Z build-std=core,alloc,compiler_builtins \ -Z build-std-features=compiler-builtins-mem \ --target-dir $(BUILD_DIR)/blk/sdmmc/meson/ \ diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/sdmmc/src/sddf_helper.c index 4d5e039b1..9d90c81bf 100644 --- a/drivers/blk/sdmmc/src/sddf_helper.c +++ b/drivers/blk/sdmmc/src/sddf_helper.c @@ -34,10 +34,33 @@ uint8_t blk_enqueue_resp_helper(uint8_t status, uint16_t success, uint32_t id) { return 1; } -uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint16_t *count, uint32_t *id) { +uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint32_t *count, uint32_t *id) { // It would be better if we do not use int but use int8_t + // uint16_t temp_count = 0; + if (blk_dequeue_req(queue_handle, (blk_req_code_t *)code, io_or_offset, block_number, count, id) == 0) { + return 0; + } + // *count = temp_count; + return 1; +} + +/* +uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint16_t *count, uint32_t *id) { + if (blk_dequeue_req(queue_handle, (blk_req_code_t *)code, io_or_offset, block_number, count, id) == 0) { return 0; } return 1; -} \ No newline at end of file +} + +// Why this version does not work???? +uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint32_t *count, uint32_t *id) { + + uint16_t temp_count = 0; + if (blk_dequeue_req(queue_handle, (blk_req_code_t *)code, io_or_offset, block_number, &temp_count, id) == 0) { + return 0; + } + *count = temp_count; + return 1; +} +*/ \ No newline at end of file diff --git a/examples/blk_benchmark/Makefile b/examples/blk_benchmark/Makefile new file mode 100644 index 000000000..faa33ee6c --- /dev/null +++ b/examples/blk_benchmark/Makefile @@ -0,0 +1,29 @@ +# +# Copyright 2024, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +BUILD_DIR ?= build +export SDDF=$(abspath ../..) +export BUILD_DIR:=$(abspath ${BUILD_DIR}) +export override MICROKIT_SDK:=$(abspath ${MICROKIT_SDK}) +export MICROKIT_BOARD ?= odroidc4 + +IMAGE_FILE:= ${BUILD_DIR}/loader.img +REPORT_FILE:= ${BUILD_DIR}/report.txt + +all: ${IMAGE_FILE} + +qemu ${IMAGE_FILE} ${REPORT_FILE} clean clobber: ${BUILD_DIR}/Makefile FORCE + ${MAKE} -C ${BUILD_DIR} MICROKIT_SDK=${MICROKIT_SDK} $(notdir $@) + +${BUILD_DIR}/Makefile: blk.mk + mkdir -p ${BUILD_DIR} + cp blk.mk $@ + +FORCE: diff --git a/examples/blk_benchmark/blk.mk b/examples/blk_benchmark/blk.mk new file mode 100644 index 000000000..cb4725b69 --- /dev/null +++ b/examples/blk_benchmark/blk.mk @@ -0,0 +1,136 @@ +# +# Copyright 2024, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# +# This Makefile is copied into the build directory +# and operated on from there. +# + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +ifeq ($(strip $(SDDF)),) +$(error SDDF must be specified) +endif + +ifeq ($(strip $(TOOLCHAIN)),) + TOOLCHAIN := aarch64-none-elf +endif + +ifeq ($(strip $(TOOLCHAIN)), clang) + CC := clang -target aarch64-none-elf + LD := ld.lld + AR := llvm-ar + RANLIB := llvm-ranlib +else + CC := $(TOOLCHAIN)-gcc + LD := $(TOOLCHAIN)-ld + AS := $(TOOLCHAIN)-as + AR := $(TOOLCHAIN)-ar + RANLIB := $(TOOLCHAIN)-ranlib +endif + +QEMU := qemu-system-aarch64 + +BUILD_DIR ?= build +MICROKIT_CONFIG ?= debug + +TIMER_DRIVER_DIR := arm + +TOP := ${SDDF}/examples/blk_benchmark +CONFIGS_INCLUDE := ${TOP} + +MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit + +BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) + +IMAGES := client.elf blk_virt.elf timer_driver.elf + +ifeq ($(strip $(MICROKIT_BOARD)), odroidc4) + IMAGES += sdmmc_driver.elf + BLK_DRIVER_DIR := sdmmc + BLK_DRIVER_MK := sdmmc_driver.mk + TIMER_DRIVER_DIR := meson + CPU := cortex-a53 +else ifeq ($(strip $(MICROKIT_BOARD)), qemu_virt_aarch64) + IMAGES += blk_driver.elf + BLK_DRIVER_DIR := virtio + BLK_DRIVER_MK := blk_driver.mk + CPU := cortex-a53 + QEMU := qemu-system-aarch64 +else + $(error Unsupported MICROKIT_BOARD given) +endif + +# CFLAGS := -mcpu=$(CPU) \ + -mstrict-align \ + -nostdlib \ + -ffreestanding \ + -g3 \ + -O3 \ + -Wall -Wno-unused-function -Werror -Wno-unused-command-line-argument \ + -I$(BOARD_DIR)/include \ + -I$(SDDF)/include \ + -I$(CONFIGS_INCLUDE) + +CFLAGS := -mcpu=$(CPU) \ + -mstrict-align \ + -nostdlib \ + -ffreestanding \ + -g3 \ + -O3 \ + -I$(BOARD_DIR)/include \ + -I$(SDDF)/include \ + -I$(CONFIGS_INCLUDE) + +LDFLAGS := -L$(BOARD_DIR)/lib +LIBS := --start-group -lmicrokit -Tmicrokit.ld libsddf_util_debug.a --end-group + +IMAGE_FILE := loader.img +REPORT_FILE := report.txt +SYSTEM_FILE := ${TOP}/board/$(MICROKIT_BOARD)/blk.system + +BLK_DRIVER := $(SDDF)/drivers/blk/${BLK_DRIVER_DIR} +TIMER_DRIVER := $(SDDF)/drivers/timer/${TIMER_DRIVER_DIR} + +BLK_COMPONENTS := $(SDDF)/blk/components + +all: $(IMAGE_FILE) + +include ${BLK_DRIVER}/$(BLK_DRIVER_MK) +include ${TIMER_DRIVER}/timer_driver.mk + +include ${SDDF}/util/util.mk +include ${BLK_COMPONENTS}/blk_components.mk + +${IMAGES}: libsddf_util_debug.a + +client.o: ${TOP}/client.c + $(CC) -c $(CFLAGS) -I. $< -o client.o +client.elf: client.o + $(LD) $(LDFLAGS) $< $(LIBS) -o $@ + +$(IMAGE_FILE) $(REPORT_FILE): $(IMAGES) $(SYSTEM_FILE) + $(MICROKIT_TOOL) $(SYSTEM_FILE) --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) + +qemu_disk: + ../mkvirtdisk mydisk 1 512 16777216 + +qemu: ${IMAGE_FILE} qemu_disk + $(QEMU) -machine virt,virtualization=on \ + -cpu cortex-a53 \ + -serial mon:stdio \ + -device loader,file=$(IMAGE_FILE),addr=0x70000000,cpu-num=0 \ + -m size=2G \ + -nographic \ + -global virtio-mmio.force-legacy=false \ + -d guest_errors \ + -drive file=mydisk,if=none,format=raw,id=hd \ + -device virtio-blk-device,drive=hd + +clean:: + rm -f client.o +clobber:: clean + rm -f client.elf ${IMAGE_FILE} ${REPORT_FILE} diff --git a/examples/blk_benchmark/blk_config.h b/examples/blk_benchmark/blk_config.h new file mode 100644 index 000000000..613c416ec --- /dev/null +++ b/examples/blk_benchmark/blk_config.h @@ -0,0 +1,97 @@ +/* + * Copyright 2024, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +#define BLK_NUM_CLIENTS 1 + +#define BLK_NAME_CLI0 "client" + +#define BLK_QUEUE_CAPACITY_CLI0 512 +#define BLK_QUEUE_CAPACITY_DRIV BLK_QUEUE_CAPACITY_CLI0 + +#define BLK_QUEUE_REGION_SIZE 0x200000 +#define BLK_DATA_REGION_SIZE_CLI0 BLK_QUEUE_REGION_SIZE +#define BLK_DATA_REGION_SIZE_DRIV BLK_QUEUE_REGION_SIZE + +#define BLK_QUEUE_REGION_SIZE_CLI0 BLK_QUEUE_REGION_SIZE +#define BLK_QUEUE_REGION_SIZE_DRIV BLK_QUEUE_REGION_SIZE + +/* Mapping from client index to disk partition that the client will have access to. */ +// Please ensure this point to a partition that you do not mind to courrupt as the example involves write data to that component +static const int blk_partition_mapping[BLK_NUM_CLIENTS] = { 2 }; + +static inline blk_storage_info_t *blk_virt_cli_storage_info(blk_storage_info_t *info, unsigned int id) +{ + switch (id) { + case 0: + return info; + default: + return NULL; + } +} + +static inline uintptr_t blk_virt_cli_data_region(uintptr_t data, unsigned int id) +{ + switch (id) { + case 0: + return data; + default: + return 0; + } +} + +static inline uint64_t blk_virt_cli_data_region_size(unsigned int id) +{ + switch (id) { + case 0: + return BLK_DATA_REGION_SIZE_CLI0; + default: + return 0; + } +} + +static inline blk_req_queue_t *blk_virt_cli_req_queue(blk_req_queue_t *req, unsigned int id) +{ + switch (id) { + case 0: + return req; + default: + return NULL; + } +} + +static inline blk_resp_queue_t *blk_virt_cli_resp_queue(blk_resp_queue_t *resp, unsigned int id) +{ + switch (id) { + case 0: + return resp; + default: + return NULL; + } +} + +static inline uint32_t blk_virt_cli_queue_capacity(unsigned int id) +{ + switch (id) { + case 0: + return BLK_QUEUE_CAPACITY_CLI0; + default: + return 0; + } +} + +static inline uint32_t blk_cli_queue_capacity(char *pd_name) +{ + if (!sddf_strcmp(pd_name, BLK_NAME_CLI0)) { + return BLK_QUEUE_CAPACITY_CLI0; + } else { + return 0; + } +} diff --git a/examples/blk_benchmark/board/odroidc4/blk.system b/examples/blk_benchmark/board/odroidc4/blk.system new file mode 100644 index 000000000..45284d4a4 --- /dev/null +++ b/examples/blk_benchmark/board/odroidc4/blk.system @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/blk_benchmark/client.c b/examples/blk_benchmark/client.c new file mode 100644 index 000000000..3f69f8043 --- /dev/null +++ b/examples/blk_benchmark/client.c @@ -0,0 +1,154 @@ +/* + * Copyright 2024, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_CLIENT(...) do{ sddf_dprintf("CLIENT|INFO: "); sddf_dprintf(__VA_ARGS__); }while(0) +#define LOG_CLIENT_ERR(...) do{ sddf_printf("CLIENT|ERROR: "); sddf_printf(__VA_ARGS__); }while(0) + +#define QUEUE_SIZE 512 +#define VIRT_CH 0 +#define TIMER 1 + +#define BENCHMARK_TOTAL_SIZE ONE_HUNDRED_MB +#define BLK_TRANSFER_SIZE 4096 +#define BENCHMARK_CHUNKSIZE ONE_HUNDRED_MB +#define ONE_HUNDRED_MB 0x6400000 +#define ONE_MB 0x100000 +#define BLOCK_READ_INTERVAL ONE_MB +#define BYTES_TO_MB 1048576.0 // 1 MB = 1048576 bytes + +static_assert(BENCHMARK_TOTAL_SIZE % BENCHMARK_CHUNKSIZE == 0, "The chunksize must be aligned to the total size"); +static_assert(BENCHMARK_CHUNKSIZE % BLK_TRANSFER_SIZE == 0, "The chunksize must be a multiple of the transfer size"); + +blk_storage_info_t *blk_storage_info; +uintptr_t blk_request; +uintptr_t blk_response; +uintptr_t blk_data; + +uint64_t blk_data_paddr; + +uint64_t request_id = 1; + +static blk_queue_handle_t blk_queue; + +enum benchmark_state { + START, + WRITE, + READ, + FINISH, +}; + +enum benchmark_state benchmark_state = START; + +uint64_t start_time = 0; +uint64_t end_time = 0; + +bool benchmark() +{ + switch (benchmark_state) { + case START: { + LOG_CLIENT("Benchmark: START state\n"); + + int err = blk_enqueue_req(&blk_queue, BLK_REQ_READ, blk_data_paddr, 0, 2, request_id); + + // assert(!err); + + request_id++; + benchmark_state = READ; + + break; + } + case READ: { + LOG_CLIENT("Benchmark: READ state\n"); + /* Check that our previous write was successful */ + blk_resp_status_t status = -1; + uint16_t count = -1; + uint32_t id = -1; + int err = blk_dequeue_resp(&blk_queue, &status, &count, &id); + // assert(!err); + // assert(status == BLK_RESP_OK); + // assert(count == 2); + // assert(id == 0); + + uint64_t read_start_sector = 1024; + uint64_t times = 0; + // A static check should be added here to check if the read/write requests may go out of disk capacity boundary + while (BENCHMARK_TOTAL_SIZE - times * BENCHMARK_CHUNKSIZE) { + err = blk_enqueue_req(&blk_queue, BLK_REQ_READ, blk_data_paddr + times * BENCHMARK_CHUNKSIZE, + read_start_sector + times * (BENCHMARK_CHUNKSIZE / BLK_TRANSFER_SIZE + BLOCK_READ_INTERVAL / BLK_TRANSFER_SIZE), + (BENCHMARK_CHUNKSIZE / BLK_TRANSFER_SIZE), request_id); + request_id++; + times++; + // assert(!err); + } + + benchmark_state = FINISH; + + start_time = sddf_timer_time_now(TIMER); + + break; + } + case FINISH: { + end_time = sddf_timer_time_now(TIMER); + + // assert(end_time > start_time); + + uint64_t time_passed = end_time - start_time; + + LOG_CLIENT("Benchmark: FINISH state\n"); + + double speed_bytes_per_sec = (double)BENCHMARK_TOTAL_SIZE * 1000000000.0 / time_passed; + + // Convert to MB/s + double speed_mb_per_sec = speed_bytes_per_sec / BYTES_TO_MB; + + sddf_dprintf("Benchmarking speed: %.5fByte/Second\n", speed_bytes_per_sec); + sddf_dprintf("Benchmark Speed: %.5f MB/s\n", speed_mb_per_sec); + sddf_dprintf("Time has passed: %ld nanosecond\n", time_passed); + + LOG_CLIENT("Benchmark: successfully finished!\n"); + + return true; + } + default: + LOG_CLIENT_ERR("internal error, invalid state\n"); + // assert(false); + } + + return false; +} + +void init(void) +{ + LOG_CLIENT("starting\n"); + + blk_queue_init(&blk_queue, (blk_req_queue_t *)blk_request, (blk_resp_queue_t *)blk_response, QUEUE_SIZE); + + /* Want to print out configuration information, so wait until the config is ready. */ + while (!blk_storage_is_ready(blk_storage_info)); + LOG_CLIENT("device config ready\n"); + + LOG_CLIENT("device size: 0x%lx bytes\n", blk_storage_info->capacity * BLK_TRANSFER_SIZE); + + benchmark(); + microkit_notify(VIRT_CH); +} + +void notified(microkit_channel ch) +{ + // assert(ch == VIRT_CH); + + if (!benchmark()) { + microkit_notify(VIRT_CH); + } +} From 9acaa030b420f0267f8d6562b3aa20964db9400b Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 2 Dec 2024 17:09:31 +1100 Subject: [PATCH 10/48] sdmmc: upload the files I forgot to upload Signed-off-by: Cheng --- drivers/blk/sdmmc/src/main.rs | 245 +++++++++++++++++--------- drivers/blk/sdmmc/src/sddf_blk/mod.rs | 4 +- 2 files changed, 159 insertions(+), 90 deletions(-) diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 3f2e5dd5f..fa6dc3c05 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -16,9 +16,12 @@ use sddf_blk::{ blk_dequeue_req_helper, blk_enqueue_resp_helper, blk_queue_empty_req_helper, blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkRequest, BlkStatus, }; -use sdmmc_hal::meson_gx_mmc::MesonSdmmcRegisters; +use sdmmc_hal::meson_gx_mmc::SdmmcMesonHardware; -use sdmmc_protocol::sdmmc::{InterruptType, SdmmcHalError, SdmmcHardware, SdmmcProtocol}; +use sdmmc_protocol::sdmmc::{ + sdmmc_capability::{MMC_INTERRUPT_END_OF_CHAIN, MMC_INTERRUPT_ERROR}, + SdmmcHalError, SdmmcHardware, SdmmcProtocol, +}; use sel4_microkit::{debug_print, debug_println, protection_domain, Channel, Handler, Infallible}; const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(0); @@ -33,10 +36,10 @@ const RETRY_CHANCE: u16 = 5; // Debug function for printing out content in one block #[allow(dead_code)] -fn print_one_block(ptr: *const u8) { +unsafe fn print_one_block(ptr: *const u8, num: usize) { unsafe { - // Iterate over the 512 bytes and print each one in hexadecimal format - for i in 0..512 { + // Iterate over the number of bytes and print each one in hexadecimal format + for i in 0..num { let byte = *ptr.add(i); if i % 16 == 0 { debug_print!("\n{:04x}: ", i); @@ -53,6 +56,10 @@ unsafe fn noop_clone(_data: *const ()) -> RawWaker { RawWaker::new(_data, &VTABLE) } +/// Since in .system file, the page we are providing to tune_performance function is uncached +/// we do not need to provide a real cache invalidate function +fn dummy_cache_invalidate_function() {} + // A VTable that points to the no-op functions static VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); @@ -63,43 +70,88 @@ fn create_dummy_waker() -> Waker { } #[protection_domain(heap_size = 0x10000)] -fn init() -> HandlerImpl<'static, MesonSdmmcRegisters> { - // debug_println!("Driver init!"); +fn init() -> HandlerImpl { + debug_println!("Driver init!"); unsafe { blk_queue_init_helper(); } - let meson_hal: &mut MesonSdmmcRegisters = MesonSdmmcRegisters::new(); - let mut protocol: SdmmcProtocol<'static, MesonSdmmcRegisters> = SdmmcProtocol::new(meson_hal); - let mut irq_to_enable = InterruptType::Success as u32 | InterruptType::Error as u32; + let meson_hal: SdmmcMesonHardware = unsafe { SdmmcMesonHardware::new() }; + + let unsafe_stolen_memory: &mut [u8; 64]; + + // This line of code actually is very unsafe! + // Considering the memory is stolen from the memory that has sdcard registers mapped in + unsafe { + let stolen_memory_addr = 0xf5500000 as *mut [u8; 64]; + assert!(stolen_memory_addr as usize % 8 == 0); + unsafe_stolen_memory = &mut (*stolen_memory_addr); + } + + // Handling result in two different ways, by matching and unwrap_or_else + let res = SdmmcProtocol::new(meson_hal); + let mut sdmmc_host = match res { + Ok(host) => host, + Err(err) => panic!("SDMMC: Error at init {:?}", err), + }; + + sdmmc_host + .setup_card() + .unwrap_or_else(|error| panic!("SDMMC: Error at setup {:?}", error)); + + let mut test: u32 = 0; + let _ = sdmmc_host.enable_interrupt(&mut test); + + /* + unsafe { + unsafe_stolen_memory[0] = 1; + unsafe_stolen_memory[10] = 64; + unsafe_stolen_memory[53] = 98; + + debug_println!("printing out memory have value written in it"); + + print_one_block(unsafe_stolen_memory.as_ptr(), 512); + + assert!(unsafe_stolen_memory[0] == 1); + assert!(unsafe_stolen_memory[10] == 64); + assert!(unsafe_stolen_memory[53] == 98); + } + */ + + // TODO: Should tuning be possible to fail? + sdmmc_host + .tune_performance(Some(( + unsafe_stolen_memory, + dummy_cache_invalidate_function, + ))) + .unwrap_or_else(|error| panic!("SDMMC: Error at tuning performance {:?}", error)); + + unsafe { + print_one_block(unsafe_stolen_memory.as_ptr(), 64); + } + + let mut irq_to_enable = MMC_INTERRUPT_ERROR | MMC_INTERRUPT_END_OF_CHAIN; + // Should always succeed, at least for odroid C4 - let _ = protocol.enable_interrupt(&mut irq_to_enable); + let _ = sdmmc_host.enable_interrupt(&mut irq_to_enable); HandlerImpl { future: None, - sdmmc: Some(protocol), + sdmmc: Some(sdmmc_host), request: None, retry: RETRY_CHANCE, } } -struct HandlerImpl<'a, T: SdmmcHardware> { - future: Option< - Pin< - Box< - dyn Future, Option>)> + 'a, - >, - >, - >, - sdmmc: Option>, +struct HandlerImpl { + future: Option, SdmmcProtocol)>>>>, + sdmmc: Option>, request: Option, retry: u16, } -impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { +impl Handler for HandlerImpl { type Error = Infallible; fn notified(&mut self, channel: Channel) -> Result<(), Self::Error> { - // debug_println!("SDMMC_DRIVER: MESSAGE FROM CHANNEL: {}", channel.index()); - if channel.index() != INTERRUPT.index() && channel.index() != BLK_VIRTUALIZER.index() { debug_println!( "SDMMC_DRIVER: Unknown channel sent me message: {}", @@ -108,64 +160,62 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { return Ok(()); } - if channel.index() == INTERRUPT.index() { - let err = channel.irq_ack(); - if err.is_err() { - panic!("SDMMC: Cannot acknowledge interrupt for CPU!") - } - } - let mut notify_virt: bool = false; - loop { - // Polling if receive any notification, it is to poll even the notification is not from the interrupt as polling is cheap - if let Some(request) = &mut self.request { - if let Some(future) = &mut self.future { - let waker = create_dummy_waker(); - let mut cx = Context::from_waker(&waker); - match future.as_mut().poll(&mut cx) { - Poll::Ready((result, sdmmc)) => { - // debug_println!("SDMMC_DRIVER: Future completed with result"); - self.future = None; // Reset the future once done - self.sdmmc = sdmmc; - if result.is_err() { - debug_println!( - "SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possibly retry!" - ); - self.retry -= 1; - } else { - // Deduct finished count from count - request.success_count += request.count_to_do; - request.count -= request.count_to_do as u16; - } - if request.count == 0 { - let resp_status = BlkStatus::BlkRespOk; - notify_virt = true; - unsafe { - blk_enqueue_resp_helper( - resp_status, - request.success_count / SDDF_TO_REAL_SECTOR, - request.id, + + 'process_notification: { + // Polling if receive interrupt notification + if channel.index() == INTERRUPT.index() { + if let Some(request) = &mut self.request { + if let Some(future) = &mut self.future { + let waker = create_dummy_waker(); + let mut cx = Context::from_waker(&waker); + match future.as_mut().poll(&mut cx) { + Poll::Ready((result, sdmmc)) => { + // debug_println!("SDMMC_DRIVER: Future completed with result"); + self.future = None; // Reset the future once done + self.sdmmc = Some(sdmmc); + if result.is_err() { + debug_println!( + "SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possibly retry!" ); + self.retry -= 1; + } else { + // Deduct finished count from count + request.success_count += request.count_to_do; + request.count -= request.count_to_do; } - self.request = None; - } else if self.retry == 0 { - let resp_status = BlkStatus::BlkRespSeekError; - notify_virt = true; - unsafe { - blk_enqueue_resp_helper( - resp_status, - request.success_count / SDDF_TO_REAL_SECTOR, - request.id, - ); + if request.count == 0 { + let resp_status = BlkStatus::BlkRespOk; + notify_virt = true; + unsafe { + blk_enqueue_resp_helper( + resp_status, + request.success_count / SDDF_TO_REAL_SECTOR, + request.id, + ); + } + self.request = None; + } else if self.retry == 0 { + let resp_status = BlkStatus::BlkRespSeekError; + notify_virt = true; + unsafe { + blk_enqueue_resp_helper( + resp_status, + request.success_count / SDDF_TO_REAL_SECTOR, + request.id, + ); + } + self.request = None; } - self.request = None; + } + Poll::Pending => { + // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); + // Since the future is not ready, no other request can be dequeued, exit the big loop + break 'process_notification; } } - Poll::Pending => { - // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); - // Since the future is not ready, no other request can be dequeued, exit the big loop - break; - } + } else { + panic!("SDMMC: Receive a hardware interrupt despite not having a future!"); } } } @@ -187,19 +237,19 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { &mut request.request_code as *mut BlkOp, &mut request.io_or_offset as *mut u64, &mut request.block_number as *mut u32, - &mut request.count as *mut u16, + &mut request.count as *mut u32, &mut request.id as *mut u32, ); } request.block_number = request.block_number * SDDF_TO_REAL_SECTOR; - request.count = request.count * SDDF_TO_REAL_SECTOR as u16; + request.count = request.count * SDDF_TO_REAL_SECTOR; // Print the retrieved values - /* - debug_println!("io_or_offset: 0x{:x}", io_or_offset);// Simple u64 - debug_println!("block_number: {}", block_number); // Simple u32 - debug_println!("count: {}", count); // Simple u16 - debug_println!("id: {}", id); // Simple u32 - */ + + // debug_println!("io_or_offset: 0x{:x}", request.io_or_offset);// Simple u64 + // debug_println!("block_number: {}", request.block_number); // Simple u32 + // debug_println!("count: {}", request.count); // Simple u16 + // debug_println!("id: {}", request.id); // Simple u32 + match request.request_code { BlkOp::BlkReqRead => { // Reset retry chance here @@ -222,6 +272,7 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { } } } + // If future is empty if let Some(request) = &mut self.request { if let None = self.future { @@ -240,7 +291,7 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64, ))); } else { - panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") + panic!("SDMMC_DRIVER: The sdmmc should be here since the future should be empty!!!") } } BlkOp::BlkReqWrite => { @@ -264,16 +315,34 @@ impl<'a, T: SdmmcHardware> Handler for HandlerImpl<'a, T> { panic!("SDMMC_DRIVER: You should not reach here!") } } + let waker = create_dummy_waker(); + let mut cx = Context::from_waker(&waker); + // Poll the future once to make it start working! + if let Some(ref mut future) = self.future { + match future.as_mut().poll(&mut cx) { + Poll::Ready(_) => { + panic!("SDMMC: The newly created future returned immediately! + Most likely the future contain an invalid request! + Double check request sanitize process!") + } + Poll::Pending => break 'process_notification, + } + } } - } else { - // If Request is empty, means there are no future available, so we do not need to poll again - break; } } + if notify_virt == true { // debug_println!("SDMMC_DRIVER: Notify the BLK_VIRTUALIZER!"); BLK_VIRTUALIZER.notify(); } + // Ack irq + if channel.index() == INTERRUPT.index() { + let err = channel.irq_ack(); + if err.is_err() { + panic!("SDMMC: Cannot acknowledge interrupt for CPU!") + } + } Ok(()) } } diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs index e58937601..6c4e537a3 100644 --- a/drivers/blk/sdmmc/src/sddf_blk/mod.rs +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -8,7 +8,7 @@ extern "C" { code: *mut BlkOp, io_or_offset: *mut u64, block_number: *mut u32, - count: *mut u16, + count: *mut u32, id: *mut u32, ); } @@ -31,7 +31,7 @@ pub struct BlkRequest { pub request_code: BlkOp, pub io_or_offset: u64, pub block_number: u32, - pub count: u16, + pub count: u32, // I suggest use u32 here and change the count to use u32 in sddf_blk pub success_count: u32, pub count_to_do: u32, From 9d6f9a7dabf7858030b8cc8a1af7a2b63ad3d73a Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 2 Dec 2024 20:50:49 +1100 Subject: [PATCH 11/48] Update rev Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 34294306b..d107c1798 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -8,8 +8,8 @@ sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d3790bfd # Change it to based on # sdmmc_hal = { path = "sdmmc_hal/meson", features = ["sel4-microkit"] } # sdmmc_protocol = { path = "sdmmc_protocol" } -sdmmc_hal = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "8f6a7b95c757eed67745772274144b734c5f384d" } -sdmmc_protocol = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "8f6a7b95c757eed67745772274144b734c5f384d" } +sdmmc_hal = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "1e5e95ac27862d8e4d2c386e5392d3217b7dbb7b" } +sdmmc_protocol = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "1e5e95ac27862d8e4d2c386e5392d3217b7dbb7b" } [build] build = "build.rs" \ No newline at end of file From 0cfb398a4aad0ec067546a63ad205a51a98250f1 Mon Sep 17 00:00:00 2001 From: Cheng Date: Tue, 3 Dec 2024 12:01:44 +1100 Subject: [PATCH 12/48] sdmmc: update rev Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index d107c1798..835d73a06 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -4,12 +4,12 @@ version = "0.1.0" edition = "2021" [dependencies] -sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d3790bfd15512e18659f9491c319867fabf9552d" } +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5b9ebfd0a3a9805f28cc9222cd558e8d56a3919d" } # Change it to based on # sdmmc_hal = { path = "sdmmc_hal/meson", features = ["sel4-microkit"] } # sdmmc_protocol = { path = "sdmmc_protocol" } -sdmmc_hal = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "1e5e95ac27862d8e4d2c386e5392d3217b7dbb7b" } -sdmmc_protocol = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "1e5e95ac27862d8e4d2c386e5392d3217b7dbb7b" } +sdmmc_hal = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "c763d9b891ece68994ea59ae209987b0f26ab7db" } +sdmmc_protocol = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "c763d9b891ece68994ea59ae209987b0f26ab7db" } [build] build = "build.rs" \ No newline at end of file From 406c0f117bc1a85eebf355ea8a142a5acf51e83b Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 9 Dec 2024 15:15:13 +1100 Subject: [PATCH 13/48] sdmmc: change the delay to spinning Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 835d73a06..4b2fc57fc 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -8,8 +8,8 @@ sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5b9ebfd0 # Change it to based on # sdmmc_hal = { path = "sdmmc_hal/meson", features = ["sel4-microkit"] } # sdmmc_protocol = { path = "sdmmc_protocol" } -sdmmc_hal = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "c763d9b891ece68994ea59ae209987b0f26ab7db" } -sdmmc_protocol = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "c763d9b891ece68994ea59ae209987b0f26ab7db" } +sdmmc_hal = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "db3b37da9fe2a17ce8b4f65853637f94a6556201" } +sdmmc_protocol = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "db3b37da9fe2a17ce8b4f65853637f94a6556201" } [build] build = "build.rs" \ No newline at end of file From a98086c174e2ec891ba8a1855dc8c181b59283f5 Mon Sep 17 00:00:00 2001 From: Cheng Date: Fri, 28 Feb 2025 14:25:31 +1100 Subject: [PATCH 14/48] sdmmc: upstream the most recent changes Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 9 +- drivers/blk/sdmmc/README.md | 93 ++ drivers/blk/sdmmc/build.rs | 5 +- .../sel4-microkit-support/Cargo.toml | 15 + .../sel4-microkit-support/lib.rs | 40 + drivers/blk/sdmmc/rust-toolchain.toml | 2 +- drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml | 18 +- drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs | 4 +- .../blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 809 ++++++++--- drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml | 13 +- drivers/blk/sdmmc/sdmmc_protocol/lib.rs | 6 +- drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 1275 +++++++++++++++-- .../sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs | 86 ++ .../blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs | 281 ++++ .../sdmmc_protocol/sdmmc/sdmmc_capability.rs | 114 ++ .../sdmmc_protocol/sdmmc/sdmmc_constant.rs | 46 +- drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs | 21 + .../blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs | 144 ++ drivers/blk/sdmmc/src/main.rs | 51 +- drivers/blk/sdmmc/src/sddf_blk/mod.rs | 12 +- .../aarch64-sel4-microkit-minimal.json | 8 +- 21 files changed, 2701 insertions(+), 351 deletions(-) create mode 100644 drivers/blk/sdmmc/README.md create mode 100644 drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/Cargo.toml create mode 100644 drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/lib.rs create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 4b2fc57fc..0e7b2e88f 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -4,12 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5b9ebfd0a3a9805f28cc9222cd558e8d56a3919d" } -# Change it to based on -# sdmmc_hal = { path = "sdmmc_hal/meson", features = ["sel4-microkit"] } -# sdmmc_protocol = { path = "sdmmc_protocol" } -sdmmc_hal = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "db3b37da9fe2a17ce8b4f65853637f94a6556201" } -sdmmc_protocol = { features = ["sel4-microkit"], git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "db3b37da9fe2a17ce8b4f65853637f94a6556201" } +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5826be1a6c83803faeaa79cd9f164c26a5a32e7c" } +sdmmc_hal = { path = "sdmmc_hal/meson" } +sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } [build] build = "build.rs" \ No newline at end of file diff --git a/drivers/blk/sdmmc/README.md b/drivers/blk/sdmmc/README.md new file mode 100644 index 000000000..e51234ea2 --- /dev/null +++ b/drivers/blk/sdmmc/README.md @@ -0,0 +1,93 @@ +# Generic Rust-Based SDMMC Driver + +This repository contains a modular SDMMC driver written entirely in Rust, focusing on **async support**, **memory safety**, and **functional correctness**. While developed for LionsOS, the driver is designed to be **OS-agnostic** and extensible for other operating systems. + +I’m excited to share this driver as a unique contribution to the open-source community. It combines wide compatibility, high performance, and Rust’s safety benefits, making it a standout in its category. + +--- + +## Features + +- **Modular Design**: Inspired by U-Boot and Linux, separates device-specific logic from protocol implementation, ensuring flexibility and extensibility. +- **Wide Compatibility and High Performance**: + - Supports a range of SD cards, including **SDHC** and **SDXC**, with speed classes up to **UHS-I**. + - While Linux only added support for UHS-II in October 2024, this driver provides robust UHS-I support, ideal for embedded systems and platforms where UHS-II adoption remains limited. +- **Memory Safety**: Utilizes Rust's strict compile-time checks and ownership model, ensuring better safety and readability compared to traditional C-based drivers. +- **OS-Agnostic**: Designed to integrate seamlessly with LionsOS but easily portable to other operating systems. + +--- + +## Supported Hardware Platforms + +- **Odroid C4**: Currently, the driver supports and has been tested on the Odroid C4 platform. +- **Adding Support**: Thanks to the modular design, adding support for additional platforms is straightforward by implementing the hardware abstraction layer for the new platform. + +--- + +## Current Status + +The driver is currently in the **final stages of development for its first iteration**: +- Core functionality has been fully implemented. +- Performance is comparable to Linux's SDMMC subsystem, with support for speed classes up to **UHS-I**. +- Code refinements and documentation updates are ongoing. +- **Expected Milestone**: Public release and merging into LionsOS main branch by February 2025. + +--- + +## Driver Structure + +The driver is organized into the following components: + +1. **`sdmmc_hal` Folder**: + - Contains the hardware abstraction layer for different platforms. + - To add support for a new platform, implement the necessary hardware-specific interfaces here. + +2. **`sdmmc_protocol` Folder**: + - Implements the SD card protocol using the hardware abstraction layer. + - Modify this layer to add new protocol features (e.g., hotplugging or eMMC support). + +3. **`optional_os_support` Folder**: + - Provides OS-specific utilities such as optimized wait/sleep operations(instead of spinning waiting) or printing debug messages to the terminal. + - Modify this layer to provide support for a new OS + +--- + +## Usage and Examples + +Usage instructions and examples will be added soon. Stay tuned! + +--- + +## Why Rust? + +Rust provides unmatched safety and performance benefits for driver development: +- **Memory Safety**: Prevents common bugs like null pointer dereferences and buffer overflows. +- **Async Support**: Enables efficient, non-blocking I/O operations. +- **Readable and Extensible**: Rust’s modern syntax and tooling make the driver easier to understand and maintain compared to traditional C-based implementations. + +--- + +## Future Plans + +- Expand hardware support to additional platforms. +- Add support for legacy SDSC cards and the latest SDUC cards (though they may have limited use cases). +- Add hotplugging and eMMC support. +- Publish detailed usage examples and benchmarks. + +--- + +## Contributing + +Contributions are welcome! Feel free to open issues or submit pull requests to improve functionality, expand platform support, or enhance documentation. + +--- + +## License + +The license for this work will be added upon the first public release. The project is developed as part of my work at Trustworthy Systems, UNSW, so the copyright may reside with the university. Clarifications about licensing are ongoing. + +--- + +## Related Links + +- [LionsOS GitHub Repository](https://github.com/au-ts/lionsos) diff --git a/drivers/blk/sdmmc/build.rs b/drivers/blk/sdmmc/build.rs index 66c23f2cb..6dba70af3 100644 --- a/drivers/blk/sdmmc/build.rs +++ b/drivers/blk/sdmmc/build.rs @@ -4,7 +4,4 @@ fn main() { // Link the C library (static or dynamic). Adjust "static" or "dylib" as needed. println!("cargo:rustc-link-lib=static=sddfblk"); - - // If you need to specify the include directory for C headers: - // println!("cargo:include=path/to/your/c/include"); -} \ No newline at end of file +} diff --git a/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/Cargo.toml b/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/Cargo.toml new file mode 100644 index 000000000..e91f47ed9 --- /dev/null +++ b/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "sel4-microkit-support" +version = "0.1.0" +edition = "2021" +authors = ["Cheng Li 李澄 "] + +[lib] +name = "sel4_microkit_support" +path = "lib.rs" + +# Optional OS-specific crates can be included to enhance functionality, +# such as providing improved debugging output and more efficient sleep functions +# (as opposed to simple spin-wait). +[dependencies] +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5826be1a6c83803faeaa79cd9f164c26a5a32e7c" } \ No newline at end of file diff --git a/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/lib.rs b/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/lib.rs new file mode 100644 index 000000000..acdc4dcad --- /dev/null +++ b/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/lib.rs @@ -0,0 +1,40 @@ +#![no_std] // Don't link the standard library + +use core::hint; + +/// Api should only accessible in this crate +#[doc(hidden)] +pub use sel4_microkit::debug_print; + +/// Spins for an approximate number of nanoseconds. +/// +/// This function is unreliable because it does not account for CPU frequency, +/// power-saving modes, or other hardware variations. It should be used only +/// in situations where approximate delays are sufficient. +/// +/// # Arguments +/// +/// * `time_ns` - The approximate number of nanoseconds to spin-wait. +/// +/// # Notes +/// +/// This function uses a simple busy-wait loop combined with `hint::spin_loop` +/// to reduce contention during the wait. The actual delay may vary significantly +/// depending on the hardware and system load. +/// +/// For accurate delays, use a hardware timer or platform-specific APIs. +/// TODO: Change this to using a timer to stop later +#[inline] +pub fn process_wait_unreliable(time_ns: u64) { + for _ in 0..time_ns { + hint::spin_loop(); // Use spin loop hint to reduce contention during the wait + } +} + +/// `sel4-microkit` specific serial implementation +#[macro_export] +macro_rules! debug_log { + ($($arg:tt)*) => { + $crate::debug_print!($($arg)*); + } +} diff --git a/drivers/blk/sdmmc/rust-toolchain.toml b/drivers/blk/sdmmc/rust-toolchain.toml index 84a9735bf..40ab25b58 100644 --- a/drivers/blk/sdmmc/rust-toolchain.toml +++ b/drivers/blk/sdmmc/rust-toolchain.toml @@ -5,5 +5,5 @@ # [toolchain] -channel = "nightly-2024-05-01" +channel = "nightly-2024-10-26" components = [ "rustfmt", "rust-src", "rustc-dev", "llvm-tools-preview" ] diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml b/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml index 6bbae1511..eb97b1ac2 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml @@ -2,11 +2,27 @@ name = "sdmmc_hal" version = "0.1.0" edition = "2021" +authors = ["Cheng Li 李澄 "] [lib] name = "sdmmc_hal" path = "lib.rs" +# The Hardware Abstraction Layer (HAL) is designed to be: +# - Bare-metal compatible +# - Operating system (OS) agnostic +# - Minimally dependent to ensure cross-platform flexibility +# +# Optional OS-specific crates can be included to enhance functionality, +# such as providing improved debugging output and more efficient sleep functions +# (as opposed to simple spin-wait). +# +# Required Dependencies: +# - sdmmc_protocol: Essential for core functionality. +# +# Optional Dependencies: +# - sel4-microkit: Provides additional support for debugging and optimized sleep functions +# specifically on the seL4 microkernel platform. + [dependencies] sdmmc_protocol = { path = "../../sdmmc_protocol" } -sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d3790bfd15512e18659f9491c319867fabf9552d", package = "sel4-microkit" } \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs index 9ed5d73d3..936dc4771 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs @@ -1,3 +1,3 @@ -#![no_std] // Don't link the standard library +#![no_std] // Don't link the standard library -pub mod meson_gx_mmc; \ No newline at end of file +pub mod meson_gx_mmc; diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs index 5828ff69c..29cc3f4a0 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs @@ -1,21 +1,43 @@ use core::ptr; -use sdmmc_protocol::sdmmc::{InterruptType, MmcData, MmcDataFlag, SdmmcCmd, SdmmcHalError, SdmmcHardware}; -use sel4_microkit::debug_println; +use sdmmc_protocol::{sdmmc::{ + mmc_struct::{MmcBusWidth, MmcTiming, TuningState}, + sdmmc_capability::{ + MMC_CAP_4_BIT_DATA, MMC_CAP_CMD23, MMC_CAP_VOLTAGE_TUNE, MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS, MMC_VDD_31_32, MMC_VDD_32_33, MMC_VDD_33_34 + }, + HostInfo, MmcData, MmcDataFlag, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, SdmmcError + }, sdmmc_os::{debug_log, process_wait_unreliable}, sdmmc_traits::SdmmcHardware +}; const SDIO_BASE: u64 = 0xffe05000; // Base address from DTS +// The always on gpio pin pull +const AO_RTI_PIN_REGION_START: u64 = 0xff800014; +const AO_RTI_PIN_REGION_END: u64 = 0xff800038; +const AO_RTI_PULL_UP_REG: u64 = 0xff80002c; +const AO_RTI_OUTPUT_ENABLE_REG: u64 = 0xff800024; +const AO_RTI_OUTPUT_LEVEL_REG: u64 = 0xff800034; +const AO_RTI_PULL_UP_EN_REG: u64 = 0xff800030; +const GPIO_AO_3: u32 = 1 << 3; + macro_rules! div_round_up { ($n:expr, $d:expr) => { (($n + $d - 1) / $d) }; } +const SD_EMMC_ADJ: u32 = 16; +const SD_EMMC_ADJUST_ADJ_DELAY_MASK: u32 = 0x3F << SD_EMMC_ADJ; +const SD_EMMC_ADJ_ENABLE: u32 = 0x2000; + // Constants translated from the C version // Clock related constant -const SD_EMMC_CLKSRC_24M: u32 = 24000000; // 24 MHz -const SD_EMMC_CLKSRC_DIV2: u32 = 1000000000; // 1 GHz +const SD_EMMC_CLKSRC_24M: u32 = 24000000; // 24 MHz +const SD_EMMC_CLKSRC_DIV2: u32 = 1000000000; // 1 GHz +const MESON_MIN_FREQUENCY: u32 = div_round_up!(SD_EMMC_CLKSRC_24M, CLK_MAX_DIV); +const MESON_MAX_FREQUENCY: u32 = 200000000; // 200 Mhz const CLK_MAX_DIV: u32 = 63; +const CLK_SRC_MASK: u32 = 0b11000000; const CLK_SRC_24M: u32 = 0 << 6; const CLK_SRC_DIV2: u32 = 1 << 6; const CLK_CO_PHASE_000: u32 = 0 << 8; @@ -26,6 +48,24 @@ const CLK_TX_PHASE_000: u32 = 0 << 10; const CLK_TX_PHASE_180: u32 = 2 << 10; const CLK_ALWAYS_ON: u32 = 1 << 24; +pub const CFG_BUS_WIDTH_MASK: u32 = 0b11; +pub const CFG_BUS_WIDTH_1: u32 = 0; +pub const CFG_BUS_WIDTH_4: u32 = 1; +pub const CFG_BUS_WIDTH_8: u32 = 2; +pub const CFG_DDR_MODE: u32 = 1 << 2; +pub const CFG_BL_LEN_MASK: u32 = 0b1111 << 4; +pub const CFG_BL_LEN_SHIFT: u32 = 4; +pub const CFG_BL_LEN_512: u32 = 0b1001 << 4; +pub const CFG_RESP_TIMEOUT_MASK: u32 = 0b1111 << 8; +pub const CFG_RESP_TIMEOUT_256: u32 = 0b1000 << 8; +pub const CFG_RC_CC_MASK: u32 = 0b1111 << 12; +pub const CFG_RC_CC_16: u32 = 0b0100 << 12; +pub const CFG_RC_CC_256: u32 = 0b1000 << 12; +pub const CFG_SDCLK_ALWAYS_ON: u32 = 1 << 18; +pub const CFG_STOP_CLOCK: u32 = 1 << 22; +pub const CFG_AUTO_CLK: u32 = 1 << 23; +pub const CFG_ERR_ABORT: u32 = 1 << 27; + // CMD_CFG constants const CMD_CFG_CMD_INDEX_SHIFT: u32 = 24; const CMD_CFG_RESP_128: u32 = 1 << 21; @@ -46,18 +86,19 @@ const MMC_RSP_CRC: u32 = 1 << 2; const MMC_RSP_BUSY: u32 = 1 << 3; // STATUS register masks and flags -const STATUS_MASK: u32 = 0xFFFF; // GENMASK(15, 0) -const STATUS_ERR_MASK: u32 = 0x1FFF; // GENMASK(12, 0) -const STATUS_RXD_ERR_MASK: u32 = 0xFF; // GENMASK(7, 0) -const STATUS_TXD_ERR: u32 = 1 << 8; // BIT(8) -const STATUS_DESC_ERR: u32 = 1 << 9; // BIT(9) -const STATUS_RESP_ERR: u32 = 1 << 10; // BIT(10) -const STATUS_RESP_TIMEOUT: u32 = 1 << 11; // BIT(11) -const STATUS_DESC_TIMEOUT: u32 = 1 << 12; // BIT(12) -const STATUS_END_OF_CHAIN: u32 = 1 << 13; // BIT(13) +const STATUS_MASK: u32 = 0xFFFF; // GENMASK(15, 0) +const STATUS_ERR_MASK: u32 = 0x1FFF; // GENMASK(12, 0) +const STATUS_RXD_ERR_MASK: u32 = 0xFF; // GENMASK(7, 0) +const STATUS_TXD_ERR: u32 = 1 << 8; // BIT(8) +const STATUS_DESC_ERR: u32 = 1 << 9; // BIT(9) +const STATUS_RESP_ERR: u32 = 1 << 10; // BIT(10) +const STATUS_RESP_TIMEOUT: u32 = 1 << 11; // BIT(11) +const STATUS_DESC_TIMEOUT: u32 = 1 << 12; // BIT(12) +const STATUS_END_OF_CHAIN: u32 = 1 << 13; // BIT(13) const STATUS_BUSY: u32 = 1 << 31; const STATUS_DESC_BUSY: u32 = 1 << 30; -const STATUS_DATI: u32 = 0xFF << 16; // Equivalent to GENMASK(23, 16) +const STATUS_DAT_MASK: u32 = 0xFF << 16; // Equivalent to GENMASK(23, 16) +const STATUS_DAT_SHIFT: u32 = 16; // IRQ enable register masks and flags const IRQ_RXD_ERR_MASK: u32 = 0xFF; // Equivalent to GENMASK(7, 0) @@ -74,16 +115,12 @@ const IRQ_END_OF_CHAIN: u32 = 1 << 13; const IRQ_RESP_STATUS: u32 = 1 << 14; const IRQ_SDIO: u32 = 1 << 15; const IRQ_ERR_MASK: u32 = IRQ_CRC_ERR | IRQ_TIMEOUTS; -// Equivalent to (IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN) -const IRQ_EN_MASK: u32 = IRQ_CRC_ERR | IRQ_TIMEOUTS | IRQ_END_OF_CHAIN; -// Configuration constants (assuming based on context) -const CFG_BL_LEN_MASK: u32 = 0xF << 4; // Bits 4-7 -const CFG_BL_LEN_SHIFT: u32 = 4; +const IRQ_EN_MASK: u32 = IRQ_ERR_MASK | IRQ_END_OF_CHAIN; const MESON_SDCARD_SECTOR_SIZE: u32 = 512; -pub const MAX_BLOCK_PER_TRANSFER:u32 = 0xFF; +pub const MAX_BLOCK_PER_TRANSFER: u32 = 0x1FF; const WRITE_ADDR_UPPER: u32 = 0xFFFE0000; @@ -97,13 +134,13 @@ fn ilog2(x: u32) -> u32 { } // Structure representing the SDIO controller's registers -/* - * Those register mapping are taken from meson-gx-mmc.c in Linux source code, +/* + * Those register mapping are taken from meson-gx-mmc.c in Linux source code, * meson_gx_mmc.h in uboot source code and S905X3 datasheet. * Despite Odroid C4 belong to Meson GX Family, the sdmmc register mapping * seems to be the same with the register mapping for meson_axg according to documentation * and the register mapping defined in Linux kernel. - * + * * #define MESON_SD_EMMC_CLOCK 0x00 * #define SD_EMMC_START 0x40 * #define MESON_SD_EMMC_CFG 0x44 @@ -120,96 +157,122 @@ fn ilog2(x: u32) -> u32 { * #define SD_EMMC_TXD 0x94 * #define SD_EMMC_LAST_REG SD_EMMC_TXD */ -// I think I find a bug in Linux, the odroid C4 delay register mapping are the same with meson-axg but it belongs to meson-gx -/* - * There are some assumptions that I have made for this driver: - * 1. The card is already powered on by U-Boot, so I do not need to manually manipulate - * gpio pins or regulator to turn it on or off. - * 2. The clocks are already enabled by U-Boot and there is no implicit clock shutdown when the uboot start to run - * my image, so I do not need to turn on the clocks that the sd card needs by myself. - * - */ #[repr(C)] -pub struct MesonSdmmcRegisters { - pub clock: u32, // 0x00: Clock control register - _reserved0: [u32; 15], // Padding for other unused registers (0x04 - 0x3C) - pub start: u32, // 0x40: Start register - pub cfg: u32, // 0x44: Configuration register - pub status: u32, // 0x48: Status register - pub irq_en: u32, // 0x4C: Interrupt enable register - pub cmd_cfg: u32, // 0x50: Command configuration register - pub cmd_arg: u32, // 0x54: Command argument register - pub cmd_dat: u32, // 0x58: Command data register (for DMA address) - pub cmd_rsp: u32, // 0x5C: Command response register - pub cmd_rsp1: u32, // 0x60: Command response register 1 - pub cmd_rsp2: u32, // 0x64: Command response register 2 - pub cmd_rsp3: u32, // 0x68: Command response register 3 - _reserved1: [u32; 9], // Padding for other unused registers (0x6C - 0x90) - pub rxd: u32, // 0x94: Receive data register (not used) - pub txd: u32, // 0x94: Transmit data register (not used, same as RXD) - // Add other registers as needed +struct MesonSdmmcRegisters { + clock: u32, // 0x00: Clock control register + delay1: u32, // 0x04: Delay register one + delay2: u32, // 0x08: Delay register two + adjust: u32, // 0x0C: Adjust register + _reserved0: [u32; 12], // Padding for other unused registers (0x04 - 0x3C) + start: u32, // 0x40: Start register + cfg: u32, // 0x44: Configuration register + status: u32, // 0x48: Status register + irq_en: u32, // 0x4C: Interrupt enable register + cmd_cfg: u32, // 0x50: Command configuration register + cmd_arg: u32, // 0x54: Command argument register + cmd_dat: u32, // 0x58: Command data register (for DMA address) + cmd_rsp: u32, // 0x5C: Command response register + cmd_rsp1: u32, // 0x60: Command response register 1 + cmd_rsp2: u32, // 0x64: Command response register 2 + cmd_rsp3: u32, // 0x68: Command response register 3 + _reserved1: [u32; 9], // Padding for other unused registers (0x6C - 0x90) + rxd: u32, // 0x94: Receive data register (not used) + txd: u32, // 0x94: Transmit data register (not used, same as RXD) + // Add other registers as needed } -impl Unpin for MesonSdmmcRegisters {} - impl MesonSdmmcRegisters { - /// This new use unsafe under the hood, ensure correct memory page is mapped into - /// the respective virtual memory address and do not do things stupid - pub fn new() -> &'static mut MesonSdmmcRegisters { - unsafe { &mut *(SDIO_BASE as *mut MesonSdmmcRegisters) } + /// This function is unsafe because it tries to + unsafe fn new() -> &'static mut MesonSdmmcRegisters { + &mut *(SDIO_BASE as *mut MesonSdmmcRegisters) } +} - /// Configures the SDIO clock based on the requested clock frequency and SoC type. - /// - /// # Arguments - /// - /// * `mmc_clock` - The desired clock frequency in Hz. - /// * `is_sm1_soc` - A boolean indicating whether the SoC is an SM1 variant. - /// * For odorid C4, this is_sm1_soc is true - fn meson_mmc_config_clock(&mut self, frequency: u32) { - // #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) - let mut meson_mmc_clk:u32 = 0; +struct DelayConfig { + timing: MmcTiming, // Clock rate in Hz + current_delay: u32, // Delay value in some unit, e.g., nanoseconds + tried_lowest_delay: u32, + tried_highest_delay: u32, +} - // Valid clock freq range: - // f_min = div_round_up!(SD_EMMC_CLKSRC_24M, CLK_MAX_DIV); - // f_max = 100000000; /* 100 MHz */ - let clk: u32; - let clk_src: u32; - // 400 khz for init the card - let clock_freq: u32 = frequency; - if clock_freq > 16000000 { - clk = SD_EMMC_CLKSRC_DIV2; - clk_src = CLK_SRC_DIV2; - } else { - clk = SD_EMMC_CLKSRC_24M; - clk_src = CLK_SRC_24M; +pub struct SdmmcMesonHardware { + register: &'static mut MesonSdmmcRegisters, + delay: Option, + timing: MmcTiming, + frequency: u32, + // Put other variables here +} + +impl SdmmcMesonHardware { + pub unsafe fn new() -> Self { + let register = MesonSdmmcRegisters::new(); + + // TODO: Call reset function here + SdmmcMesonHardware { + register, + delay: None, + // Default uboot speed class + timing: MmcTiming::SdHs, + // Wrong value but should not have much impact + frequency: MESON_MIN_FREQUENCY, } + } - let clk_div = div_round_up!(clk, clock_freq); - /* - * From uboot meson_gx_mmc.c - * SM1 SoCs doesn't work fine over 50MHz with CLK_CO_PHASE_180 - * If CLK_CO_PHASE_270 is used, it's more stable than other. - * Other SoCs use CLK_CO_PHASE_180 by default. - * It needs to find what is a proper value about each SoCs. - * Since we are using Odroid C4, we set phase to 270 - * TODO: Config it as what Linux driver are doing - */ - meson_mmc_clk |= CLK_CO_PHASE_270; - meson_mmc_clk |= CLK_TX_PHASE_000; + /// The meson_reset function reset the host register state + /// However, this function does not try to reset the power state like operating voltage and signal voltage + fn meson_reset(&mut self) { + let _ = self.sdmmc_set_power(MmcPowerMode::On); - meson_mmc_clk |= clk_src; - meson_mmc_clk |= clk_div; + // Stop execution + unsafe { + ptr::write_volatile(&mut self.register.start, 0); + } - unsafe { ptr::write_volatile(&mut self.clock, meson_mmc_clk); } - } + // Disable interrupt + unsafe { + ptr::write_volatile(&mut self.register.irq_en, 0); + } - // Incomplete placeholder function, need regulator system to configure voltage - pub fn meson_set_ios(&mut self) { - /* - * This function should be able to adjust the voltage, frequency and number of data lanes in use - * - */ + // Acknowledge interrupt + unsafe { + ptr::write_volatile(&mut self.register.status, IRQ_EN_MASK | IRQ_SDIO); + } + + // Reset delay and adjust registers + unsafe { + ptr::write_volatile(&mut self.register.delay1, 0); + } + unsafe { + ptr::write_volatile(&mut self.register.delay2, 0); + } + unsafe { + ptr::write_volatile(&mut self.register.adjust, 0); + } + + // Set clock to a low freq + if self.sdmmc_config_timing(MmcTiming::CardSetup).is_err() { + panic!("Fatal fault in setting frequency when resetting"); + } + + // Reset config register + let mut cfg: u32 = 0; + + // Set timeout bits + cfg |= CFG_RESP_TIMEOUT_256 & CFG_RESP_TIMEOUT_MASK; + + // Set cmd interval + // TODO: Both Linux and uboot use 16 cycle interval but is it the right value? + cfg |= CFG_RC_CC_16 & CFG_RC_CC_MASK; + + // Set block len to 512 + cfg |= CFG_BL_LEN_512 & CFG_BL_LEN_MASK; + + // Set task abort bit when error is encountered + cfg |= CFG_ERR_ABORT; + + unsafe { + ptr::write_volatile(&mut self.register.cfg, cfg); + } } // This function can be seen as a Rust version of meson_mmc_setup_cmd function in uboot @@ -230,33 +293,36 @@ impl MesonSdmmcRegisters { if cmd.resp_type & MMC_RSP_CRC == 0 { meson_mmc_cmd |= CMD_CFG_RESP_NOCRC; } - } - else { + } else { meson_mmc_cmd |= CMD_CFG_NO_RESP; } if let Some(data) = data { - let mut cfg: u32 = unsafe { ptr::read_volatile(&self.cfg) }; + let mut cfg: u32 = unsafe { ptr::read_volatile(&self.register.cfg) }; cfg &= !CFG_BL_LEN_MASK; cfg |= ilog2(data.blocksize) << CFG_BL_LEN_SHIFT; - - // This value should only be 512 - assert!(data.blocksize == 512); - unsafe { ptr::write_volatile(&mut self.cfg, cfg); }; - + // TODO: Maybe add blocksize is power of 2 check here? + // debug_log!("Configure register value: 0x{:08x}", cfg); + + unsafe { + ptr::write_volatile(&mut self.register.cfg, cfg); + }; + if let MmcDataFlag::SdmmcDataWrite = data.flags { meson_mmc_cmd |= CMD_CFG_DATA_WR; } - + meson_mmc_cmd |= CMD_CFG_DATA_IO | CMD_CFG_BLOCK_MODE | data.blockcnt; } meson_mmc_cmd |= CMD_CFG_TIMEOUT_4S | CMD_CFG_OWNER | CMD_CFG_END_OF_CHAIN; - unsafe { ptr::write_volatile(&mut self.cmd_cfg, meson_mmc_cmd); } + unsafe { + ptr::write_volatile(&mut self.register.cmd_cfg, meson_mmc_cmd); + } } fn meson_read_response(&self, cmd: &SdmmcCmd, response: &mut [u32; 4]) { @@ -267,77 +333,378 @@ impl MesonSdmmcRegisters { unsafe { // Yes, this is in a reverse order as rsp0 and self.cmd_rsp3 is the least significant // Check uboot read response code for more details - *rsp0 = ptr::read_volatile(&self.cmd_rsp3); - *rsp1 = ptr::read_volatile(&self.cmd_rsp2); - *rsp2 = ptr::read_volatile(&self.cmd_rsp1); - *rsp3 = ptr::read_volatile(&self.cmd_rsp); + *rsp0 = ptr::read_volatile(&self.register.cmd_rsp3); + *rsp1 = ptr::read_volatile(&self.register.cmd_rsp2); + *rsp2 = ptr::read_volatile(&self.register.cmd_rsp1); + *rsp3 = ptr::read_volatile(&self.register.cmd_rsp); } - // debug_println!("Meson received 4 response back!"); } else if cmd.resp_type & MMC_RSP_PRESENT != 0 { - unsafe { - *rsp0 = ptr::read_volatile(&self.cmd_rsp); - // debug_println!("Meson response value: {:#034b} (binary), {:#X} (hex)", *rsp0, *rsp0); - // debug_println!("Meson received 1 response back!"); + unsafe { + *rsp0 = ptr::read_volatile(&self.register.cmd_rsp); } } } + + /// The result that such function exists instead of using generic frequency is that different + /// hosts may have preferred frequency for speed classes. For example, in meson, the frequency for + /// UhsSdr104 would be 200Mhz instead of 208Mhz + fn meson_frequency(timing: MmcTiming) -> Result { + let freq: u32 = match timing { + MmcTiming::Legacy => 25000000, + MmcTiming::MmcHs => 26000000, + MmcTiming::SdHs => 50000000, + MmcTiming::UhsSdr12 => 25000000, + MmcTiming::UhsSdr25 => 50000000, + MmcTiming::UhsSdr50 => 100000000, + MmcTiming::UhsSdr104 => MESON_MAX_FREQUENCY, + MmcTiming::UhsDdr50 => 50000000, + MmcTiming::MmcDdr52 => 52000000, + MmcTiming::MmcHs200 => MESON_MAX_FREQUENCY, + MmcTiming::MmcHs400 => MESON_MAX_FREQUENCY, + // Typical low frequency for card initialization + MmcTiming::CardSetup => MESON_MIN_FREQUENCY, + MmcTiming::CardSleep => MESON_MIN_FREQUENCY, + _ => { + return Err(SdmmcError::EINVAL); + } + }; + Ok(freq) + } + + fn meson_stop_clock(&mut self, stop: bool) { + unsafe { + // Read the current configuration register value + let mut meson_mmc_cfg: u32 = ptr::read_volatile(&self.register.cfg); + + // Update the CFG_STOP_CLOCK bit based on the `stop` parameter + meson_mmc_cfg = (meson_mmc_cfg & !CFG_STOP_CLOCK) | ((stop as u32) * CFG_STOP_CLOCK); + + // Write the updated value back to the configuration register + ptr::write_volatile(&mut self.register.cfg, meson_mmc_cfg); + } + } + + fn meson_enable_ddr(&mut self, enable: bool) { + unsafe { + // Read the current configuration register value + let mut meson_mmc_cfg: u32 = ptr::read_volatile(&self.register.cfg); + + // Update the CFG_DDR_MODE bit based on the `enable` parameter + meson_mmc_cfg = (meson_mmc_cfg & !CFG_DDR_MODE) | ((enable as u32) * CFG_DDR_MODE); + + // Write the updated value back to the configuration register + ptr::write_volatile(&mut self.register.cfg, meson_mmc_cfg); + } + } + + const DESC_STOP_TIMEOUT_NS: u32 = 5_000_000; // 5ms in nanoseconds + const POLL_INTERVAL_NS: u32 = 100_000; // 100µs in nanoseconds + /// Waits for the descriptor engine to stop, with a timeout + fn meson_wait_desc_stop(&self) -> Result<(), SdmmcError> { + let mut start_time: u32 = 0; + + while unsafe { ptr::read_volatile(&self.register.status) } & (STATUS_DESC_BUSY | STATUS_BUSY) != 0 { + if start_time > Self::DESC_STOP_TIMEOUT_NS { + return Err(SdmmcError::EUNDEFINED); + } + // Use the provided wait function instead of sleep + process_wait_unreliable(Self::POLL_INTERVAL_NS as u64); + start_time += Self::POLL_INTERVAL_NS; + } + Ok(()) + } } -impl SdmmcHardware for MesonSdmmcRegisters { - fn sdmmc_send_command(&mut self, cmd: &SdmmcCmd, data: Option<&MmcData>) -> Result<(), SdmmcHalError> { - // It seems that let Some(mmc_data) = data && mmc_data.blocksize != 512 - // is not stable on this nightly 2024.05.01 compiler +impl SdmmcHardware for SdmmcMesonHardware { + fn sdmmc_init(&mut self) -> Result<(MmcIos, HostInfo, u128), SdmmcError> { + let cap: u128 = MMC_TIMING_LEGACY + | MMC_TIMING_SD_HS + | MMC_TIMING_UHS + | MMC_CAP_VOLTAGE_TUNE + | MMC_CAP_4_BIT_DATA + | MMC_CAP_CMD23; + + // Reset host state + self.meson_reset(); + + let ios: MmcIos = MmcIos { + clock: MESON_MIN_FREQUENCY as u64, + // On odroid c4, the operating voltage is default to 3.3V + vdd: (MMC_VDD_33_34 | MMC_VDD_32_33 | MMC_VDD_31_32), + // TODO, figure out the correct value when we can power the card on and off + power_delay_ms: 10, + power_mode: MmcPowerMode::On, + bus_width: MmcBusWidth::Width1, + signal_voltage: MmcSignalVoltage::Voltage330, + enabled_irq: false, + emmc: None, + spi: None, + }; + + let info: HostInfo = HostInfo { + max_frequency: MESON_MAX_FREQUENCY as u64, + min_frequency: MESON_MIN_FREQUENCY as u64, + max_block_per_req: MAX_BLOCK_PER_TRANSFER, + }; + + return Ok((ios, info, cap)); + } + + fn sdmmc_tune_sampling(&mut self, state: TuningState) -> Result<(), SdmmcError> { + match state { + TuningState::TuningStart => { + if let Some(ref mut delay_config) = self.delay { + delay_config.tried_highest_delay = delay_config.current_delay; + delay_config.tried_lowest_delay = delay_config.current_delay; + } + return Ok(()); + } + TuningState::TuningContinue => (), + TuningState::TuningComplete => return Ok(()), + } + + let mut delay_config = match self.delay.take() { + Some(config) if config.timing == self.timing => config, + _ => DelayConfig { + timing: self.timing, + current_delay: 0, + tried_lowest_delay: 0, + tried_highest_delay: 0, + }, + }; + + let mut adjust: u32 = SD_EMMC_ADJ_ENABLE; + let clk: u32; + + unsafe { + clk = ptr::read_volatile(&self.register.clock); + } + + let clk_src: u32 = clk & CLK_SRC_MASK; + + let mux_clk_freq: u32 = match clk_src { + CLK_SRC_24M => SD_EMMC_CLKSRC_24M, + CLK_SRC_DIV2 => SD_EMMC_CLKSRC_DIV2, + _ => return Err(SdmmcError::EUNDEFINED), + }; + + let max_div: u32 = div_round_up!(mux_clk_freq, self.frequency); + + if max_div - delay_config.tried_highest_delay >= delay_config.tried_lowest_delay { + if max_div - delay_config.tried_highest_delay == 0 { + return Err(SdmmcError::EUNSUPPORTEDCARD); + } + delay_config.tried_highest_delay += 1; + delay_config.current_delay = delay_config.tried_highest_delay; + } else { + delay_config.tried_lowest_delay -= 1; + delay_config.current_delay = delay_config.tried_lowest_delay; + } + + adjust |= (delay_config.current_delay << SD_EMMC_ADJ) & SD_EMMC_ADJUST_ADJ_DELAY_MASK; + + debug_log!("Tuning sampling function: Current delay: {}, tried lowest delay: {}, tried highest delay: {}, final register value: 0x{:08x}\n", delay_config.current_delay, delay_config.tried_lowest_delay, delay_config.tried_highest_delay, adjust); + + self.delay = Some(delay_config); + + unsafe { + ptr::write_volatile(&mut self.register.adjust, adjust); + } + + Ok(()) + } + + fn sdmmc_config_timing(&mut self, timing: MmcTiming) -> Result { + // Why calling this function if the timing does not change? + if self.timing == timing { + return Ok(self.frequency as u64); + } + + if timing == MmcTiming::ClockStop { + // Disable the clock completely + self.meson_stop_clock(true); + + // Change the timing + self.timing = timing; + self.frequency = 0; + + return Ok(0); + } + + let freq: u32 = Self::meson_frequency(timing)?; + + if freq > MESON_MAX_FREQUENCY || freq < MESON_MIN_FREQUENCY { + return Err(SdmmcError::EINVAL); + } + // #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + let mut meson_mmc_clk: u32 = 0; + + // Valid clock freq range: + // f_min = div_round_up!(SD_EMMC_CLKSRC_24M, CLK_MAX_DIV); + // f_max = 100000000; /* 100 MHz */ + let clk: u32; + let clk_src: u32; + // 400 khz for init the card + let clock_freq: u32 = freq as u32; + if clock_freq > 16000000 { + clk = SD_EMMC_CLKSRC_DIV2; + clk_src = CLK_SRC_DIV2; + } else { + clk = SD_EMMC_CLKSRC_24M; + clk_src = CLK_SRC_24M; + } + + let clk_div = div_round_up!(clk, clock_freq); + /* + * From uboot meson_gx_mmc.c + * SM1 SoCs doesn't work fine over 50MHz with CLK_CO_PHASE_180 + * If CLK_CO_PHASE_270 is used, it's more stable than other. + * Other SoCs use CLK_CO_PHASE_180 by default. + * Linux default to use CLK_CO_PHASE_180 + * However, if CLK_CO_PHASE_180 is used without tuning, + * Sdcard will not work in High speed mode on Odroid C4 + */ + meson_mmc_clk |= CLK_CO_PHASE_180; + // meson_mmc_clk |= CLK_CO_PHASE_270; + meson_mmc_clk |= CLK_TX_PHASE_000; + + meson_mmc_clk |= clk_src; + meson_mmc_clk |= clk_div; + + unsafe { + ptr::write_volatile(&mut self.register.clock, meson_mmc_clk); + } + + // If pervious timing is clock stop, renable the clock here + if self.timing == MmcTiming::ClockStop { + self.meson_stop_clock(false); + } + + // If the pervious timing mode is using ddr, then disable ddr + if self.timing == MmcTiming::UhsDdr50 || self.timing == MmcTiming::MmcDdr52 { + self.meson_enable_ddr(false); + } + + // If the next timing mode is using ddr, enable ddr + if timing == MmcTiming::UhsDdr50 || timing == MmcTiming::MmcDdr52 { + self.meson_enable_ddr(true); + } + + // Update timing + self.timing = timing; + self.frequency = clk / clk_div; + + Ok(self.frequency as u64) + } + + fn sdmmc_config_bus_width(&mut self, bus_width: MmcBusWidth) -> Result<(), SdmmcError> { + let mut meson_mmc_cfg: u32; + unsafe { + meson_mmc_cfg = ptr::read_volatile(&self.register.cfg); + } + match bus_width { + MmcBusWidth::Width1 => meson_mmc_cfg |= CFG_BUS_WIDTH_1, + MmcBusWidth::Width4 => meson_mmc_cfg |= CFG_BUS_WIDTH_4, + MmcBusWidth::Width8 => meson_mmc_cfg |= CFG_BUS_WIDTH_8, + } + unsafe { + ptr::write_volatile(&mut self.register.cfg, meson_mmc_cfg); + } + Ok(()) + } + + fn sdmmc_read_datalanes(&self) -> Result { + unsafe { + // Read the status register + let status = ptr::read_volatile(&self.register.status); + + // Extract and return the DAT signal state + Ok(((status & STATUS_DAT_MASK) >> STATUS_DAT_SHIFT) as u8) + } + } + + fn sdmmc_send_command( + &mut self, + cmd: &SdmmcCmd, + data: Option<&MmcData>, + ) -> Result<(), SdmmcError> { // Set up the data addr let mut data_addr: u32 = 0u32; if let Some(mmc_data) = data { // TODO: Check what if the addr is u32::MAX, will the sdcard still working? - if mmc_data.blocksize != MESON_SDCARD_SECTOR_SIZE || mmc_data.addr >= (WRITE_ADDR_UPPER as u64) - || mmc_data.blockcnt == 0 || mmc_data.blockcnt > MAX_BLOCK_PER_TRANSFER { - debug_println!("SDMMC: INVALID INPUT VARIABLE!"); - return Err(SdmmcHalError::EINVAL); + if mmc_data.addr >= (WRITE_ADDR_UPPER as u64) + || mmc_data.blockcnt == 0 + || mmc_data.blockcnt > MAX_BLOCK_PER_TRANSFER + { + debug_log!("SDMMC: INVALID INPUT VARIABLE!"); + return Err(SdmmcError::EINVAL); } // Depend on the flag and hardware, the cache should be flushed accordingly data_addr = mmc_data.addr as u32; } // Stop data transfer - unsafe { ptr::write_volatile(&mut self.start, 0u32); } + unsafe { + ptr::write_volatile(&mut self.register.start, 0u32); + } + + unsafe { + ptr::write_volatile(&mut self.register.cmd_dat, data_addr); + } - unsafe { ptr::write_volatile(&mut self.cmd_dat, data_addr); } - self.meson_mmc_set_up_cmd_cfg_and_cfg(&cmd, data); - // Reset status register before executing the cmd - // If we keep this line of code, do we still need to manually ack interrupts??? - unsafe { ptr::write_volatile(&mut self.status, STATUS_MASK); } + // I am still keeping this line of code here but I think it is not necessary + unsafe { + ptr::write_volatile(&mut self.register.status, STATUS_MASK); + } // Clear the response register, for testing & debugging - unsafe { ptr::write_volatile(&mut self.cmd_rsp, 0u32); } + unsafe { + ptr::write_volatile(&mut self.register.cmd_rsp, 0u32); + } - unsafe { ptr::write_volatile(&mut self.cmd_arg, cmd.cmdarg); } + unsafe { + ptr::write_volatile(&mut self.register.cmd_arg, cmd.cmdarg); + } Ok(()) } - fn sdmmc_receive_response(&self, cmd: &SdmmcCmd, response: &mut [u32; 4]) -> Result<(), SdmmcHalError> { + fn sdmmc_receive_response( + &self, + cmd: &SdmmcCmd, + response: &mut [u32; 4], + ) -> Result<(), SdmmcError> { let status: u32; - unsafe { status = ptr::read_volatile(&self.status); } + unsafe { + status = ptr::read_volatile(&self.register.status); + } if (status & STATUS_END_OF_CHAIN) == 0 { - return Err(SdmmcHalError::EBUSY); + return Err(SdmmcError::EBUSY); } if (status & STATUS_RESP_TIMEOUT) != 0 { - debug_println!("SDMMC: CARD TIMEOUT!"); - return Err(SdmmcHalError::ETIMEDOUT); + debug_log!( + "SDMMC: CARD TIMEOUT! Host status register: 0x{:08x}\n", + status + ); + // This could negatively impact the result of benchmarking in case of cmd error + self.meson_wait_desc_stop()?; + return Err(SdmmcError::ETIMEDOUT); } - - let mut return_val: Result<(), SdmmcHalError> = Ok(()); + + let mut return_val: Result<(), SdmmcError> = Ok(()); if (status & STATUS_ERR_MASK) != 0 { - debug_println!("SDMMC: CARD IO ERROR!"); - return_val = Err(SdmmcHalError::EIO); + debug_log!( + "SDMMC: CARD IO ERROR! Host status register: 0x{:08x}\n", + status + ); + self.meson_wait_desc_stop()?; + return_val = Err(SdmmcError::EIO); } self.meson_read_response(cmd, response); @@ -345,33 +712,147 @@ impl SdmmcHardware for MesonSdmmcRegisters { return_val } - fn sdmmc_enable_interrupt(&mut self, irq_to_enable: &mut u32) -> Result<(), SdmmcHalError> { - let mut irq_bits_to_set: u32 = 0; - if *irq_to_enable & (InterruptType::Success as u32) > 0 { - irq_bits_to_set |= IRQ_END_OF_CHAIN; + /// This function is meant to clear, acknowledge and then reenable the interrupt + fn sdmmc_config_interrupt(&mut self, enable_irq: bool, enable_sdio_irq: bool) -> Result<(), SdmmcError> { + // Disable interrupt + unsafe { + ptr::write_volatile(&mut self.register.irq_en, 0); + } + + // Acknowledge interrupt + unsafe { + ptr::write_volatile(&mut self.register.status, IRQ_EN_MASK | IRQ_SDIO); } - if *irq_to_enable & (InterruptType::Error as u32) > 0 { - irq_bits_to_set |= IRQ_ERR_MASK; + let mut irq_bits_to_set: u32 = 0; + if enable_irq == true { + irq_bits_to_set |= IRQ_EN_MASK; } - if *irq_to_enable & (InterruptType::SDIO as u32) > 0 { + if enable_sdio_irq == true { irq_bits_to_set |= IRQ_SDIO; } - unsafe { ptr::write_volatile(&mut self.irq_en, irq_bits_to_set); } + unsafe { + ptr::write_volatile(&mut self.register.irq_en, irq_bits_to_set); + } return Ok(()); } - fn sdmmc_ack_interrupt(&mut self, irq_enabled: &u32) -> Result<(), SdmmcHalError> { - let mut irq_bits_to_set: u32 = 0; - if *irq_enabled & (InterruptType::Success as u32) > 0 { - irq_bits_to_set |= IRQ_END_OF_CHAIN; + fn sdmmc_ack_interrupt(&mut self) -> Result<(), SdmmcError> { + unsafe { + ptr::write_volatile(&mut self.register.status, IRQ_END_OF_CHAIN | IRQ_ERR_MASK | IRQ_SDIO); } - if *irq_enabled & (InterruptType::Error as u32) > 0 { - irq_bits_to_set |= IRQ_ERR_MASK; + return Ok(()); + } + + fn sdmmc_set_signal_voltage(&mut self, voltage: MmcSignalVoltage) -> Result<(), SdmmcError> { + match voltage { + MmcSignalVoltage::Voltage330 => { + let mut value: u32; + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); + } + value &= !(1 << 6); + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); + } + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + value &= !(1 << 6); + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); + } + } + MmcSignalVoltage::Voltage180 => { + let mut value: u32; + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); + } + value &= !(1 << 6); + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); + } + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + value |= 1 << 6; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); + } + } + MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EINVAL), } - if *irq_enabled & (InterruptType::SDIO as u32) > 0 { - irq_bits_to_set |= IRQ_SDIO; + // Disable pull-up/down for gpioAO_6 + let mut value: u32; + unsafe { + value = ptr::read_volatile(AO_RTI_PULL_UP_EN_REG as *const u32); } - unsafe { ptr::write_volatile(&mut self.status, irq_bits_to_set); } - return Ok(()); + value &= !(1 << 6); // Disable pull-up/down for gpioAO_6 + unsafe { + ptr::write_volatile(AO_RTI_PULL_UP_EN_REG as *mut u32, value); + } + + Ok(()) } -} \ No newline at end of file + + + // Experimental function that tries to modify the pin that might control the power of the sdcard slot + // This function should be pretty much the same with sdmmc_set_signal_voltage, the only difference + // is the pin modified is gpioAO_3, busy wait is needed to be added after setting the power + fn sdmmc_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { + /* + unsafe { + debug_log!("In set power function\n"); + let mut value: u32; + + // Read the value using ptr::read_volatile + value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); + debug_log!("Address {:#x}: {:#x}\n", AO_RTI_OUTPUT_ENABLE_REG, value); + + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + debug_log!("Address {:#x}: {:#x}\n", AO_RTI_OUTPUT_LEVEL_REG, value); + + value = ptr::read_volatile(AO_RTI_PULL_UP_EN_REG as *const u32); + debug_log!("Address {:#x}: {:#x}\n", AO_RTI_PULL_UP_EN_REG, value); + } + */ + + let mut value: u32; + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); + } + // If the GPIO pin is not being set as output, set it to output first + if value & GPIO_AO_3 != 0 { + value &= !GPIO_AO_3; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); + } + } + match power_mode { + MmcPowerMode::On => { + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + if value & GPIO_AO_3 == 0 { + value |= GPIO_AO_3; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); + } + } + self.sdmmc_set_signal_voltage(MmcSignalVoltage::Voltage330)?; + } + MmcPowerMode::Off => { + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + if value & GPIO_AO_3 != 0 { + value &= !GPIO_AO_3; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); + } + } + } + _ => return Err(SdmmcError::EINVAL), + } + Ok(()) + } +} diff --git a/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml b/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml index c17254306..102c640b5 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml +++ b/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml @@ -2,7 +2,18 @@ name = "sdmmc_protocol" version = "0.1.0" edition = "2021" +authors = ["Cheng Li 李澄 "] [lib] name = "sdmmc_protocol" -path = "lib.rs" \ No newline at end of file +path = "lib.rs" + +[dependencies] +bitflags = "2.6.0" +sel4-microkit-support = { path = "../optional_os_support/sel4-microkit-support", optional = true } + +[features] +# Feature Flags: +# - sel4-microkit: Enables support for the optional `sel4-microkit` crate, +# adding seL4-specific functionalities such as debug output and optimized sleep functions. +sel4-microkit = ["dep:sel4-microkit-support"] \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_protocol/lib.rs b/drivers/blk/sdmmc/sdmmc_protocol/lib.rs index 8a12c3789..07b0cb627 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/lib.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/lib.rs @@ -1,3 +1,5 @@ -#![no_std] // Don't link the standard library +#![no_std] // Don't link the standard library -pub mod sdmmc; \ No newline at end of file +pub mod sdmmc; +pub mod sdmmc_traits; +pub mod sdmmc_os; diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs index 52cf6adf3..80dfa2ae4 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -1,7 +1,29 @@ -use core::{future::Future, pin::Pin, task::{Context, Poll, Waker}}; +use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, Waker}, +}; -use sdmmc_constant::{MMC_CMD_READ_MULTIPLE_BLOCK, MMC_CMD_READ_SINGLE_BLOCK, MMC_CMD_STOP_TRANSMISSION, MMC_CMD_WRITE_MULTIPLE_BLOCK, MMC_CMD_WRITE_SINGLE_BLOCK}; +use mmc_struct::{MmcBusWidth, MmcDevice, MmcState, MmcTiming, TuningState}; +use sdcard::{Cid, Csd, Sdcard}; +use sdmmc_capability::{ + SdcardCapability, SdmmcHostCapability, MMC_CAP_4_BIT_DATA, MMC_CAP_VOLTAGE_TUNE, MMC_EMPTY_CAP, + MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS_DDR50, MMC_TIMING_UHS_SDR104, + MMC_TIMING_UHS_SDR12, MMC_TIMING_UHS_SDR25, MMC_TIMING_UHS_SDR50 +}; +use sdmmc_constant::{ + MMC_CMD_ALL_SEND_CID, MMC_CMD_APP_CMD, MMC_CMD_ERASE, MMC_CMD_GO_IDLE_STATE, MMC_CMD_READ_MULTIPLE_BLOCK, MMC_CMD_READ_SINGLE_BLOCK, MMC_CMD_SELECT_CARD, MMC_CMD_SEND_CSD, MMC_CMD_SET_BLOCK_COUNT, MMC_CMD_STOP_TRANSMISSION, MMC_CMD_WRITE_MULTIPLE_BLOCK, MMC_CMD_WRITE_SINGLE_BLOCK, OCR_BUSY, OCR_HCS, OCR_S18R, SD_CMD_APP_SEND_OP_COND, SD_CMD_APP_SET_BUS_WIDTH, SD_CMD_ERASE_WR_BLK_END, SD_CMD_ERASE_WR_BLK_START, SD_CMD_SEND_IF_COND, SD_CMD_SEND_RELATIVE_ADDR, SD_CMD_SWITCH_FUNC, SD_CMD_SWITCH_UHS18V, SD_ERASE_ARG, SD_SWITCH_FUNCTION_GROUP_ONE, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_SDHS, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR12, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR25, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR50, SD_SWITCH_FUNCTION_GROUP_ONE_SET_LEGACY, SD_SWITCH_FUNCTION_GROUP_ONE_SET_SDHS, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_DDR50, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR104, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR12, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR25, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR50, SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE +}; + +use crate::sdmmc_traits::SdmmcHardware; + +use crate::sdmmc_os::{debug_log, process_wait_unreliable}; + +pub mod mmc_struct; +mod sdcard; +pub mod sdmmc_capability; mod sdmmc_constant; + pub struct SdmmcCmd { pub cmdidx: u32, pub resp_type: u32, @@ -22,34 +44,29 @@ pub enum MmcDataFlag { SdmmcDataWrite, } -pub enum SdmmcHalError { +#[derive(Debug)] +pub enum SdmmcError { // Error for result not ready yet EBUSY, ETIMEDOUT, EINVAL, EIO, + EUNSUPPORTEDCARD, ENOTIMPLEMENTED, // This error should not be triggered unless there are bugs in program EUNDEFINED, // The block transfer succeed, but fail to stop the read/write process ESTOPCMD, -} - -// Interrupt related define -#[repr(u32)] // Ensures the enum variants are stored as 32-bit integers -pub enum InterruptType { - Success = 0b0001, // 1st bit - Error = 0b0010, // 2nd bit - SDIO = 0b0100, // 3rd bit - // You can add more flags as needed + ENOCARD, + ECARDINACTIVE, } // Define the MMC response flags const MMC_RSP_PRESENT: u32 = 1 << 0; -const MMC_RSP_136: u32 = 1 << 1; // 136-bit response -const MMC_RSP_CRC: u32 = 1 << 2; // Expect valid CRC -const MMC_RSP_BUSY: u32 = 1 << 3; // Card may send busy -const MMC_RSP_OPCODE: u32 = 1 << 4; // Response contains opcode +const MMC_RSP_136: u32 = 1 << 1; // 136-bit response +const MMC_RSP_CRC: u32 = 1 << 2; // Expect valid CRC +const MMC_RSP_BUSY: u32 = 1 << 3; // Card may send busy +const MMC_RSP_OPCODE: u32 = 1 << 4; // Response contains opcode // Define the MMC response types pub const MMC_RSP_NONE: u32 = 0; @@ -62,108 +79,1009 @@ pub const MMC_RSP_R5: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; pub const MMC_RSP_R6: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; pub const MMC_RSP_R7: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; +// Enums for power_mode +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum MmcPowerMode { + Off = 0, + On = 1, + Undefined = 2, +} + +// Signal voltage +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum MmcSignalVoltage { + Voltage330 = 0, + Voltage180 = 1, + Voltage120 = 2, +} + +// Driver type +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum MmcDriverType { + TypeB = 0, + TypeA = 1, + TypeC = 2, + TypeD = 3, +} + +// Enums for chip_select +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum MmcChipSelect { + DontCare = 0, + High = 1, + Low = 2, +} + +/// Settings specific to eMMC cards. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EmmcSettings { + /// The drive strength of the host driver, typically relevant for eMMC devices. + /// + /// - The drive strength affects signal integrity and is selected based on the card's + /// operating conditions, such as bus load and speed. + /// - The eMMC specification defines four possible driver types (A, B, C, D) that + /// optimize for different use cases and electrical environments: + /// - `DriverType::TypeB`: Default driver strength for most cases. + /// - `DriverType::TypeA`, `TypeC`, `TypeD`: Other driver types based on signal + /// strength requirements. + pub drv_type: MmcDriverType, + + /// Specifies whether **HS400 Enhanced Strobe** mode is enabled. + /// + /// - Enhanced Strobe is used in **HS400** mode for eMMC devices to improve data + /// reliability at high speeds. It allows more accurate data capture by aligning + /// strobe signals with data. + /// - This is only relevant for eMMC cards in **HS400ES** mode. + pub enhanced_strobe: bool, +} + +/// Settings specific to SPI communication mode. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SpiSettings { + /// The chip select mode used in **SPI mode** communication. + /// + /// - This field is relevant only when the SD/MMC host controller is operating in **SPI mode**. + /// In **native SD/MMC protocol**, this field is not used. + /// + /// - The **chip select (CS)** pin is used to activate or deactivate the SD/MMC card on the SPI bus. + /// It allows the host to select which device it is communicating with when multiple devices share the same bus. + /// + /// - Possible values: + /// - `MmcChipSelect::DontCare`: The chip select state is ignored by the host. + /// - `MmcChipSelect::High`: The chip select pin is driven high, indicating that the card is not selected. + /// - `MmcChipSelect::Low`: The chip select pin is driven low, indicating that the card is selected and active. + /// + /// **Note**: + /// - In **native SD/MMC mode**, communication happens via dedicated **command and data lines** without the need for chip select. + /// - In most applications, **SPI mode** is less commonly used, especially in high-performance systems. + pub chip_select: MmcChipSelect, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +/// The `MmcIos` struct represents the I/O settings for the SD/MMC controller, +/// configuring how the host communicates with the card during various operations. +pub struct MmcIos { + /// The clock rate (in Hz) used for communication with the SD/MMC card. + /// + /// - This field specifies the frequency at which data is transferred between + /// the host and the card. The clock can vary depending on the mode the card + /// is in (e.g., initialization, data transfer). + /// - Typically, initialization occurs at a lower clock rate, and high-speed + /// data transfer occurs at higher rates. + pub clock: u64, + + /// The voltage range (VDD) used for powering the SD/MMC card. + /// + /// - This field stores the selected voltage range in a bit-encoded format. + /// It indicates the voltage level the card is operating at. + /// - Common voltage levels are 3.3V, 1.8V, and sometimes 1.2V (for eMMC). + /// - Cards often negotiate their operating voltage during initialization. + pub vdd: u32, + + /// The power delay (in milliseconds) used after powering the card to ensure + /// stable operation. + /// + /// - After powering up the card, the host controller typically waits for a + /// certain period before initiating communication to ensure that the card's + /// power supply is stable. + /// - This delay ensures the card is ready to respond to commands. + pub power_delay_ms: u32, + + /// The current power supply mode for the SD/MMC card. + /// + /// - This field indicates whether the card is powered on, powered off, or + /// being powered up. The power mode can affect the card's internal state + /// and availability for communication. + /// - Possible values: + /// - `PowerMode::Off`: The card is completely powered off. + /// - `PowerMode::Up`: The card is in the process of powering up. + /// - `PowerMode::On`: The card is fully powered and ready for communication. + pub power_mode: MmcPowerMode, + + /// The width of the data bus used for communication between the host and the card. + /// + /// - This field specifies whether the bus operates in 1-bit, 4-bit, or 8-bit mode. + /// - Wider bus widths (4-bit, 8-bit) enable higher data transfer rates, but not all + /// cards or host controllers support every bus width. + /// - Common values: + /// - `BusWidth::Width1`: 1-bit data width (lowest speed, used during initialization). + /// - `BusWidth::Width4`: 4-bit data width (common for SD cards). + /// - `BusWidth::Width8`: 8-bit data width (mainly for eMMC). + pub bus_width: MmcBusWidth, + + /// The signaling voltage level used for communication with the card. + /// + /// - Different SD/MMC cards support different signaling voltage levels. This field + /// indicates the voltage level used for signaling between the host and the card. + /// - Common voltage levels: + /// - `SignalVoltage::Voltage330`: 3.3V signaling. + /// - `SignalVoltage::Voltage180`: 1.8V signaling. + /// - `SignalVoltage::Voltage120`: 1.2V signaling (mainly for newer eMMC devices). + pub signal_voltage: MmcSignalVoltage, + + /// Indicating if interrupt is enabled or not + pub enabled_irq: bool, + + /// eMMC-specific settings, if applicable. + /// + /// This field is `None` if the card is not an eMMC card. + pub emmc: Option, + + /// SPI-specific settings, if applicable. + /// + /// This field is `None` if the card is not operating in SPI mode. + pub spi: Option, +} + +#[derive(Debug, Clone)] +pub struct HostInfo { + pub max_frequency: u64, + pub min_frequency: u64, + pub max_block_per_req: u32, +} + +/// TODO: Add more variables for SdmmcProtocol to track the state of the sdmmc controller and card correctly +pub struct SdmmcProtocol { + pub hardware: T, + + mmc_ios: MmcIos, + + host_info: HostInfo, + + host_capability: SdmmcHostCapability, -/// Program async Rust can be very dangerous if you do not know what is happening understand the hood -/// Power up and power off cannot be properly implemented if I do not have access to control gpio/ regulator and timer -pub trait SdmmcHardware { - fn sdmmc_power_up(&mut self) -> Result<(), SdmmcHalError> { - return Err(SdmmcHalError::ENOTIMPLEMENTED); + /// This mmc device is optional because there may not always be a card in the slot! + mmc_device: Option, +} + +impl Unpin for SdmmcProtocol where T: Unpin + SdmmcHardware {} + +impl SdmmcProtocol { + pub fn new(mut hardware: T) -> Result { + let (ios, info, cap) = hardware.sdmmc_init()?; + + Ok(SdmmcProtocol { + hardware, + mmc_ios: ios, + host_info: info, + host_capability: SdmmcHostCapability(cap), + mmc_device: None, + }) } - fn sdmmc_set_ios(&mut self) -> Result<(), SdmmcHalError> { - return Err(SdmmcHalError::ENOTIMPLEMENTED); + fn sdcard_init(&mut self, voltage_switch: bool) -> Result<(), SdmmcError> { + let mut resp: [u32; 4] = [0; 4]; + + let mut cmd = SdmmcCmd { + cmdidx: MMC_CMD_GO_IDLE_STATE, + resp_type: MMC_RSP_NONE, + cmdarg: 0, + }; + + debug_log!("Try to send go idle cmd\n"); + + // Go idle command does not expect a response + self.hardware.sdmmc_send_command(&cmd, None)?; + + // Linux use 1ms delay here, we use 2ms + process_wait_unreliable(2_000_000); + + debug_log!("Try to send check operating voltage cmd\n"); + + cmd = SdmmcCmd { + cmdidx: SD_CMD_SEND_IF_COND, + resp_type: MMC_RSP_R7, + cmdarg: 0x000001AA, // Voltage supply and check pattern + }; + + let res = Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp); + + // If the result is OK and the resp is 0x1AA, the card we are initializing is a SDHC/SDXC + // If the result is error, it is either the voltage not being set up correctly, which mean a bug in hardware layer + // or the card is eMMC or legacy sdcard + // For now, we only deal with the situation it is a sdcard + if res.is_ok() && resp[0] != 0x1AA { + return Err(SdmmcError::EUNSUPPORTEDCARD); + } + + // Uboot define this value to 1000... + let mut retry: u16 = 1000; + + loop { + debug_log!("Sending SD_CMD_APP_SEND_OP_COND!\n"); + // Prepare CMD55 (APP_CMD) + cmd = SdmmcCmd { + cmdidx: MMC_CMD_APP_CMD, + resp_type: MMC_RSP_R1, + cmdarg: 0, + }; + + // Send CMD55 + let res = Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp); + + match res { + Ok(_) => {}, + Err(SdmmcError::ETIMEDOUT) => return Err(SdmmcError::EUNSUPPORTEDCARD), + Err(_) => return res, + } + + cmd = SdmmcCmd { + cmdidx: SD_CMD_APP_SEND_OP_COND, + resp_type: MMC_RSP_R3, + cmdarg: 0, + }; + + // Set the HCS bit if version is SD Version 2 + // Since we are only support SDHC card, the OCR_HCS bit should be supported by the card + cmd.cmdarg |= OCR_HCS; + + // Right now we deliberately not set XPC bit for maximum compatibility + + // Change this when we decide to support spi or SDSC as well + cmd.cmdarg |= self.mmc_ios.vdd & 0xff8000; + + if self + .host_capability + .contains(sdmmc_capability::SdmmcHostCapability( + MMC_TIMING_UHS_SDR12 | MMC_CAP_VOLTAGE_TUNE, + )) + { + cmd.cmdarg |= OCR_S18R; + // It seems that cards will not respond to commands that have MMC_VDD_165_195 bit set, even if the card supports UHS-I + // cmd.cmdarg |= MMC_VDD_165_195; + } + + // Send ACMD41 + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + debug_log!("OCR: {:08x}\n", resp[0]); + + // Check if card is ready (OCR_BUSY bit) + if (resp[0] & OCR_BUSY) != 0 { + break; + } + + // retry handling + if retry <= 0 { + debug_log!("SDMMC: SEND_OP_COND failed, card not supported!\n"); + return Err(SdmmcError::EUNSUPPORTEDCARD); + } + retry -= 1; + } + + // Checking if the host and card is eligible for voltage switch + if self + .host_capability + .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) && voltage_switch == true + && resp[0] & OCR_HCS == OCR_HCS && resp[0] & OCR_S18R == OCR_S18R { + // TODO: If the sdcard fail at this stage, a power circle and reinit should be performed + self.tune_sdcard_switch_uhs18v()?; + self.mmc_ios.signal_voltage = MmcSignalVoltage::Voltage180; + } + + Ok(()) } - fn sdmmc_send_command(&mut self, cmd: &SdmmcCmd, data: Option<&MmcData>) -> Result<(), SdmmcHalError> { - return Err(SdmmcHalError::ENOTIMPLEMENTED); + fn sdmmc_power_cycle(&mut self) -> Result<(), SdmmcError> { + self.hardware.sdmmc_set_power(MmcPowerMode::Off)?; + process_wait_unreliable(5_000_000); + + self.hardware.sdmmc_set_power(MmcPowerMode::On)?; + process_wait_unreliable(1_000_000); + + self.mmc_ios.power_mode = MmcPowerMode::On; + Ok(()) } - fn sdmmc_receive_response(&self, cmd: &SdmmcCmd, response: &mut [u32; 4]) -> Result<(), SdmmcHalError> { - return Err(SdmmcHalError::ENOTIMPLEMENTED); + // Funtion that is not completed + pub fn setup_card(&mut self) -> Result<(), SdmmcError> { + // Disable all irqs here + self.hardware.sdmmc_config_interrupt(false, false)?; + + // TODO: Different sdcard and eMMC support different voltages, figure those out + if self.mmc_ios.power_mode != MmcPowerMode::On { + debug_log!("Turn the power up"); + self.hardware.sdmmc_set_power(MmcPowerMode::On)?; + // TODO: Right now we know the power will always be up and this function should not be called + // But when we encounter scenerio that may actually call this function, we should wait for the time specified in ios + // Right now this whole power up related thing does not work + process_wait_unreliable(self.mmc_ios.power_delay_ms as u64 * 1000000); + + self.mmc_ios.power_mode = MmcPowerMode::On; + } + + let clock = self.hardware.sdmmc_config_timing(MmcTiming::CardSetup)?; + + self.mmc_ios.clock = clock; + + // This line of code may not be needed? + self.hardware.sdmmc_config_bus_width(MmcBusWidth::Width1)?; + + self.mmc_ios.bus_width = MmcBusWidth::Width1; + + let mut voltage_switch: bool = true; + + // Use labeled block here for better clarification + // There could be more complex retry logic implemented in the future + 'sdcard_init: loop { + match self.sdcard_init(voltage_switch) { + Ok(()) => {}, + Err(SdmmcError::EUNSUPPORTEDCARD) => { + break 'sdcard_init SdmmcError::EUNSUPPORTEDCARD; + }, + Err(_) => { + if voltage_switch == true { + voltage_switch = false; + // Retry with voltage switch off + debug_log!("Try to init the card without voltage switch\n"); + self.sdmmc_power_cycle()?; + continue 'sdcard_init; + } + else { + break 'sdcard_init SdmmcError::ECARDINACTIVE; + } + } + } + + let card: Sdcard = self.setup_sdcard_cont()?; + self.mmc_device = Some(MmcDevice::Sdcard(card)); + return Ok(()); + }; + + // Unsupported card + { + // If the result is error, it is either the voltage not being set up correctly, which mean a bug in hardware layer + // or the card is eMMC or legacy sdcard + // For now, we only deal with the situation it is a sdcard + // TODO: Implement setup for eMMC and legacy sdcard(SDSC) here + debug_log!("Driver right now only support SDHC/SDXC card, please check if you are running this driver on SDIO/SDSC/EMMC card!\n"); + Err(SdmmcError::EUNSUPPORTEDCARD) + } } - fn sdmmc_enable_interrupt(&mut self, irq_to_enable: &mut u32) -> Result<(), SdmmcHalError> { - return Err(SdmmcHalError::ENOTIMPLEMENTED); + /// From uboot + /// Most cards do not answer if some reserved bits + /// in the ocr are set. However, Some controller + /// can set bit 7 (reserved for low voltages), but + /// how to manage low voltages SD card is not yet + /// specified. + /// Check mmc_sd_get_cid() in Linux for card init process + fn setup_sdcard_cont(&mut self) -> Result { + let mut resp: [u32; 4] = [0; 4]; + + // Send CMD2 to get the CID register + let mut cmd = SdmmcCmd { + cmdidx: MMC_CMD_ALL_SEND_CID, + resp_type: MMC_RSP_R2, + cmdarg: 0, + }; + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + let cid: Cid = Cid::new(resp); + + let card_id = ((resp[0] as u128) << 96) + | ((resp[1] as u128) << 64) + | ((resp[2] as u128) << 32) + | (resp[3] as u128); + + // TODO: Figure out a better way to do this than adding microkit crate + debug_log!( + "CID: {:08x} {:08x} {:08x} {:08x}\n", + resp[0], + resp[1], + resp[2], + resp[3] + ); + + // Send CMD3 to set and receive the RCA + cmd = SdmmcCmd { + cmdidx: SD_CMD_SEND_RELATIVE_ADDR, + resp_type: MMC_RSP_R6, + cmdarg: 0, + }; + + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + let rca: u16 = (resp[0] >> 16) as u16; // Store RCA from response + + debug_log!("RCA: {:04x}\n", rca); + + // Send CMD9 to get the CSD register + cmd = SdmmcCmd { + cmdidx: MMC_CMD_SEND_CSD, + resp_type: MMC_RSP_R2, + cmdarg: (rca as u32) << 16, + }; + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + debug_log!( + "CSD: {:08x} {:08x} {:08x} {:08x}\n", + resp[0], + resp[1], + resp[2], + resp[3] + ); + + let (csd, card_version) = Csd::new(resp); + + // Send CMD7 to select the card + cmd = SdmmcCmd { + cmdidx: MMC_CMD_SELECT_CARD, + resp_type: MMC_RSP_R1, + cmdarg: (rca as u32) << 16, + }; + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + // SDHC/SDXC default to 512 bytes sector size so I did not manually set it here + + self.mmc_ios.clock = self.hardware.sdmmc_config_timing(MmcTiming::Legacy)?; + + let card_state: MmcState = MmcState { + timing: MmcTiming::Legacy, + bus_width: MmcBusWidth::Width1, + }; + + // Continue working on it next week + Ok(Sdcard { + card_id, + manufacture_info: cid, + card_specific_data: csd, + card_version, + relative_card_addr: rca, + card_state, + card_cap: sdmmc_capability::SdcardCapability(MMC_EMPTY_CAP), + card_config: None, + }) } - fn sdmmc_ack_interrupt(&mut self, irq_enabled: &u32) -> Result<(), SdmmcHalError> { - return Err(SdmmcHalError::ENOTIMPLEMENTED); + /// A function that tune the card speed + /// This function does not do roll back so if this function return an error, reset up the card + /// Do NOT call this function again if your card is already tuned as this function is not that cheap! + /// But you should call this function the card being turned into power saving mode + /// `stolen_memory` is a specific memory region used to read data from the SD card through CMD6. + /// + /// ### Important Considerations: + /// + /// 1. **Memory Region Constraints**: + /// - `stolen_memory` is used as a buffer to hold the 64-byte response from the SD card + /// when executing CMD6. This response contains the function switch status and other + /// function-related information. + /// - The memory region from `stolen_memory` to `stolen_memory + 64 bytes` must not overlap + /// with any other data structures, device registers, or memory-mapped peripherals to + /// avoid conflicts or unintended behavior. + /// + /// 2. **Cache Considerations**: + /// - If the system uses caching, you may need to ensure that cache effects do not interfere + /// with the integrity of the data read from the SD card. + /// - If caching is enabled, consider using cache invalidation before reading and cache flushing + /// after writing data to ensure consistency between the memory and the SD card. + /// For example, you might need to use cache control instructions or APIs specific to your + /// platform to manage this. + /// + /// 3. **Alignment and Access Requirements**: + /// - `stolen_memory` should be aligned to at least 4 bytes (or preferably 8 bytes) to avoid + /// misaligned memory access issues, which could lead to performance penalties or even faults + /// on some architectures. + /// Tunes SD card performance by adjusting data bus width and speed mode. + /// + /// # Parameters + /// - `addr_and_invalidate_cache_fn`: An optional tuple containing: + /// - `*mut [u8; 64]`: A memory address used as a buffer for certain commands (e.g., CMD6). The memory + /// should be suitable for DMA into(e.g. aligned to 8 bytes memory border and not conflict + /// with other structure or device registers). + /// - `fn()`: A function pointer that, when called, invalidates the cache for the range + /// `addr` to `addr + 64 bytes`. This function should ensure cache consistency for + /// that specific memory range. If `None`, no buffer is used, and the tune performance function + /// will not attempt to change the card speed class. The fn should not take any variables and would + /// not be stored. By this way, the protocol layer only has the minimal privilege it required for cache invalidation. + /// + /// # Returns + /// - `Result<(), SdmmcError>`: `Ok(())` if tuning was successful, or an error otherwise. + pub fn tune_performance( + &mut self, + memory_and_invalidate_cache_fn: Option<(&mut [u8; 64], fn())>, + ) -> Result<(), SdmmcError> { + // For testing + let mmc_device = self.mmc_device.as_mut().ok_or(SdmmcError::ENOCARD)?; + + // Turn down the clock frequency + self.mmc_ios.clock = self.hardware.sdmmc_config_timing(MmcTiming::CardSetup)?; + + match mmc_device { + MmcDevice::Sdcard(sdcard) => { + sdcard.card_state.timing = MmcTiming::CardSetup; + self.tune_sdcard_performance(memory_and_invalidate_cache_fn) + } + MmcDevice::EMmc(emmc) => Err(SdmmcError::ENOTIMPLEMENTED), + MmcDevice::Unknown => Err(SdmmcError::ENOTIMPLEMENTED), + } } - fn sdmmc_power_off(&mut self) -> Result<(), SdmmcHalError> { - return Err(SdmmcHalError::ENOTIMPLEMENTED); + pub fn get_host_info(&mut self) -> HostInfo { + self.host_info.clone() } -} -// Not used right now, but would be useful in the future once we want to execute some command synchronously -fn send_cmd_and_receive_resp( - hardware: &mut T, - cmd: &SdmmcCmd, - data: Option<&MmcData>, - resp: &mut [u32; 4] -) -> Result<(), SdmmcHalError> { - // Send the command using the hardware layer - let mut res = hardware.sdmmc_send_command(cmd, data); - if res.is_err() { - return res; + /// Helper function to print out the content of one block + #[allow(dead_code)] + unsafe fn print_one_block(ptr: *const u8, num: usize) { + unsafe { + // Iterate over the number of bytes and print each one in hexadecimal format + for i in 0..num { + let byte = *ptr.add(i); + if i % 16 == 0 { + debug_log!("\n{:04x}: ", i); + } + debug_log!("{:02x} ", byte); + } + debug_log!("\n"); + } } - // TODO: Change it to use the sleep function provided by the hardware layer - let mut retry: u32 = 1000000; - - while retry > 0 { - // Try to receive the response - res = hardware.sdmmc_receive_response(cmd, resp); - - if let Err(SdmmcHalError::EBUSY) = res { - // Busy response, retry - retry -= 1; - // hardware.sleep(1); // Placeholder: Implement a sleep function in SdmmcHardware trait - } else { - // If any other error or success, break the loop - break; + pub fn test_read_one_block(&mut self, start_idx: u64, destination: u64) { + let data: MmcData = MmcData { + blocksize: 512, + blockcnt: 1, + flags: MmcDataFlag::SdmmcDataRead, + addr: destination, + }; + debug_log!("Gonna test read one block!\n"); + let mut resp: [u32; 4] = [0; 4]; + let cmd_arg: u64 = start_idx; + let cmd = SdmmcCmd { + cmdidx: MMC_CMD_READ_SINGLE_BLOCK, + resp_type: MMC_RSP_R1, + cmdarg: cmd_arg as u32, + }; + if let Err(error) = + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, Some(&data), &mut resp) + { + debug_log!("Error in reading\n"); } + unsafe { Self::print_one_block(destination as *mut u8, 512) }; } - res // Return the final result (Ok or Err) -} + fn test_sampling( + &mut self, + memory_addr: u64, + cache_invalidate_function: fn(), + ) -> Result<(), SdmmcError> { + let mut resp: [u32; 4] = [0; 4]; -/// TODO: Add more variables for SdmmcProtocol to track the state of the sdmmc controller and card correctly -pub struct SdmmcProtocol<'a, T: SdmmcHardware> { - pub hardware: &'a mut T, - enabled_irq: u32, -} + let data = MmcData { + blocksize: 64, + blockcnt: 1, + flags: MmcDataFlag::SdmmcDataRead, + addr: memory_addr, + }; -impl Unpin for SdmmcProtocol<'_, T> where T: Unpin + SdmmcHardware {} + let cmd = SdmmcCmd { + cmdidx: SD_CMD_SWITCH_FUNC, + resp_type: MMC_RSP_R1, + cmdarg: 0x00FFFFFF, + }; -impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { - pub fn new(hardware: &'a mut T) -> Self { - SdmmcProtocol { - hardware, - enabled_irq: 0, + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, Some(&data), &mut resp) + // TODO: Add validate the content of data returned here, but it is optional because + // if there is not error it must has passed the CRC check! + } + + fn process_sampling( + &mut self, + memory_addr: u64, + cache_invalidate_function: fn(), + ) -> Result<(), SdmmcError> { + self.hardware + .sdmmc_tune_sampling(TuningState::TuningStart)?; + loop { + // Call test_sampling + match self.test_sampling(memory_addr, cache_invalidate_function) { + Ok(_) => { + self.hardware + .sdmmc_tune_sampling(TuningState::TuningComplete)?; + // If test_sampling succeeds, return Ok + return Ok(()); + } + Err(SdmmcError::EIO) => { + // If test_sampling fails with EIO, try tuning the sampling point + debug_log!("Try different sampling points\n"); + self.hardware + .sdmmc_tune_sampling(TuningState::TuningContinue)?; + } + Err(e) => { + // If test_sampling fails with an error other than EIO, return that error + debug_log!("Tuning sampling fails\n"); + return Err(e); + } + } } } - // Funtion that is not completed - pub fn reset_card(&mut self) -> Result<(), SdmmcHalError> { - let all: u32 = InterruptType::Success as u32 | InterruptType::Error as u32 | InterruptType::SDIO as u32; - todo!(); - self.hardware.sdmmc_ack_interrupt(&all) + /// Switch voltage function + fn tune_sdcard_switch_uhs18v(&mut self) -> Result<(), SdmmcError> { + debug_log!("Entering tune sdcard signal voltage function!\n"); + let mut resp: [u32; 4] = [0; 4]; + let cmd = SdmmcCmd { + cmdidx: SD_CMD_SWITCH_UHS18V, + resp_type: MMC_RSP_R1, + cmdarg: 0, // Argument for 4-bit mode (0 for 1-bit mode) + }; + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + debug_log!("Switch voltage prepared!\n"); + + self.mmc_ios.clock = self.hardware.sdmmc_config_timing(MmcTiming::ClockStop)?; + + // TODO: figuring out the optimal delay + process_wait_unreliable(100000); + + let mut signal: u8 = 0xFF; + + for _ in 0..100 { + signal = self.hardware.sdmmc_read_datalanes()?; + // TODO: figuring out the optimal delay + process_wait_unreliable(100000); + debug_log!("data signal value: 0b{:b}\n", signal); + if signal & 0xF == 0x0 { + break; + } + } + + if signal & 0xF != 0x0 { + return Err(SdmmcError::ECARDINACTIVE); + } + + self.hardware + .sdmmc_set_signal_voltage(MmcSignalVoltage::Voltage180)?; + + // TODO: figuring out the optimal delay + process_wait_unreliable(10_000_000); + + self.mmc_ios.clock = self.hardware.sdmmc_config_timing(MmcTiming::CardSetup)?; + + // TODO: figuring out the optimal delay + process_wait_unreliable(100_000); + + for _ in 0..100 { + signal = self.hardware.sdmmc_read_datalanes()?; + // TODO: figuring out the optimal delay + process_wait_unreliable(100_000); + debug_log!("data signal value: 0b{:b}\n", signal); + if signal & 0xF == 0xF { + break; + } + } + + if signal & 0xF != 0xF { + self.hardware.sdmmc_set_signal_voltage(MmcSignalVoltage::Voltage330)?; + process_wait_unreliable(1_000_000); + return Err(SdmmcError::ECARDINACTIVE); + } + + Ok(()) + } + + /// Right now the speed switch is done hackily + /// The speed switch is hardcoded to switch to UHS SDR104 + /// If the card does not support UHS SDR104, the switch will fail! + /// Implement this sdcard switch function to avoid this hackiness! + /// Like first get the speed classes the sdcard support by this function + /// and then switch to the proper speed class! + fn sdcard_switch_speed( + &mut self, + target: MmcTiming, + memory: &mut [u8; 64], + invalidate_cache_fn: fn(), + ) -> Result<(), SdmmcError> { + let data: MmcData = MmcData { + blocksize: 64, + blockcnt: 1, + flags: MmcDataFlag::SdmmcDataRead, + addr: memory.as_ptr() as u64, + }; + + let mut resp: [u32; 4] = [0; 4]; + + // As this function only change speed class, + let mut cmdarg: u32 = 0x80FFFFF0; + match target { + MmcTiming::Legacy => cmdarg |= SD_SWITCH_FUNCTION_GROUP_ONE_SET_LEGACY as u32, + MmcTiming::SdHs => cmdarg |= SD_SWITCH_FUNCTION_GROUP_ONE_SET_SDHS as u32, + MmcTiming::UhsSdr12 => cmdarg |= SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR12 as u32, + MmcTiming::UhsSdr25 => cmdarg |= SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR25 as u32, + MmcTiming::UhsSdr50 => cmdarg |= SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR50 as u32, + MmcTiming::UhsSdr104 => cmdarg |= SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR104 as u32, + MmcTiming::UhsDdr50 => cmdarg |= SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_DDR50 as u32, + _ => return Err(SdmmcError::EUNDEFINED), + } + let cmd = SdmmcCmd { + cmdidx: SD_CMD_SWITCH_FUNC, + resp_type: MMC_RSP_R1, + cmdarg, + }; + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, Some(&data), &mut resp)?; + // Error handling here + invalidate_cache_fn(); + // Since we are using u8 here, the endianness does matter + // 0xF indicate function switch error + // TODO: Double check here and deliberately trigger swithc error to see if the field + // is being set to 0xF + if memory[SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE] & 0xF == 0xF { + return Err(SdmmcError::EINVAL); + } + Ok(()) + } + + fn sdcard_check_supported_speed_class( + &mut self, + memory: &mut [u8; 64], + invalidate_cache_fn: fn(), + ) -> Result<(), SdmmcError> { + let data: MmcData = MmcData { + blocksize: 64, + blockcnt: 1, + flags: MmcDataFlag::SdmmcDataRead, + addr: memory.as_ptr() as u64, + }; + + let mut resp: [u32; 4] = [0; 4]; + + let mut card_cap: SdcardCapability = sdmmc_capability::SdcardCapability(MMC_EMPTY_CAP); + let cmd = SdmmcCmd { + cmdidx: SD_CMD_SWITCH_FUNC, + resp_type: MMC_RSP_R1, + cmdarg: 0x00FFFFFF, + }; + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, Some(&data), &mut resp)?; + invalidate_cache_fn(); + // Typical speed class supported: 80 03 + match self.mmc_ios.signal_voltage { + MmcSignalVoltage::Voltage330 => { + let speed_class_byte: u8 = memory[SD_SWITCH_FUNCTION_GROUP_ONE]; + if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_SDHS != 0 { + card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_SD_HS)); + } + } + MmcSignalVoltage::Voltage180 => { + let speed_class_byte: u8 = memory[SD_SWITCH_FUNCTION_GROUP_ONE]; + if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR12 != 0 { + card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_SDR12)); + } + if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR25 != 0 { + card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_SDR25)); + } + if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR50 != 0 { + card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_SDR50)); + } + if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104 != 0 { + card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_SDR104)); + } + if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50 != 0 { + card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_DDR50)); + } + } + // For sdcard, the signal voltage cannot be 1.2V + MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EUNDEFINED), + } + // Add the card cap to self + if let Some(MmcDevice::Sdcard(ref mut sdcard)) = &mut self.mmc_device { + sdcard.card_cap |= card_cap; + } else { + return Err(SdmmcError::EINVAL); + } + Ok(()) + } + + /// Since we are using u8 here, the endianness does matter + fn tune_sdcard_performance( + &mut self, + memory_and_invalidate_cache_fn: Option<(&mut [u8; 64], fn())>, + ) -> Result<(), SdmmcError> { + let mut resp: [u32; 4] = [0; 4]; + + if self.mmc_ios.bus_width == MmcBusWidth::Width1 + && self + .host_capability + .contains(SdmmcHostCapability(MMC_CAP_4_BIT_DATA)) + { + // Switch data bits per transfer + let relative_card_address: u16; + if let Some(MmcDevice::Sdcard(ref sdcard)) = self.mmc_device { + relative_card_address = sdcard.relative_card_addr; + } else { + return Err(SdmmcError::EINVAL); + } + // CMD55 + ACMD6 to set the card to 4-bit mode (if supported by host and card) + let cmd = SdmmcCmd { + cmdidx: MMC_CMD_APP_CMD, + resp_type: MMC_RSP_R1, + cmdarg: (relative_card_address as u32) << 16, + }; + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + let cmd = SdmmcCmd { + cmdidx: SD_CMD_APP_SET_BUS_WIDTH, + resp_type: MMC_RSP_R1, + cmdarg: 2, // Argument for 4-bit mode (0 for 1-bit mode) + }; + Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + self.hardware.sdmmc_config_bus_width(MmcBusWidth::Width4)?; + + debug_log!("Tuning datalanes succeed!\n"); + + // If any of the cmd above fail, the card should be completely reinit + self.mmc_ios.bus_width = MmcBusWidth::Width4; + // TODO: Change sdcard bus width here, or get rid of that field completely + } + + if let Some((memory, cache_invalidate_function)) = memory_and_invalidate_cache_fn { + debug_log!("Checking supported speed classes\n"); + if let Some(MmcDevice::Sdcard(ref mut sdcard)) = self.mmc_device { + match self.mmc_ios.signal_voltage { + MmcSignalVoltage::Voltage330 => { + sdcard.card_state.timing = MmcTiming::Legacy; + if !sdcard + .card_cap + .contains(SdcardCapability(MMC_TIMING_LEGACY)) + { + // When the target speed is None, the sdcard_switch_speed update sdcard speed capability + self.sdcard_check_supported_speed_class( + memory, + cache_invalidate_function, + )?; + } + } + MmcSignalVoltage::Voltage180 => { + sdcard.card_state.timing = MmcTiming::UhsSdr12; + if !sdcard + .card_cap + .contains(SdcardCapability(MMC_TIMING_UHS_SDR12)) + { + self.sdcard_check_supported_speed_class( + memory, + cache_invalidate_function, + )?; + } + } + MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EUNDEFINED), + }; + } else { + return Err(SdmmcError::EUNDEFINED); + } + + let mut timing; + let sdcard_cap: SdcardCapability; + if let Some(MmcDevice::Sdcard(ref sdcard)) = self.mmc_device { + debug_log!("Switch to higher speed class\n"); + sdcard_cap = sdcard.card_cap.clone(); + timing = sdcard.card_state.timing; + // This 'tune_speed thing is a feature called labeled block in Rust + 'tune_speed: { + match self.mmc_ios.signal_voltage { + MmcSignalVoltage::Voltage330 => { + if sdcard_cap.contains(SdcardCapability(MMC_TIMING_SD_HS)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_SD_HS)) + { + // When the target speed is Some, the sdcard_switch_speed try to switch the speed class to target + self.sdcard_switch_speed( + MmcTiming::SdHs, + memory, + cache_invalidate_function, + )?; + timing = MmcTiming::SdHs; + } + } + MmcSignalVoltage::Voltage180 => { + if sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR104)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR104)) + { + self.sdcard_switch_speed( + MmcTiming::UhsSdr104, + memory, + cache_invalidate_function, + )?; + timing = MmcTiming::UhsSdr104; + // If the switch speed succeed, terminate the block + break 'tune_speed; + } + if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_DDR50)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_UHS_DDR50)) + { + self.sdcard_switch_speed( + MmcTiming::UhsDdr50, + memory, + cache_invalidate_function, + )?; + timing = MmcTiming::UhsDdr50; + break 'tune_speed; + } + if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR50)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR50)) + { + self.sdcard_switch_speed( + MmcTiming::UhsSdr50, + memory, + cache_invalidate_function, + )?; + timing = MmcTiming::UhsSdr50; + break 'tune_speed; + } + if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR25)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR25)) + { + self.sdcard_switch_speed( + MmcTiming::UhsSdr25, + memory, + cache_invalidate_function, + )?; + timing = MmcTiming::UhsSdr25; + break 'tune_speed; + } + } + MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EUNDEFINED), + } + }; + self.mmc_ios.clock = self.hardware.sdmmc_config_timing(timing)?; + self.process_sampling(memory.as_ptr() as u64, cache_invalidate_function)?; + + debug_log!("Current frequency: {}Hz", self.mmc_ios.clock); + } else { + return Err(SdmmcError::EUNDEFINED); + } + if let Some(MmcDevice::Sdcard(ref mut sdcard)) = self.mmc_device { + sdcard.card_state.timing = timing; + } + } + + Ok(()) } - pub fn enable_interrupt(&mut self, irq_to_enable: &mut u32) -> Result<(), SdmmcHalError> { - let res = self.hardware.sdmmc_enable_interrupt(irq_to_enable); - self.enabled_irq = *irq_to_enable; - res + pub fn config_interrupt(&mut self, enable_irq: bool, enable_sdio_irq: bool) -> Result<(), SdmmcError> { + self.mmc_ios.enabled_irq = enable_irq | enable_sdio_irq; + self.hardware.sdmmc_config_interrupt(enable_irq, enable_sdio_irq) } - pub fn ack_interrupt(&mut self) -> Result<(), SdmmcHalError> { - self.hardware.sdmmc_ack_interrupt(&self.enabled_irq) + pub fn ack_interrupt(&mut self) -> Result<(), SdmmcError> { + self.hardware.sdmmc_ack_interrupt() } - pub async fn read_block(self, blockcnt: u32, start_idx: u64, destination: u64) -> (Result<(), SdmmcHalError>, Option>) { + pub async fn read_block( + mut self, + blockcnt: u32, + start_idx: u64, + destination: u64, + ) -> (Result<(), SdmmcError>, SdmmcProtocol) { let mut cmd: SdmmcCmd; - let mut res: Result<(), SdmmcHalError>; + let mut res: Result<(), SdmmcError>; // TODO: Figure out a way to support cards with 4 KB sector size let data: MmcData = MmcData { blocksize: 512, @@ -192,28 +1110,28 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { resp_type: MMC_RSP_R1, cmdarg: cmd_arg as u32, }; - let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); + let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, Some(&data), &mut resp); res = future.await; // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + let _ = self.hardware.sdmmc_ack_interrupt(); - return (res, Some(self)); - } - else { + return (res, self); + } else { cmd = SdmmcCmd { cmdidx: MMC_CMD_READ_MULTIPLE_BLOCK, resp_type: MMC_RSP_R1, cmdarg: cmd_arg as u32, }; - let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); + let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, Some(&data), &mut resp); res = future.await; // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); - + // Should I use if statement to check whether to ack irq or not based on if irq is enabled or not? + let _ = self.hardware.sdmmc_ack_interrupt(); + if let Ok(()) = res { // Uboot code for determine response type in this case // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; @@ -223,16 +1141,16 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { resp_type: MMC_RSP_R1B, cmdarg: 0, }; - let future = SdmmcCmdFuture::new(self.hardware, &cmd, None, &mut resp); + let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); res = future.await; // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + let _ = self.hardware.sdmmc_ack_interrupt(); - return (res.map_err(|_| SdmmcHalError::ESTOPCMD), Some(self)); + return (res.map_err(|_| SdmmcError::ESTOPCMD), self); } else { - return (res, Some(self)); + return (res, self); } } } @@ -240,9 +1158,14 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { // Almost the same with read_block aside from the cmd being sent is a bit different // For any future code add to read_block/write_block, remember to change both // Should read_block/write_block be the same function? - pub async fn write_block(self, blockcnt: u32, start_idx: u64, source: u64) -> (Result<(), SdmmcHalError>, Option>) { + pub async fn write_block( + mut self, + blockcnt: u32, + start_idx: u64, + source: u64, + ) -> (Result<(), SdmmcError>, SdmmcProtocol) { let mut cmd: SdmmcCmd; - let mut res: Result<(), SdmmcHalError>; + let mut res: Result<(), SdmmcError>; // TODO: Figure out a way to support cards with 4 KB sector size let data: MmcData = MmcData { blocksize: 512, @@ -260,49 +1183,150 @@ impl<'a, T: SdmmcHardware> SdmmcProtocol<'a, T> { resp_type: MMC_RSP_R1, cmdarg: cmd_arg as u32, }; - let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); + let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, Some(&data), &mut resp); res = future.await; // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + let _ = self.hardware.sdmmc_ack_interrupt(); - return (res, Some(self)); - } - else { + return (res, self); + } else { + // TODO: Add if here to determine if the card support cmd23 or not to determine to use cmd23 or cmd12 + // Set the expected number of blocks cmd = SdmmcCmd { - cmdidx: MMC_CMD_WRITE_MULTIPLE_BLOCK, + cmdidx: MMC_CMD_SET_BLOCK_COUNT, resp_type: MMC_RSP_R1, - cmdarg: cmd_arg as u32, + cmdarg: blockcnt, // block count for the upcoming CMD25 operation }; - let future = SdmmcCmdFuture::new(self.hardware, &cmd, Some(&data), &mut resp); + + let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); res = future.await; // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + let _ = self.hardware.sdmmc_ack_interrupt(); + if let Ok(()) = res { // Uboot code for determine response type in this case // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; // TODO: Add mmc checks here cmd = SdmmcCmd { - cmdidx: MMC_CMD_STOP_TRANSMISSION, - resp_type: MMC_RSP_R1B, - cmdarg: 0, + cmdidx: MMC_CMD_WRITE_MULTIPLE_BLOCK, + resp_type: MMC_RSP_R1, + cmdarg: cmd_arg as u32, }; - let future = SdmmcCmdFuture::new(self.hardware, &cmd, None, &mut resp); + let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, Some(&data), &mut resp); res = future.await; - + // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(&self.enabled_irq); + let _ = self.hardware.sdmmc_ack_interrupt(); + + return (res.map_err(|_| SdmmcError::ESTOPCMD), self); + } else { + return (res, self); + } + } + } + + // TODO: correct erase block alignment, error handling and sdcard internal erasure timing(this erasure seems to be non-blocking?) + // It is likely(from reading Linux source code), polling in some host controller could be necessary + pub async fn erase_block( + mut self, + start_idx: u64, + end_idx: u64, + ) -> (Result<(), SdmmcError>, SdmmcProtocol) { + let mut cmd: SdmmcCmd; + let mut res: Result<(), SdmmcError>; + + let mut resp: [u32; 4] = [0; 4]; + + cmd = SdmmcCmd { + cmdidx: SD_CMD_ERASE_WR_BLK_START, + resp_type: MMC_RSP_R1, + cmdarg: start_idx as u32, + }; + + let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); + + res = future.await; + + if let Err(_) = res { + return (res, self); + } + + cmd = SdmmcCmd { + cmdidx: SD_CMD_ERASE_WR_BLK_END, + resp_type: MMC_RSP_R1, + cmdarg: end_idx as u32, + }; + + let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); + + res = future.await; + + if let Err(_) = res { + return (res, self); + } - return (res.map_err(|_| SdmmcHalError::ESTOPCMD), Some(self)); + cmd = SdmmcCmd { + cmdidx: MMC_CMD_ERASE, + resp_type: MMC_RSP_R1B, + cmdarg: SD_ERASE_ARG, + }; + + let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); + + res = future.await; + + return (res, self) + } + + // Not used right now, but would be useful in the future once we want to execute some command synchronously + fn send_cmd_and_receive_resp( + hardware: &mut T, + cmd: &SdmmcCmd, + data: Option<&MmcData>, + resp: &mut [u32; 4], + ) -> Result<(), SdmmcError> { + // TODO: Add temporarily disable interrupt here + + // Send the command using the hardware layer + let mut res = hardware.sdmmc_send_command(cmd, data); + if res.is_err() { + return res; + } + + res = Err(SdmmcError::ETIMEDOUT); + + // TODO: Change it to use the sleep function provided by the hardware layer + // This is a busy poll retry, we could poll infinitely if we trust the device to be correct + let mut retry: u32 = 100_000_000; + + // sel4_microkit::debug_println!("Request sent! Let us wait!"); + + while retry > 0 { + // Try to receive the response + res = hardware.sdmmc_receive_response(cmd, resp); + + if let Err(SdmmcError::EBUSY) = res { + // Busy response, retry + retry -= 1; + // hardware.sleep(1); // Placeholder: Implement a sleep function in SdmmcHardware trait } else { - return (res, Some(self)); + // If any other error or success, break the loop + break; } } + + if let Err(_) = res { + debug_log!("Resp[0] value {:08x}\n", resp[0]); + } + + // TODO: Add renable interrupt here + res // Return the final result (Ok or Err) } } @@ -332,7 +1356,8 @@ impl<'a, 'b, 'c> SdmmcCmdFuture<'a, 'b, 'c> { hardware: &'a mut dyn SdmmcHardware, cmd: &'b SdmmcCmd, data: Option<&'b MmcData>, - response: &'c mut [u32; 4]) -> SdmmcCmdFuture<'a, 'b, 'c> { + response: &'c mut [u32; 4], + ) -> SdmmcCmdFuture<'a, 'b, 'c> { SdmmcCmdFuture { hardware, cmd, @@ -351,9 +1376,9 @@ impl<'a, 'b, 'c> SdmmcCmdFuture<'a, 'b, 'c> { /// So for now, the context is being stored in waker but this waker will not be used /// Beside, we are in no std environment and can barely use any locks impl<'a, 'b, 'c> Future for SdmmcCmdFuture<'a, 'b, 'c> { - type Output = Result<(), SdmmcHalError>; + type Output = Result<(), SdmmcError>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // As I have said above, this waker is not being used so it do not need to be shared data // But store the waker provided anyway // Beware you need to update waker every polling, for more details about why @@ -362,7 +1387,7 @@ impl<'a, 'b, 'c> Future for SdmmcCmdFuture<'a, 'b, 'c> { match self.state { CmdState::NotSent => { - let res: Result<(), SdmmcHalError>; + let res: Result<(), SdmmcError>; { let this: &mut SdmmcCmdFuture<'a, 'b, 'c> = self.as_mut().get_mut(); @@ -385,10 +1410,10 @@ impl<'a, 'b, 'c> Future for SdmmcCmdFuture<'a, 'b, 'c> { let this: &mut SdmmcCmdFuture<'a, 'b, 'c> = self.as_mut().get_mut(); let cmd: &SdmmcCmd = this.cmd; let response: &mut [u32; 4] = this.response; - let hardware: &mut dyn SdmmcHardware = this.hardware; + let hardware: &dyn SdmmcHardware = this.hardware; res = hardware.sdmmc_receive_response(cmd, response); } - if let Err(SdmmcHalError::EBUSY) = res { + if let Err(SdmmcError::EBUSY) = res { return Poll::Pending; } else if let Ok(()) = res { self.state = CmdState::Finished; @@ -398,8 +1423,8 @@ impl<'a, 'b, 'c> Future for SdmmcCmdFuture<'a, 'b, 'c> { return Poll::Ready(res); } } - CmdState::Error => return Poll::Ready(Err(SdmmcHalError::EUNDEFINED)), - CmdState::Finished => return Poll::Ready(Err(SdmmcHalError::EUNDEFINED)), + CmdState::Error => return Poll::Ready(Err(SdmmcError::EUNDEFINED)), + CmdState::Finished => return Poll::Ready(Err(SdmmcError::EUNDEFINED)), } } -} \ No newline at end of file +} diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs new file mode 100644 index 000000000..71398d13c --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs @@ -0,0 +1,86 @@ +use super::sdcard::{EMmc, Sdcard}; + +// Enums for bus_width +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum MmcBusWidth { + Width1 = 0, + // One is skipped because for SD_ACMD_SET_BUS_WIDTH, setting cmdargs to 2 indicate 4 datalanes + Width4 = 2, + Width8 = 3, +} + +#[derive(Debug)] +pub enum TuningState { + TuningStart = 0, + TuningContinue = 1, + TuningComplete = 2, +} + +// Timing modes (could be an enum or use the bitflags constants defined earlier) +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum MmcTiming { + Legacy = 0, + MmcHs = 1, + SdHs = 2, + UhsSdr12 = 3, + UhsSdr25 = 4, + UhsSdr50 = 5, + UhsSdr104 = 6, + UhsDdr50 = 7, + MmcDdr52 = 8, + MmcHs200 = 9, + MmcHs400 = 10, + SdExp = 11, + SdExp12V = 12, + CardSetup = 13, // Additional frequency for card setup + CardSleep = 14, + ClockStop = 15, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct MmcState { + /// The timing specification that dictates how data is transferred between the host + /// and the card. + /// + /// - The timing mode defines the protocol and speed class for communication, such as + /// legacy modes, high-speed modes, or ultra-high-speed modes. + /// - Examples include: + /// - `Timing::Legacy`: Legacy slower transfer mode. + /// - `Timing::SdHs`: SD high-speed mode. + /// - `Timing::MmcHs200`: eMMC HS200 mode for high-speed data transfers. + pub(crate) timing: MmcTiming, + + /// The width of the data bus used for communication between the host and the card. + /// + /// - This field specifies whether the bus operates in 1-bit, 4-bit, or 8-bit mode. + /// - Wider bus widths (4-bit, 8-bit) enable higher data transfer rates, but not all + /// cards or host controllers support every bus width. + /// - Common values: + /// - `BusWidth::Width1`: 1-bit data width (lowest speed, used during initialization). + /// - `BusWidth::Width4`: 4-bit data width (common for SD cards). + /// - `BusWidth::Width8`: 8-bit data width (mainly for eMMC). + pub(crate) bus_width: MmcBusWidth, +} + +/// Some of the MmcDevice is reserved for future use +pub(crate) enum MmcDevice { + Sdcard(Sdcard), + EMmc(EMmc), + Unknown, + // TODO, when we decide to support emmc/sdio, modify this struct +} + +/// Represents the different states of an SD or eMMC card. +#[derive(Debug, PartialEq)] +enum CardState { + Idle, + Ready, + Identification, + Standby, + Transfer, + SendingData, + ReceiveData, + Programming, + Disconnect, + Unknown, +} diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs new file mode 100644 index 000000000..aacaca250 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs @@ -0,0 +1,281 @@ +use core::fmt::{self, Write}; + +use super::{ + mmc_struct::{self, MmcState}, + sdmmc_capability::SdcardCapability, + SdmmcHardware, SdmmcProtocol, +}; + +pub(crate) struct Sdcard { + pub card_id: u128, + pub manufacture_info: Cid, + pub card_specific_data: Csd, + pub card_version: SdVersion, + pub relative_card_addr: u16, + pub card_state: MmcState, + pub card_cap: SdcardCapability, + pub card_config: Option, +} + +/// Placeholder eMMC struct that is not implemented +pub struct EMmc { + pub card_id: u128, +} + +// Beware this struct is meant to track the cmd set that the sdcard should support +// For example, if the SdVersion is set to V3_0, it does not mean the card version is 3.0 +// But mean that the sdcard support cmd at least up to specification 3.0 +// The SD card specification is cumulative, meaning that if an SD card reports support for a +// particular version (say 4.0), it implicitly supports all earlier versions as well. +#[derive(Debug, PartialEq, Eq)] +pub enum SdVersion { + V1_0 = 1, + V2_0 = 2, + V3_0 = 3, + V4_0 = 4, +} + +pub struct Cid { + manufacturer_id: u8, + oem_id: u16, + product_name: [u8; 5], + product_revision: u8, + serial_number: u32, + manufacturing_date: (u32, u8), // (year, month) +} + +impl Cid { + pub fn new(cid: [u32; 4]) -> Cid { + // Combine the 4 u32 words into a single 128-bit CID value for easy bit manipulation + let cid_combined = ((cid[0] as u128) << 96) + | ((cid[1] as u128) << 64) + | ((cid[2] as u128) << 32) + | (cid[3] as u128); + + // Extract each field based on the CID structure + let manufacturer_id = ((cid_combined >> 120) & 0xFF) as u8; + let oem_id = ((cid_combined >> 104) & 0xFFFF) as u16; + + // Extract product name, which is 5 bytes (40 bits) + let product_name = [ + ((cid_combined >> 96) & 0xFF) as u8, + ((cid_combined >> 88) & 0xFF) as u8, + ((cid_combined >> 80) & 0xFF) as u8, + ((cid_combined >> 72) & 0xFF) as u8, + ((cid_combined >> 64) & 0xFF) as u8, + ]; + + let product_revision = ((cid_combined >> 56) & 0xFF) as u8; + let serial_number = ((cid_combined >> 24) & 0xFFFFFFFF) as u32; + + // Extract year and month from the manufacturing date + let year = ((cid_combined >> 12) & 0x0F) as u32 + 2000; + let month = ((cid_combined >> 8) & 0x0F) as u8; + + Cid { + manufacturer_id, + oem_id, + product_name, + product_revision, + serial_number, + manufacturing_date: (year, month), + } + } +} + +trait ToArray { + fn to_array(&self) -> [u8; 128]; // A fixed buffer size that you can change as needed +} + +impl ToArray for Cid { + fn to_array(&self) -> [u8; 128] { + let mut buf = [0u8; 128]; + let mut writer = ArrayWriter::new(&mut buf); + + write!( + writer, + "Manufacturer ID: {}\nOEM ID: {}\nProduct Name: {}\nProduct Revision: {}\n\ + Serial Number: {}\nManufacturing Date: {}-{}\n", + self.manufacturer_id, + self.oem_id, + core::str::from_utf8(&self.product_name).unwrap_or("?????"), + self.product_revision, + self.serial_number, + self.manufacturing_date.0, + self.manufacturing_date.1, + ) + .ok(); + + buf + } +} + +// This struct is super unreliable, I am thinking +pub struct Csd { + csd_structure: u8, + card_capacity: u64, + max_read_block_len: u16, + max_write_block_len: u16, + erase_sector_size: u32, + supports_partial_write: bool, +} + +impl Csd { + pub fn new(csd: [u32; 4]) -> (Csd, SdVersion) { + // Combine the four 32-bit words into a single 128-bit value for easier bit manipulation + let csd_combined = ((csd[0] as u128) << 96) + | ((csd[1] as u128) << 64) + | ((csd[2] as u128) << 32) + | (csd[3] as u128); + + // Extract the CSD structure version + let csd_structure = ((csd_combined >> 126) & 0x3) as u8; // Bits 126–127 + let sd_version = match csd_structure { + 0 => SdVersion::V1_0, // CSD Version 1.0 + 1 => SdVersion::V2_0, // CSD Version 2.0 + _ => panic!("Unsupported CSD structure version"), // CSD structures beyond 2.0 are not supported here + // Actually SDUC card are using CSD 3.0 so maybe add something here later. + }; + + // Parse fields based on CSD version + let (card_capacity, erase_sector_size) = match sd_version { + SdVersion::V1_0 => { + // CSD Version 1.0 capacity calculation + let c_size = ((csd_combined >> 62) & 0xFFF) as u64; // Bits 62–73 + let c_size_mult = ((csd_combined >> 47) & 0x7) as u64; // Bits 47–49 + let read_bl_len = ((csd_combined >> 80) & 0xF) as u64; // Bits 80–83 + let card_capacity = (c_size + 1) * (1 << (c_size_mult + 2)) * (1 << read_bl_len); + + // Erase sector size is calculated differently in CSD Version 1.0 + let sector_size = ((csd_combined >> 39) & 0x7F) as u32 + 1; // Bits 39–45 + (card_capacity, sector_size) + } + SdVersion::V2_0 => { + // CSD Version 2.0 capacity calculation for SDHC/SDXC + let c_size = ((csd_combined >> 48) & 0x3FFFF) as u64; // Bits 48–69 + let card_capacity = (c_size + 1) * 512 * 1024; // Capacity formula for SDHC/SDXC + + // Erase sector size calculation for CSD Version 2.0 + let sector_size = ((csd_combined >> 39) & 0x7F + 1) as u32 * 512; // Bits 39–45 + + (card_capacity, sector_size) + } + SdVersion::V3_0 => unreachable!(), + SdVersion::V4_0 => unreachable!(), + }; + + // Block lengths (same for both versions) + let read_bl_len = ((csd_combined >> 80) & 0xF) as u16; // Bits 80–83 + let max_read_block_len = 1 << read_bl_len; + + let write_bl_len = ((csd_combined >> 22) & 0xF) as u16; // Bits 22–25 + let max_write_block_len = 1 << write_bl_len; + + // Partial write support (same for both versions) + let supports_partial_write = ((csd_combined >> 21) & 0x1) != 0; // Bit 21 + + // Return the constructed CSD struct along with the SD version + ( + Csd { + csd_structure, + card_capacity, + max_read_block_len, + max_write_block_len, + erase_sector_size, + supports_partial_write, + }, + sd_version, + ) + } +} + +pub struct Scr { + sd_spec: u8, + data_stat_after_erase: bool, + sd_security: u8, + sd_bus_widths: u8, + sd_spec3: bool, + sd_spec4: bool, + supports_cmd23: bool, +} + +struct ArrayWriter<'a> { + buf: &'a mut [u8], + pos: usize, +} + +impl<'a> ArrayWriter<'a> { + fn new(buf: &'a mut [u8]) -> Self { + Self { buf, pos: 0 } + } +} + +impl<'a> Write for ArrayWriter<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { + let bytes = s.as_bytes(); + let len = bytes.len(); + + if self.pos + len > self.buf.len() { + return Err(fmt::Error); + } + + self.buf[self.pos..self.pos + len].copy_from_slice(bytes); + + self.pos += len; + Ok(()) + } +} + +/* +/// Parses the R1 response from an SD card command. +/// The function takes a 32-bit unsigned integer representing the R1 response +/// and returns a structured report of the status. +/// +/// # Arguments +/// * `response` - The 32-bit R1 response from the SD card command. +/// +/// # Returns +/// A result with either Ok (no errors) or an error string describing the problem. +fn parse_r1_response(response: u32) -> Result { + // Check each status bit and gather relevant info. + let out_of_range = (response >> 31) & 1 != 0; + let address_error = (response >> 30) & 1 != 0; + let block_len_error = (response >> 29) & 1 != 0; + let erase_seq_error = (response >> 28) & 1 != 0; + let ready_for_data = (response >> 8) & 1 != 0; + let idle_state = (response >> 0) & 1 != 0; + + // Extract card state (bits 9-12). + let card_state = (response >> 9) & 0xF; + + // Collect error messages for any issues found. + let mut errors = Vec::new(); + if out_of_range { errors.push("Out of range error"); } + if address_error { errors.push("Address error"); } + if block_len_error { errors.push("Block length error"); } + if erase_seq_error { errors.push("Erase sequence error"); } + + // Parse and interpret card state + let state_description = match card_state { + 0 => "Idle", + 1 => "Ready", + 2 => "Identification", + 3 => "Standby", + 4 => "Transfer", + 5 => "Sending Data", + 6 => "Receive Data", + 7 => "Programming", + 8 => "Disconnect", + _ => "Unknown", + }; + + // Report ready status or errors. + if !errors.is_empty() { + Err(format!("R1 response contains errors: {}", errors.join(", "))) + } else if !ready_for_data { + Err("Card is not ready for data.".to_string()) + } else { + Ok(format!("R1 response is valid. Card state: {}", state_description)) + } +} +*/ diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs new file mode 100644 index 000000000..e8a3d29e0 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs @@ -0,0 +1,114 @@ +use bitflags::bitflags; + +// Linux protocol layer use two u32 to represent all capabilities +// In this Rust based protocol layer we use u128. +// I have thought about whether I should seperate the SdmmcHostCapability to two structs, one for sdcard, one for eMMC +// But give up this idea because I do not know if there are such host that support both sdcard and eMMC +pub(crate) struct SdmmcHostCapability(pub u128); + +bitflags! { + /// Represents the host capabilities for SD/MMC controllers + impl SdmmcHostCapability: u128 { + // Timing modes + const MMC_TIMING_LEGACY = MMC_TIMING_LEGACY; + const MMC_TIMING_MMC_HS = MMC_TIMING_MMC_HS; + const MMC_TIMING_SD_HS = MMC_TIMING_SD_HS; + const MMC_TIMING_UHS_SDR12 = MMC_TIMING_UHS_SDR12; + const MMC_TIMING_UHS_SDR25 = MMC_TIMING_UHS_SDR25; + const MMC_TIMING_UHS_SDR50 = MMC_TIMING_UHS_SDR50; + const MMC_TIMING_UHS_SDR104 = MMC_TIMING_UHS_SDR104; + const MMC_TIMING_UHS_DDR50 = MMC_TIMING_UHS_DDR50; + const MMC_TIMING_MMC_DDR52 = MMC_TIMING_MMC_DDR52; + const MMC_TIMING_MMC_HS200 = MMC_TIMING_MMC_HS200; + const MMC_TIMING_MMC_HS400 = MMC_TIMING_MMC_HS400; + const MMC_TIMING_SD_EXP = MMC_TIMING_SD_EXP; + const MMC_TIMING_SD_EXP_1_2V = MMC_TIMING_SD_EXP_1_2V; + + // Capabilities + const MMC_CAP_4_BIT_DATA = MMC_CAP_4_BIT_DATA; + const MMC_CAP_8_BIT_DATA = MMC_CAP_8_BIT_DATA; + const MMC_CAP_BUS_WIDTH_TEST = MMC_CAP_BUS_WIDTH_TEST; + + const MMC_CAP_VOLTAGE_TUNE = MMC_CAP_VOLTAGE_TUNE; + const MMC_CAP_CMD23 = MMC_CAP_CMD23; + const MMC_CAP_AUTO_STOP = MMC_CAP_AUTO_STOP; + } +} +#[derive(Debug, Clone)] +pub(crate) struct SdcardCapability(pub u128); + +bitflags! { + /// Represents the host capabilities for SD/MMC controllers + impl SdcardCapability: u128 { + // Timing modes + const MMC_TIMING_LEGACY = MMC_TIMING_LEGACY; + const MMC_TIMING_MMC_HS = MMC_TIMING_MMC_HS; + const MMC_TIMING_SD_HS = MMC_TIMING_SD_HS; + const MMC_TIMING_UHS_SDR12 = MMC_TIMING_UHS_SDR12; + const MMC_TIMING_UHS_SDR25 = MMC_TIMING_UHS_SDR25; + const MMC_TIMING_UHS_SDR50 = MMC_TIMING_UHS_SDR50; + const MMC_TIMING_UHS_SDR104 = MMC_TIMING_UHS_SDR104; + const MMC_TIMING_UHS_DDR50 = MMC_TIMING_UHS_DDR50; + const MMC_TIMING_SD_EXP = MMC_TIMING_SD_EXP; + const MMC_TIMING_SD_EXP_1_2V = MMC_TIMING_SD_EXP_1_2V; + + // Capabilities + const MMC_CAP_4_BIT_DATA = MMC_CAP_4_BIT_DATA; + + const MMC_CAP_CMD23 = MMC_CAP_CMD23; + const MMC_CAP_AUTO_STOP = MMC_CAP_AUTO_STOP; + } +} + +pub const MMC_EMPTY_CAP: u128 = 0; + +// Timing modes (starting from bit 0) +pub const MMC_TIMING_LEGACY: u128 = 1 << 0; +pub const MMC_TIMING_MMC_HS: u128 = 1 << 1; +pub const MMC_TIMING_SD_HS: u128 = 1 << 2; +pub const MMC_TIMING_UHS_SDR12: u128 = 1 << 3; +pub const MMC_TIMING_UHS_SDR25: u128 = 1 << 4; +pub const MMC_TIMING_UHS_SDR50: u128 = 1 << 5; +pub const MMC_TIMING_UHS_DDR50: u128 = 1 << 6; +pub const MMC_TIMING_UHS_SDR104: u128 = 1 << 7; +pub const MMC_TIMING_MMC_DDR52: u128 = 1 << 8; +pub const MMC_TIMING_MMC_HS200: u128 = 1 << 9; +pub const MMC_TIMING_MMC_HS400: u128 = 1 << 10; + +pub const MMC_TIMING_UHS: u128 = MMC_TIMING_UHS_SDR12 + | MMC_TIMING_UHS_SDR25 + | MMC_TIMING_UHS_SDR50 + | MMC_TIMING_UHS_SDR104 + | MMC_TIMING_UHS_DDR50; + +pub const MMC_TIMING_SD_EXP: u128 = 1 << 11; +pub const MMC_TIMING_SD_EXP_1_2V: u128 = 1 << 12; + +// Capabilities +pub const MMC_CAP_4_BIT_DATA: u128 = 1 << 16; +pub const MMC_CAP_8_BIT_DATA: u128 = 1 << 17; + +pub const MMC_CAP_BUS_WIDTH_TEST: u128 = 1 << 18; + +pub const MMC_CAP_VOLTAGE_TUNE: u128 = 1 << 29; +pub const MMC_CAP_CMD23: u128 = 1 << 30; +pub const MMC_CAP_AUTO_STOP: u128 = 1 << 31; + +// Host VDD voltage levels for MMC/SD card +pub const MMC_VDD_165_195: u32 = 0x0000_0080; // VDD voltage 1.65 - 1.95V +pub const MMC_VDD_20_21: u32 = 0x0000_0100; // VDD voltage 2.0 - 2.1V +pub const MMC_VDD_21_22: u32 = 0x0000_0200; // VDD voltage 2.1 - 2.2V +pub const MMC_VDD_22_23: u32 = 0x0000_0400; // VDD voltage 2.2 - 2.3V +pub const MMC_VDD_23_24: u32 = 0x0000_0800; // VDD voltage 2.3 - 2.4V +pub const MMC_VDD_24_25: u32 = 0x0000_1000; // VDD voltage 2.4 - 2.5V +pub const MMC_VDD_25_26: u32 = 0x0000_2000; // VDD voltage 2.5 - 2.6V +pub const MMC_VDD_26_27: u32 = 0x0000_4000; // VDD voltage 2.6 - 2.7V +pub const MMC_VDD_27_28: u32 = 0x0000_8000; // VDD voltage 2.7 - 2.8V +pub const MMC_VDD_28_29: u32 = 0x0001_0000; // VDD voltage 2.8 - 2.9V +pub const MMC_VDD_29_30: u32 = 0x0002_0000; // VDD voltage 2.9 - 3.0V +pub const MMC_VDD_30_31: u32 = 0x0004_0000; // VDD voltage 3.0 - 3.1V +pub const MMC_VDD_31_32: u32 = 0x0008_0000; // VDD voltage 3.1 - 3.2V +pub const MMC_VDD_32_33: u32 = 0x0010_0000; // VDD voltage 3.2 - 3.3V +pub const MMC_VDD_33_34: u32 = 0x0020_0000; // VDD voltage 3.3 - 3.4V +pub const MMC_VDD_34_35: u32 = 0x0040_0000; // VDD voltage 3.4 - 3.5V +pub const MMC_VDD_35_36: u32 = 0x0080_0000; // VDD voltage 3.5 - 3.6V diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs index 962c43a29..a7aa60393 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] // Allow dead code for the entire module + // Define constants for MMC data flags pub const MMC_DATA_READ: u32 = 1; pub const MMC_DATA_WRITE: u32 = 2; @@ -43,7 +45,49 @@ pub const SD_CMD_SWITCH_UHS18V: u32 = 11; pub const SD_CMD_APP_SET_BUS_WIDTH: u32 = 6; pub const SD_CMD_APP_SD_STATUS: u32 = 13; + pub const SD_CMD_ERASE_WR_BLK_START: u32 = 32; pub const SD_CMD_ERASE_WR_BLK_END: u32 = 33; + +/* + * Erase/discard + */ +pub const SD_ERASE_ARG: u32 = 0x00000000; +pub const SD_DISCARD_ARG: u32 = 0x00000001; + pub const SD_CMD_APP_SEND_OP_COND: u32 = 41; -pub const SD_CMD_APP_SEND_SCR: u32 = 51; \ No newline at end of file +pub const SD_CMD_APP_SEND_SCR: u32 = 51; + +pub const OCR_BUSY: u32 = 0x8000_0000; +pub const OCR_XPC: u32 = 0x1000_0000; +pub const OCR_HCS: u32 = 0x4000_0000; +pub const OCR_S18R: u32 = 0x0100_0000; +pub const OCR_VOLTAGE_MASK: u32 = 0x007F_FF80; +pub const OCR_ACCESS_MODE: u32 = 0x6000_0000; + +// The index to get the speed class information from SD switch function cmd +// Check Part 1 Physical Layer Simplified Specification Ver9.10 table 4-11 to see if I am wrong +pub const SD_SWITCH_FUNCTION_GROUP_ONE: usize = 13; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_SET_LEGACY: u8 = 0x0; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_SET_SDHS: u8 = 0x1; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR12: u8 = 0x0; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR25: u8 = 0x1; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR50: u8 = 0x2; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR104: u8 = 0x3; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_DDR50: u8 = 0x4; + +pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_LEGACY: u8 = + 1 << SD_SWITCH_FUNCTION_GROUP_ONE_SET_LEGACY; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_SDHS: u8 = 1 << SD_SWITCH_FUNCTION_GROUP_ONE_SET_SDHS; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR12: u8 = + 1 << SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR12; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR25: u8 = + 1 << SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR25; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR50: u8 = + 1 << SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR50; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104: u8 = + 1 << SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR104; +pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50: u8 = + 1 << SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_DDR50; + +pub const SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE: usize = 16; diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs new file mode 100644 index 000000000..37e0fa8be --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs @@ -0,0 +1,21 @@ +#[cfg(feature = "sel4-microkit")] +pub use sel4_microkit_support::process_wait_unreliable; + +#[cfg(feature = "sel4-microkit")] +pub use sel4_microkit_support::debug_log; + +#[cfg(not(feature = "sel4-microkit"))] +#[inline] +pub fn process_wait_unreliable(time_ns: u64) { + for _ in 0..time_ns { + hint::spin_loop(); // Use spin loop hint to reduce contention during the wait + } +} + +/// Bare metal +#[cfg(not(feature = "sel4-microkit"))] +#[macro_export] +// No operation, would be optimized out +macro_rules! debug_log { + ($($arg:tt)*) => {}; +} \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs new file mode 100644 index 000000000..98a2e1192 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs @@ -0,0 +1,144 @@ +use crate::sdmmc::{mmc_struct::{MmcBusWidth, MmcTiming, TuningState}, HostInfo, MmcData, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, SdmmcError}; + +#[allow(unused_variables)] +/// Program async Rust can be very dangerous if you do not know what is happening understand the hood +/// Power up and power off cannot be properly implemented if I do not have access to control gpio/ regulator and timer +pub trait SdmmcHardware { + fn sdmmc_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + fn sdmmc_init(&mut self) -> Result<(MmcIos, HostInfo, u128), SdmmcError> { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + /// Change the clock, return the value or do not change it at all + /// If the freq is set to zero, this function should try to stop the clock completely + /// Beware at higher frequency, you may need to play with delay, adjust and clock phase + /// to ensure that the clock edges (sampling points) occur just in time for the valid data window. + fn sdmmc_config_timing(&mut self, timing: MmcTiming) -> Result { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + fn sdmmc_config_bus_width(&mut self, bus_width: MmcBusWidth) -> Result<(), SdmmcError> { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + fn sdmmc_set_signal_voltage(&mut self, voltage: MmcSignalVoltage) -> Result<(), SdmmcError> { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + /// Reads the current state of the SD card data lanes. + /// + /// This function is specifically used during voltage switching to check if the SD card + /// acknowledges the switch by setting the data signal to low or high. + /// + /// Returns: + /// - `u8`: The current state of the data lanes, where each bit represents a data line: + /// - `DAT0` corresponds to the least significant bit (LSB). + /// - `DAT7` corresponds to the most significant bit (MSB). + /// + /// Note: + /// - This function is not yet implemented and currently returns an `ENOTIMPLEMENTED` error. + fn sdmmc_read_datalanes(&self) -> Result { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + /// Sends a command to the SD/MMC card, ensuring that busy signal handling is managed appropriately. + /// + /// ### Busy Signal Handling + /// The hardware layer is responsible for delaying the actual sending of the command if the card is busy. + /// For example, when the protocol layer sends a command expecting an R1B response (which indicates a busy state), + /// and immediately sends another command afterward, the hardware layer must ensure that the new command is sent + /// only after the busy signal from the card has cleared. + /// + /// ### Hardware Busy Signal Detection + /// Many modern host controllers support automatic busy signal detection, in which case the hardware layer + /// does not need to implement any additional checks or delays—the controller will wait internally until + /// the busy state is cleared before completing the command. + /// + /// ### Manual Busy Waiting + /// If the host controller does not support hardware busy signal detection, the hardware layer must + /// implement this behavior manually by monitoring the card's busy state and delaying the next command + /// until the card is ready. This approach aligns with Linux’s handling of busy signals in its MMC/SD subsystem. + /// + /// ### Parameters + /// * `cmd` - The SD/MMC command to send. + /// * `data` - Optional data associated with the command, if applicable. + /// + /// ### Returns + /// * `Ok(())` on success. + /// * `Err(SdmmcError::ENOTIMPLEMENTED)` if the function is not implemented. + /// + fn sdmmc_send_command( + &mut self, + cmd: &SdmmcCmd, + data: Option<&MmcData>, + ) -> Result<(), SdmmcError> { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + fn sdmmc_receive_response( + &self, + cmd: &SdmmcCmd, + response: &mut [u32; 4], + ) -> Result<(), SdmmcError> { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + // Change the function signature to something like sdmmc_config_interrupt(&mut self, enable_irq: bool, enable_sdio_irq: bool); + fn sdmmc_config_interrupt(&mut self, enable_irq: bool, enable_sdio_irq: bool) -> Result<(), SdmmcError> { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + fn sdmmc_ack_interrupt(&mut self) -> Result<(), SdmmcError> { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + /// At higher clock frequencies, timing mismatches can occur between the host's sampling point and the valid data window + /// from the SD card during read operations. This can lead to CRC errors, as the host may sample incoming data outside the + /// stable data window, even when the SD card’s response appears normal. + /// + /// To address this, the `sdmmc_tune_sampling` function is introduced. This function aims to adjust the host's sampling + /// timing to align with the SD card’s data output window, reducing errors caused by timing misalignment. + /// + /// In some cases, a similar function (e.g., `sdmmc_tune_sending_data_window`) may be needed to tune the timing of data + /// signals sent from the host to the SD card. This would ensure that the SD card reliably receives data, especially + /// at high frequencies. However, output timing tends to be more stable, and a specific function for tuning host-to-card + /// data timing is often not implemented or needed, as seen in the Linux driver. + /// + /// The cooperation between protocol layer and hardware layer by this function is like: the protocol layer send + /// the MMC_CMD_SEND_TUNING_BLOCK request. And if the result receive back is in error, the protocol layer will call this + /// tune_sampling function once again. If tune sampling has run out of option, return an error. + /// It is suggest that hardware layer also do some book keeping about the suitable delay to making the tuning + /// sampling process faster. + /// Performs sampling point tuning for the SD/MMC interface. + /// + /// The `TuningState` enum controls the tuning process, guiding the adjustments + /// of the sampling point to achieve reliable communication at high speeds. + /// + /// # Tuning Process + /// + /// - Before tuning begins, the protocol layer calls `sdmmc_tune_sampling` + /// with the `TuningStart` state to initialize the tuning process. + /// - The protocol layer then attempts to verify if the current sampling adjustment is effective. + /// - If verification fails, the protocol layer calls `sdmmc_tune_sampling` + /// with the `TuningContinue` state to adjust the sampling point further. + /// - This adjustment and verification process repeats until a reliable sampling + /// point is found. + /// - Once a working delay is identified, the protocol layer calls + /// `sdmmc_tune_sampling` with the `TuningComplete` state to finalize the tuning process. + /// + /// # Parameters + /// - `state`: The current tuning state, represented by the `TuningState` enum: + /// - `TuningStart`: Initializes the tuning process. + /// - `TuningContinue`: Adjusts the sampling point incrementally. + /// - `TuningComplete`: Finalizes the tuning once a reliable sampling point is found. + /// + /// # Returns + /// - `Ok(())`: Indicates successful completion of the requested tuning step. + /// - `Err(SdmmcError)`: An error occurred during the tuning process. + fn sdmmc_tune_sampling(&mut self, state: TuningState) -> Result<(), SdmmcError> { + return Err(SdmmcError::ENOTIMPLEMENTED); + } +} \ No newline at end of file diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index fa6dc3c05..196e12c75 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -18,10 +18,8 @@ use sddf_blk::{ }; use sdmmc_hal::meson_gx_mmc::SdmmcMesonHardware; -use sdmmc_protocol::sdmmc::{ - sdmmc_capability::{MMC_INTERRUPT_END_OF_CHAIN, MMC_INTERRUPT_ERROR}, - SdmmcHalError, SdmmcHardware, SdmmcProtocol, -}; +use sdmmc_protocol::sdmmc::{SdmmcError, SdmmcProtocol}; +use sdmmc_protocol::sdmmc_traits::SdmmcHardware; use sel4_microkit::{debug_print, debug_println, protection_domain, Channel, Handler, Infallible}; const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(0); @@ -98,24 +96,10 @@ fn init() -> HandlerImpl { .setup_card() .unwrap_or_else(|error| panic!("SDMMC: Error at setup {:?}", error)); - let mut test: u32 = 0; - let _ = sdmmc_host.enable_interrupt(&mut test); - - /* - unsafe { - unsafe_stolen_memory[0] = 1; - unsafe_stolen_memory[10] = 64; - unsafe_stolen_memory[53] = 98; - - debug_println!("printing out memory have value written in it"); + let _ = sdmmc_host.config_interrupt(false, false); - print_one_block(unsafe_stolen_memory.as_ptr(), 512); - - assert!(unsafe_stolen_memory[0] == 1); - assert!(unsafe_stolen_memory[10] == 64); - assert!(unsafe_stolen_memory[53] == 98); - } - */ + // Print out one block to check if read works + // sdmmc_host.test_read_one_block(0, 0xf5500000); // TODO: Should tuning be possible to fail? sdmmc_host @@ -129,10 +113,8 @@ fn init() -> HandlerImpl { print_one_block(unsafe_stolen_memory.as_ptr(), 64); } - let mut irq_to_enable = MMC_INTERRUPT_ERROR | MMC_INTERRUPT_END_OF_CHAIN; - // Should always succeed, at least for odroid C4 - let _ = sdmmc_host.enable_interrupt(&mut irq_to_enable); + let _ = sdmmc_host.config_interrupt(true, false); HandlerImpl { future: None, sdmmc: Some(sdmmc_host), @@ -142,7 +124,7 @@ fn init() -> HandlerImpl { } struct HandlerImpl { - future: Option, SdmmcProtocol)>>>>, + future: Option, SdmmcProtocol)>>>>, sdmmc: Option>, request: Option, retry: u16, @@ -241,15 +223,16 @@ impl Handler for HandlerImpl { &mut request.id as *mut u32, ); } + // TODO: Consider how to add integer overflow check here request.block_number = request.block_number * SDDF_TO_REAL_SECTOR; request.count = request.count * SDDF_TO_REAL_SECTOR; // Print the retrieved values - - // debug_println!("io_or_offset: 0x{:x}", request.io_or_offset);// Simple u64 - // debug_println!("block_number: {}", request.block_number); // Simple u32 - // debug_println!("count: {}", request.count); // Simple u16 - // debug_println!("id: {}", request.id); // Simple u32 - + /* + debug_println!("io_or_offset: 0x{:x}", request.io_or_offset);// Simple u64 + debug_println!("block_number: {}", request.block_number); // Simple u32 + debug_println!("count: {}", request.count); // Simple u16 + debug_println!("id: {}", request.id); // Simple u32 + */ match request.request_code { BlkOp::BlkReqRead => { // Reset retry chance here @@ -321,9 +304,11 @@ impl Handler for HandlerImpl { if let Some(ref mut future) = self.future { match future.as_mut().poll(&mut cx) { Poll::Ready(_) => { - panic!("SDMMC: The newly created future returned immediately! + panic!( + "SDMMC: The newly created future returned immediately! Most likely the future contain an invalid request! - Double check request sanitize process!") + Double check request sanitize process!" + ) } Poll::Pending => break 'process_notification, } diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs index 6c4e537a3..549072c7c 100644 --- a/drivers/blk/sdmmc/src/sddf_blk/mod.rs +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -1,10 +1,10 @@ -extern "C" { - pub fn blk_queue_init_helper(); +unsafe extern "C" { + pub unsafe fn blk_queue_init_helper(); - pub fn blk_queue_empty_req_helper() -> u8; - pub fn blk_queue_full_resp_helper() -> u8; - pub fn blk_enqueue_resp_helper(status: BlkStatus, success: u32, id: u32) -> u8; - pub fn blk_dequeue_req_helper( + pub unsafe fn blk_queue_empty_req_helper() -> u8; + pub unsafe fn blk_queue_full_resp_helper() -> u8; + pub unsafe fn blk_enqueue_resp_helper(status: BlkStatus, success: u32, id: u32) -> u8; + pub unsafe fn blk_dequeue_req_helper( code: *mut BlkOp, io_or_offset: *mut u64, block_number: *mut u32, diff --git a/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json b/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json index 16ce9ef87..93c5b7fac 100644 --- a/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json +++ b/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json @@ -1,13 +1,11 @@ { "arch": "aarch64", "crt-objects-fallback": "false", - "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32", "disable-redzone": true, - "eh-frame-header": false, - "env": "sel4", "exe-suffix": ".elf", "features": "+v8a,+strict-align,+neon,+fp-armv8", - "link-script": "__sel4_ipc_buffer_obj = (_end + 4096 - 1) & ~(4096 - 1);", + "link-script": "__sel4_ipc_buffer_obj = (__ehdr_start & ~(4096 - 1)) - 4096;", "linker": "rust-lld", "linker-flavor": "gnu-lld", "llvm-target": "aarch64-unknown-none", @@ -34,4 +32,4 @@ "kernel-address" ], "target-pointer-width": "64" -} +} \ No newline at end of file From efdffb5aeded4d1188c65e49102d6301652d3236 Mon Sep 17 00:00:00 2001 From: Ivan-Velickovic Date: Fri, 28 Feb 2025 14:50:32 +1100 Subject: [PATCH 15/48] Fix Rust driver to adhere to block count as u16 Signed-off-by: Ivan-Velickovic --- drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 4 ++-- drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 2 +- drivers/blk/sdmmc/src/main.rs | 14 +++++++------- drivers/blk/sdmmc/src/sddf_blk/mod.rs | 10 +++++----- drivers/blk/sdmmc/src/sddf_helper.c | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs index 29cc3f4a0..77380fbac 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs @@ -120,7 +120,7 @@ const IRQ_EN_MASK: u32 = IRQ_ERR_MASK | IRQ_END_OF_CHAIN; const MESON_SDCARD_SECTOR_SIZE: u32 = 512; -pub const MAX_BLOCK_PER_TRANSFER: u32 = 0x1FF; +pub const MAX_BLOCK_PER_TRANSFER: u16 = 0x1FF; const WRITE_ADDR_UPPER: u32 = 0xFFFE0000; @@ -634,7 +634,7 @@ impl SdmmcHardware for SdmmcMesonHardware { // TODO: Check what if the addr is u32::MAX, will the sdcard still working? if mmc_data.addr >= (WRITE_ADDR_UPPER as u64) || mmc_data.blockcnt == 0 - || mmc_data.blockcnt > MAX_BLOCK_PER_TRANSFER + || mmc_data.blockcnt > MAX_BLOCK_PER_TRANSFER as u32 { debug_log!("SDMMC: INVALID INPUT VARIABLE!"); return Err(SdmmcError::EINVAL); diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs index 80dfa2ae4..e0601a723 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -237,7 +237,7 @@ pub struct MmcIos { pub struct HostInfo { pub max_frequency: u64, pub min_frequency: u64, - pub max_block_per_req: u32, + pub max_block_per_req: u16, } /// TODO: Add more variables for SdmmcProtocol to track the state of the sdmmc controller and card correctly diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 196e12c75..c92deadee 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -26,9 +26,9 @@ const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(0); const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(1); -const SDCARD_SECTOR_SIZE: u32 = 512; -const SDDF_TRANSFER_SIZE: u32 = 4096; -const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE; +const SDCARD_SECTOR_SIZE: u16 = 512; +const SDDF_TRANSFER_SIZE: u16 = 4096; +const SDDF_TO_REAL_SECTOR: u16 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE; const RETRY_CHANCE: u16 = 5; @@ -219,12 +219,12 @@ impl Handler for HandlerImpl { &mut request.request_code as *mut BlkOp, &mut request.io_or_offset as *mut u64, &mut request.block_number as *mut u32, - &mut request.count as *mut u32, + &mut request.count as *mut u16, &mut request.id as *mut u32, ); } // TODO: Consider how to add integer overflow check here - request.block_number = request.block_number * SDDF_TO_REAL_SECTOR; + request.block_number = request.block_number * SDDF_TO_REAL_SECTOR as u32; request.count = request.count * SDDF_TO_REAL_SECTOR; // Print the retrieved values /* @@ -263,7 +263,7 @@ impl Handler for HandlerImpl { BlkOp::BlkReqRead => { // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer request.count_to_do = core::cmp::min( - request.count as u32, + request.count, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER, ); if let Some(sdmmc) = self.sdmmc.take() { @@ -280,7 +280,7 @@ impl Handler for HandlerImpl { BlkOp::BlkReqWrite => { // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer request.count_to_do = core::cmp::min( - request.count as u32, + request.count, sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER, ); if let Some(sdmmc) = self.sdmmc.take() { diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs index 549072c7c..38f817f55 100644 --- a/drivers/blk/sdmmc/src/sddf_blk/mod.rs +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -3,12 +3,12 @@ unsafe extern "C" { pub unsafe fn blk_queue_empty_req_helper() -> u8; pub unsafe fn blk_queue_full_resp_helper() -> u8; - pub unsafe fn blk_enqueue_resp_helper(status: BlkStatus, success: u32, id: u32) -> u8; + pub unsafe fn blk_enqueue_resp_helper(status: BlkStatus, success: u16, id: u32) -> u8; pub unsafe fn blk_dequeue_req_helper( code: *mut BlkOp, io_or_offset: *mut u64, block_number: *mut u32, - count: *mut u32, + count: *mut u16, id: *mut u32, ); } @@ -31,9 +31,9 @@ pub struct BlkRequest { pub request_code: BlkOp, pub io_or_offset: u64, pub block_number: u32, - pub count: u32, + pub count: u16, // I suggest use u32 here and change the count to use u32 in sddf_blk - pub success_count: u32, - pub count_to_do: u32, + pub success_count: u16, + pub count_to_do: u16, pub id: u32, } diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/sdmmc/src/sddf_helper.c index 9d90c81bf..e1cc7606f 100644 --- a/drivers/blk/sdmmc/src/sddf_helper.c +++ b/drivers/blk/sdmmc/src/sddf_helper.c @@ -34,7 +34,7 @@ uint8_t blk_enqueue_resp_helper(uint8_t status, uint16_t success, uint32_t id) { return 1; } -uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint32_t *count, uint32_t *id) { +uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint16_t *count, uint32_t *id) { // It would be better if we do not use int but use int8_t // uint16_t temp_count = 0; if (blk_dequeue_req(queue_handle, (blk_req_code_t *)code, io_or_offset, block_number, count, id) == 0) { From 7bdd5387b2ec5cc0ca20c87e3e794229026f8697 Mon Sep 17 00:00:00 2001 From: Cheng Date: Fri, 28 Feb 2025 14:54:28 +1100 Subject: [PATCH 16/48] Update the .system file for odroid c4 blk example Signed-off-by: Cheng --- examples/blk/board/odroidc4/blk.system | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/blk/board/odroidc4/blk.system b/examples/blk/board/odroidc4/blk.system index 64a1d1116..35a6b982d 100644 --- a/examples/blk/board/odroidc4/blk.system +++ b/examples/blk/board/odroidc4/blk.system @@ -15,13 +15,18 @@ + + + + + From d16644c926eea9cbcd0c704ca231b40668219c7d Mon Sep 17 00:00:00 2001 From: Ivan-Velickovic Date: Fri, 28 Feb 2025 15:43:00 +1100 Subject: [PATCH 17/48] Fixes for rebase onto tooling changes and metaprogram, blk example works Signed-off-by: Ivan-Velickovic --- drivers/blk/sdmmc/Cargo.toml | 2 +- drivers/blk/sdmmc/blk_driver.mk | 2 +- drivers/blk/sdmmc/config.json | 23 +++++ .../blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 10 +- drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 21 ++-- drivers/blk/sdmmc/src/main.rs | 24 +++-- drivers/blk/sdmmc/src/sddf_blk/mod.rs | 8 +- drivers/blk/sdmmc/src/sddf_helper.c | 69 ++++++------- examples/blk/blk.mk | 2 +- examples/blk/blk_config.h | 97 ------------------- examples/blk/meta.py | 25 ++++- 11 files changed, 122 insertions(+), 161 deletions(-) create mode 100644 drivers/blk/sdmmc/config.json delete mode 100644 examples/blk/blk_config.h diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 0e7b2e88f..9dd8ea280 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sdmmc_driver" +name = "blk_driver" version = "0.1.0" edition = "2021" diff --git a/drivers/blk/sdmmc/blk_driver.mk b/drivers/blk/sdmmc/blk_driver.mk index 5bab1dbe7..2e47f0d0d 100644 --- a/drivers/blk/sdmmc/blk_driver.mk +++ b/drivers/blk/sdmmc/blk_driver.mk @@ -34,7 +34,7 @@ blk_driver.elf: $(BUILD_DIR) blk/sdmmc/meson/libsddfblk.a echo "MICROKIT SDK config directory: $(microkit_sdk_config_dir)" && \ echo "SEl4 include directories: $(sel4_include_dirs)" && \ SEL4_INCLUDE_DIRS=$(abspath $(sel4_include_dirs)) \ - cargo build --release \ + cargo build \ -Z build-std=core,alloc,compiler_builtins \ -Z build-std-features=compiler-builtins-mem \ --target-dir $(BUILD_DIR)/blk/sdmmc/meson/ \ diff --git a/drivers/blk/sdmmc/config.json b/drivers/blk/sdmmc/config.json new file mode 100644 index 000000000..fac1c91eb --- /dev/null +++ b/drivers/blk/sdmmc/config.json @@ -0,0 +1,23 @@ +{ + "compatible": [ + "amlogic,meson-axg-mmc" + ], + "resources": { + "regions": [ + { + "name": "regs", + "perms": "rw", + "dt_index": 0 + }, + { + "name": "init_data", + "size": 4096 + } + ], + "irqs": [ + { + "dt_index": 0 + } + ] + } +} diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs index 77380fbac..8cc6f012a 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs @@ -9,8 +9,6 @@ use sdmmc_protocol::{sdmmc::{ }, sdmmc_os::{debug_log, process_wait_unreliable}, sdmmc_traits::SdmmcHardware }; -const SDIO_BASE: u64 = 0xffe05000; // Base address from DTS - // The always on gpio pin pull const AO_RTI_PIN_REGION_START: u64 = 0xff800014; const AO_RTI_PIN_REGION_END: u64 = 0xff800038; @@ -183,8 +181,8 @@ struct MesonSdmmcRegisters { impl MesonSdmmcRegisters { /// This function is unsafe because it tries to - unsafe fn new() -> &'static mut MesonSdmmcRegisters { - &mut *(SDIO_BASE as *mut MesonSdmmcRegisters) + unsafe fn new(base: u64) -> &'static mut MesonSdmmcRegisters { + &mut *(base as *mut MesonSdmmcRegisters) } } @@ -204,8 +202,8 @@ pub struct SdmmcMesonHardware { } impl SdmmcMesonHardware { - pub unsafe fn new() -> Self { - let register = MesonSdmmcRegisters::new(); + pub unsafe fn new(base: u64) -> Self { + let register = MesonSdmmcRegisters::new(base); // TODO: Call reset function here SdmmcMesonHardware { diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs index e0601a723..fc76324a9 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -599,7 +599,7 @@ impl SdmmcProtocol { /// - `Result<(), SdmmcError>`: `Ok(())` if tuning was successful, or an error otherwise. pub fn tune_performance( &mut self, - memory_and_invalidate_cache_fn: Option<(&mut [u8; 64], fn())>, + memory_and_invalidate_cache_fn: Option<(&mut [u8; 64], u64, fn())>, ) -> Result<(), SdmmcError> { // For testing let mmc_device = self.mmc_device.as_mut().ok_or(SdmmcError::ENOCARD)?; @@ -789,13 +789,14 @@ impl SdmmcProtocol { &mut self, target: MmcTiming, memory: &mut [u8; 64], + memory_ioaddr: u64, invalidate_cache_fn: fn(), ) -> Result<(), SdmmcError> { let data: MmcData = MmcData { blocksize: 64, blockcnt: 1, flags: MmcDataFlag::SdmmcDataRead, - addr: memory.as_ptr() as u64, + addr: memory_ioaddr, }; let mut resp: [u32; 4] = [0; 4]; @@ -833,13 +834,14 @@ impl SdmmcProtocol { fn sdcard_check_supported_speed_class( &mut self, memory: &mut [u8; 64], + memory_ioaddr: u64, invalidate_cache_fn: fn(), ) -> Result<(), SdmmcError> { let data: MmcData = MmcData { blocksize: 64, blockcnt: 1, flags: MmcDataFlag::SdmmcDataRead, - addr: memory.as_ptr() as u64, + addr: memory_ioaddr, }; let mut resp: [u32; 4] = [0; 4]; @@ -893,7 +895,7 @@ impl SdmmcProtocol { /// Since we are using u8 here, the endianness does matter fn tune_sdcard_performance( &mut self, - memory_and_invalidate_cache_fn: Option<(&mut [u8; 64], fn())>, + memory_and_invalidate_cache_fn: Option<(&mut [u8; 64], u64, fn())>, ) -> Result<(), SdmmcError> { let mut resp: [u32; 4] = [0; 4]; @@ -933,7 +935,7 @@ impl SdmmcProtocol { // TODO: Change sdcard bus width here, or get rid of that field completely } - if let Some((memory, cache_invalidate_function)) = memory_and_invalidate_cache_fn { + if let Some((memory, memory_ioaddr, cache_invalidate_function)) = memory_and_invalidate_cache_fn { debug_log!("Checking supported speed classes\n"); if let Some(MmcDevice::Sdcard(ref mut sdcard)) = self.mmc_device { match self.mmc_ios.signal_voltage { @@ -946,6 +948,7 @@ impl SdmmcProtocol { // When the target speed is None, the sdcard_switch_speed update sdcard speed capability self.sdcard_check_supported_speed_class( memory, + memory_ioaddr, cache_invalidate_function, )?; } @@ -958,6 +961,7 @@ impl SdmmcProtocol { { self.sdcard_check_supported_speed_class( memory, + memory_ioaddr, cache_invalidate_function, )?; } @@ -987,6 +991,7 @@ impl SdmmcProtocol { self.sdcard_switch_speed( MmcTiming::SdHs, memory, + memory_ioaddr, cache_invalidate_function, )?; timing = MmcTiming::SdHs; @@ -1001,6 +1006,7 @@ impl SdmmcProtocol { self.sdcard_switch_speed( MmcTiming::UhsSdr104, memory, + memory_ioaddr, cache_invalidate_function, )?; timing = MmcTiming::UhsSdr104; @@ -1015,6 +1021,7 @@ impl SdmmcProtocol { self.sdcard_switch_speed( MmcTiming::UhsDdr50, memory, + memory_ioaddr, cache_invalidate_function, )?; timing = MmcTiming::UhsDdr50; @@ -1028,6 +1035,7 @@ impl SdmmcProtocol { self.sdcard_switch_speed( MmcTiming::UhsSdr50, memory, + memory_ioaddr, cache_invalidate_function, )?; timing = MmcTiming::UhsSdr50; @@ -1041,6 +1049,7 @@ impl SdmmcProtocol { self.sdcard_switch_speed( MmcTiming::UhsSdr25, memory, + memory_ioaddr, cache_invalidate_function, )?; timing = MmcTiming::UhsSdr25; @@ -1051,7 +1060,7 @@ impl SdmmcProtocol { } }; self.mmc_ios.clock = self.hardware.sdmmc_config_timing(timing)?; - self.process_sampling(memory.as_ptr() as u64, cache_invalidate_function)?; + self.process_sampling(memory_ioaddr, cache_invalidate_function)?; debug_log!("Current frequency: {}Hz", self.mmc_ios.clock); } else { diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index c92deadee..c93badc39 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -15,6 +15,7 @@ use alloc::boxed::Box; use sddf_blk::{ blk_dequeue_req_helper, blk_enqueue_resp_helper, blk_queue_empty_req_helper, blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkRequest, BlkStatus, + blk_device_regs_vaddr, blk_device_init_data_vaddr, blk_device_init_data_ioaddr }; use sdmmc_hal::meson_gx_mmc::SdmmcMesonHardware; @@ -22,9 +23,8 @@ use sdmmc_protocol::sdmmc::{SdmmcError, SdmmcProtocol}; use sdmmc_protocol::sdmmc_traits::SdmmcHardware; use sel4_microkit::{debug_print, debug_println, protection_domain, Channel, Handler, Infallible}; -const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(0); - -const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(1); +const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(0); +const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(1); const SDCARD_SECTOR_SIZE: u16 = 512; const SDDF_TRANSFER_SIZE: u16 = 4096; @@ -73,14 +73,23 @@ fn init() -> HandlerImpl { unsafe { blk_queue_init_helper(); } - let meson_hal: SdmmcMesonHardware = unsafe { SdmmcMesonHardware::new() }; + let regs_base = unsafe { + blk_device_regs_vaddr() + }; + let meson_hal: SdmmcMesonHardware = unsafe { SdmmcMesonHardware::new(regs_base) }; let unsafe_stolen_memory: &mut [u8; 64]; // This line of code actually is very unsafe! // Considering the memory is stolen from the memory that has sdcard registers mapped in + let init_data_vaddr = unsafe { + blk_device_init_data_vaddr() + }; + let init_data_ioaddr = unsafe { + blk_device_init_data_ioaddr() + }; unsafe { - let stolen_memory_addr = 0xf5500000 as *mut [u8; 64]; + let stolen_memory_addr = init_data_vaddr as *mut [u8; 64]; assert!(stolen_memory_addr as usize % 8 == 0); unsafe_stolen_memory = &mut (*stolen_memory_addr); } @@ -105,6 +114,7 @@ fn init() -> HandlerImpl { sdmmc_host .tune_performance(Some(( unsafe_stolen_memory, + init_data_ioaddr, dummy_cache_invalidate_function, ))) .unwrap_or_else(|error| panic!("SDMMC: Error at tuning performance {:?}", error)); @@ -218,13 +228,13 @@ impl Handler for HandlerImpl { blk_dequeue_req_helper( &mut request.request_code as *mut BlkOp, &mut request.io_or_offset as *mut u64, - &mut request.block_number as *mut u32, + &mut request.block_number as *mut u64, &mut request.count as *mut u16, &mut request.id as *mut u32, ); } // TODO: Consider how to add integer overflow check here - request.block_number = request.block_number * SDDF_TO_REAL_SECTOR as u32; + request.block_number = request.block_number * SDDF_TO_REAL_SECTOR as u64; request.count = request.count * SDDF_TO_REAL_SECTOR; // Print the retrieved values /* diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs index 38f817f55..3636b46ef 100644 --- a/drivers/blk/sdmmc/src/sddf_blk/mod.rs +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -1,13 +1,17 @@ unsafe extern "C" { pub unsafe fn blk_queue_init_helper(); + pub unsafe fn blk_device_regs_vaddr() -> u64; + pub unsafe fn blk_device_init_data_vaddr() -> u64; + pub unsafe fn blk_device_init_data_ioaddr() -> u64; + pub unsafe fn blk_queue_empty_req_helper() -> u8; pub unsafe fn blk_queue_full_resp_helper() -> u8; pub unsafe fn blk_enqueue_resp_helper(status: BlkStatus, success: u16, id: u32) -> u8; pub unsafe fn blk_dequeue_req_helper( code: *mut BlkOp, io_or_offset: *mut u64, - block_number: *mut u32, + block_number: *mut u64, count: *mut u16, id: *mut u32, ); @@ -30,7 +34,7 @@ pub enum BlkStatus { pub struct BlkRequest { pub request_code: BlkOp, pub io_or_offset: u64, - pub block_number: u32, + pub block_number: u64, pub count: u16, // I suggest use u32 here and change the count to use u32 in sddf_blk pub success_count: u16, diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/sdmmc/src/sddf_helper.c index e1cc7606f..0544e4e55 100644 --- a/drivers/blk/sdmmc/src/sddf_helper.c +++ b/drivers/blk/sdmmc/src/sddf_helper.c @@ -1,66 +1,57 @@ -#include -#include #include +#include +#include +#include -blk_queue_handle_t queue_handle_memory; -blk_queue_handle_t *queue_handle = &queue_handle_memory; - -blk_req_queue_t *blk_req_queue; -blk_resp_queue_t *blk_resp_queue; +__attribute__((__section__(".device_resources"))) device_resources_t device_resources; +__attribute__((__section__(".blk_driver_config"))) blk_driver_config_t config; -blk_storage_info_t *blk_config; +blk_queue_handle_t blk_queue; void blk_queue_init_helper() { - blk_queue_init(queue_handle, blk_req_queue, blk_resp_queue, BLK_QUEUE_CAPACITY_DRIV); - blk_config->sector_size = 512; - blk_config->block_size = 1; - blk_config->capacity = 0xFFFFFFFFFF; - blk_config->ready = true; + blk_queue_init(&blk_queue, config.virt.req_queue.vaddr, config.virt.resp_queue.vaddr, config.virt.num_buffers); + + blk_storage_info_t *storage_info = config.virt.storage_info.vaddr; + storage_info->sector_size = 512; + storage_info->block_size = 1; + storage_info->capacity = 0xFFFFFFFFFF; + storage_info->ready = true; +} + +uint64_t blk_device_regs_vaddr() { + return (uint64_t)device_resources.regions[0].region.vaddr; +} + +uint64_t blk_device_init_data_vaddr() { + return (uint64_t)device_resources.regions[1].region.vaddr; +} + +uint64_t blk_device_init_data_ioaddr() { + return (uint64_t)device_resources.regions[1].io_addr; } uint8_t blk_queue_empty_req_helper() { - return blk_queue_empty_req(queue_handle); + return blk_queue_empty_req(&blk_queue); } uint8_t blk_queue_full_resp_helper() { - return blk_queue_full_resp(queue_handle); + return blk_queue_full_resp(&blk_queue); } uint8_t blk_enqueue_resp_helper(uint8_t status, uint16_t success, uint32_t id) { // It would be better if we do not use int but use int8_t - if (blk_enqueue_resp(queue_handle, status, success, id) == 0) { + if (blk_enqueue_resp(&blk_queue, status, success, id) == 0) { return 0; } return 1; } -uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint16_t *count, uint32_t *id) { +uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint64_t *block_number, uint16_t *count, uint32_t *id) { // It would be better if we do not use int but use int8_t // uint16_t temp_count = 0; - if (blk_dequeue_req(queue_handle, (blk_req_code_t *)code, io_or_offset, block_number, count, id) == 0) { + if (blk_dequeue_req(&blk_queue, (blk_req_code_t *)code, io_or_offset, block_number, count, id) == 0) { return 0; } // *count = temp_count; return 1; } - -/* -uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint16_t *count, uint32_t *id) { - - if (blk_dequeue_req(queue_handle, (blk_req_code_t *)code, io_or_offset, block_number, count, id) == 0) { - return 0; - } - return 1; -} - -// Why this version does not work???? -uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint32_t *block_number, uint32_t *count, uint32_t *id) { - - uint16_t temp_count = 0; - if (blk_dequeue_req(queue_handle, (blk_req_code_t *)code, io_or_offset, block_number, &temp_count, id) == 0) { - return 0; - } - *count = temp_count; - return 1; -} -*/ \ No newline at end of file diff --git a/examples/blk/blk.mk b/examples/blk/blk.mk index 1361547e5..46ddd069c 100644 --- a/examples/blk/blk.mk +++ b/examples/blk/blk.mk @@ -83,7 +83,7 @@ BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) ARCH := ${shell grep 'CONFIG_SEL4_ARCH ' $(BOARD_DIR)/include/kernel/gen_config.h | cut -d' ' -f4} SDDF_CUSTOM_LIBC := 1 -IMAGES := client.elf blk_virt.elf serial_virt_tx.elf serial_driver.elf +IMAGES := blk_driver.elf client.elf blk_virt.elf serial_virt_tx.elf serial_driver.elf CFLAGS := -mcpu=$(CPU) \ -mstrict-align \ diff --git a/examples/blk/blk_config.h b/examples/blk/blk_config.h deleted file mode 100644 index ebfc15f63..000000000 --- a/examples/blk/blk_config.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024, UNSW - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#define BLK_NUM_CLIENTS 1 - -#define BLK_NAME_CLI0 "client" - -#define BLK_QUEUE_CAPACITY_CLI0 1024 -#define BLK_QUEUE_CAPACITY_DRIV BLK_QUEUE_CAPACITY_CLI0 - -#define BLK_QUEUE_REGION_SIZE 0x200000 -#define BLK_DATA_REGION_SIZE_CLI0 BLK_QUEUE_REGION_SIZE -#define BLK_DATA_REGION_SIZE_DRIV BLK_QUEUE_REGION_SIZE - -#define BLK_QUEUE_REGION_SIZE_CLI0 BLK_QUEUE_REGION_SIZE -#define BLK_QUEUE_REGION_SIZE_DRIV BLK_QUEUE_REGION_SIZE - -/* Mapping from client index to disk partition that the client will have access to. */ -// Please ensure this point to a partition that you do not mind to courrupt as the example involves write data to that component -static const int blk_partition_mapping[BLK_NUM_CLIENTS] = { 0 }; - -static inline blk_storage_info_t *blk_virt_cli_storage_info(blk_storage_info_t *info, unsigned int id) -{ - switch (id) { - case 0: - return info; - default: - return NULL; - } -} - -static inline uintptr_t blk_virt_cli_data_region(uintptr_t data, unsigned int id) -{ - switch (id) { - case 0: - return data; - default: - return 0; - } -} - -static inline uint64_t blk_virt_cli_data_region_size(unsigned int id) -{ - switch (id) { - case 0: - return BLK_DATA_REGION_SIZE_CLI0; - default: - return 0; - } -} - -static inline blk_req_queue_t *blk_virt_cli_req_queue(blk_req_queue_t *req, unsigned int id) -{ - switch (id) { - case 0: - return req; - default: - return NULL; - } -} - -static inline blk_resp_queue_t *blk_virt_cli_resp_queue(blk_resp_queue_t *resp, unsigned int id) -{ - switch (id) { - case 0: - return resp; - default: - return NULL; - } -} - -static inline uint32_t blk_virt_cli_queue_capacity(unsigned int id) -{ - switch (id) { - case 0: - return BLK_QUEUE_CAPACITY_CLI0; - default: - return 0; - } -} - -static inline uint32_t blk_cli_queue_capacity(char *pd_name) -{ - if (!sddf_strcmp(pd_name, BLK_NAME_CLI0)) { - return BLK_QUEUE_CAPACITY_CLI0; - } else { - return 0; - } -} diff --git a/examples/blk/meta.py b/examples/blk/meta.py index 420bc9f70..34ec6c9ca 100644 --- a/examples/blk/meta.py +++ b/examples/blk/meta.py @@ -9,7 +9,8 @@ assert version("sdfgen").split(".")[1] == "27", "Unexpected sdfgen version" ProtectionDomain = SystemDescription.ProtectionDomain - +MemoryRegion = SystemDescription.MemoryRegion +Map = SystemDescription.Map @dataclass class Board: @@ -45,6 +46,23 @@ class Board: timer="soc@0/bus@30000000/timer@302d0000", serial="soc@0/bus@30800000/serial@30860000", ), + + Board( + name="odroidc4", + arch=SystemDescription.Arch.AARCH64, + paddr_top=0x80000000, + partition=0, + blk="soc/sd@ffe05000", + timer=None, + ), + Board( + name="odroidc4", + arch=SystemDescription.Arch.AARCH64, + paddr_top=0x80000000, + partition=0, + blk="soc/sd@ffe05000", + timer=None, + ), Board( name="qemu_virt_riscv64", arch=SystemDescription.Arch.RISCV64, @@ -95,6 +113,11 @@ def generate(sdf_file: str, output_dir: str, dtb: DeviceTree): partition = int(args.partition) if args.partition else board.partition blk_system.add_client(client, partition=partition) + if board.name == "odroidc4": + gpio_mr = MemoryRegion("gpio", 0x1000, paddr=0xff800000) + blk_driver.add_map(Map(gpio_mr, 0xff800000, Map.Perms(r=True, w=True), cached=False)) + sdf.add_mr(gpio_mr) + serial_system.add_client(client) pds = [serial_driver, serial_virt_tx, blk_driver, blk_virt, client] From f91771f6b29580d2d9aff39fd75ed13b4cbe37ae Mon Sep 17 00:00:00 2001 From: Ivan-Velickovic Date: Fri, 28 Feb 2025 16:34:29 +1100 Subject: [PATCH 18/48] blk.mk: fix from rebase conflict Signed-off-by: Ivan-Velickovic --- examples/blk/blk.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/blk/blk.mk b/examples/blk/blk.mk index 46ddd069c..e17d337d5 100644 --- a/examples/blk/blk.mk +++ b/examples/blk/blk.mk @@ -85,8 +85,7 @@ SDDF_CUSTOM_LIBC := 1 IMAGES := blk_driver.elf client.elf blk_virt.elf serial_virt_tx.elf serial_driver.elf -CFLAGS := -mcpu=$(CPU) \ - -mstrict-align \ +CFLAGS := -mstrict-align \ -nostdlib \ -ffreestanding \ -g3 \ From 95b6fd09622c570b86c52c3a69aceef4b49e0b18 Mon Sep 17 00:00:00 2001 From: Ivan-Velickovic Date: Fri, 28 Feb 2025 21:25:34 +1100 Subject: [PATCH 19/48] blk_driver.mk fix Signed-off-by: Ivan-Velickovic --- drivers/blk/sdmmc/blk_driver.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/blk/sdmmc/blk_driver.mk b/drivers/blk/sdmmc/blk_driver.mk index 2e47f0d0d..67c669d91 100644 --- a/drivers/blk/sdmmc/blk_driver.mk +++ b/drivers/blk/sdmmc/blk_driver.mk @@ -21,7 +21,7 @@ blk/sdmmc/meson/sddf_helper.o: $(SDMMC_DRIVER_DIR)/src/sddf_helper.c |blk/sdmmc/ $(CC) -c $(CFLAGS) $< -o $@ blk/sdmmc/meson/libsddfblk.a: blk/sdmmc/meson/sddf_helper.o |blk/sdmmc/meson - ar rcs $@ $< + ${AR} rcs $@ $< blk/sdmmc/meson: mkdir -p $@ From ac9e982a3cffda2c43b43fa2ead70a436439a66b Mon Sep 17 00:00:00 2001 From: Szymon Duchniewicz Date: Wed, 12 Mar 2025 16:29:20 +1100 Subject: [PATCH 20/48] [Do not merge]: Temporary patch to make blk_driver sdmmc work with microkit 2.0 Signed-off-by: Szymon Duchniewicz --- drivers/blk/sdmmc/src/main.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index c93badc39..246a0dbb1 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -1,5 +1,6 @@ #![no_std] // Don't link the standard library #![no_main] // Don't use the default entry point +#![feature(used_with_arg)] extern crate alloc; @@ -22,6 +23,31 @@ use sdmmc_hal::meson_gx_mmc::SdmmcMesonHardware; use sdmmc_protocol::sdmmc::{SdmmcError, SdmmcProtocol}; use sdmmc_protocol::sdmmc_traits::SdmmcHardware; use sel4_microkit::{debug_print, debug_println, protection_domain, Channel, Handler, Infallible}; +/* XXX: Added to work with microkit 2.0 */ + +use sel4_microkit::var; + +#[cfg(not(feature = "extern-symbols"))] +macro_rules! maybe_extern_var { + ($symbol:ident: $ty:ty = $default:expr) => { + var! { + #[used(linker)] + $symbol: $ty = $default + } + }; +} + +#[cfg(feature = "extern-symbols")] +macro_rules! maybe_extern_var { + ($symbol:ident: $ty:ty = $default:expr) => {{ + extern "C" { + static $symbol: $ty; + } + + unsafe { &$symbol } + }}; +} +/* XXX: ^^^ Added to work with microkit 2.0 */ const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(0); const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(1); @@ -69,6 +95,11 @@ fn create_dummy_waker() -> Waker { #[protection_domain(heap_size = 0x10000)] fn init() -> HandlerImpl { +/* XXX: Added to work with microkit 2.0 */ + maybe_extern_var!(microkit_irqs: u64 = 0); + maybe_extern_var!(microkit_notifications: u64 = 0); + maybe_extern_var!(microkit_pps: u64 = 0); +/* XXX: Added to work with microkit 2.0 */ debug_println!("Driver init!"); unsafe { blk_queue_init_helper(); From 5cddeb79ef0d84fe865504a78768580d89df6b21 Mon Sep 17 00:00:00 2001 From: Cheng Date: Thu, 20 Mar 2025 15:42:13 +1100 Subject: [PATCH 21/48] Temporaily test commit for my new fix Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 6 +++-- drivers/blk/sdmmc/src/main.rs | 34 ++++++++++++++++----------- drivers/blk/sdmmc/src/sddf_blk/mod.rs | 6 ++--- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 9dd8ea280..a2102a4f3 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -5,8 +5,10 @@ edition = "2021" [dependencies] sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5826be1a6c83803faeaa79cd9f164c26a5a32e7c" } -sdmmc_hal = { path = "sdmmc_hal/meson" } -sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } +sdmmc_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "f4ae8cd4afc77daf168b309ebd3c583058ff67b0" } +sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "f4ae8cd4afc77daf168b309ebd3c583058ff67b0", features = ["sel4-microkit"] } +# sdmmc_hal = { path = "sdmmc_hal/meson" } +# sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } [build] build = "build.rs" \ No newline at end of file diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 246a0dbb1..953d2bd83 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -52,9 +52,9 @@ macro_rules! maybe_extern_var { const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(0); const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(1); -const SDCARD_SECTOR_SIZE: u16 = 512; -const SDDF_TRANSFER_SIZE: u16 = 4096; -const SDDF_TO_REAL_SECTOR: u16 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE; +const SDCARD_SECTOR_SIZE: u32 = 512; +const SDDF_TRANSFER_SIZE: u32 = 4096; +const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE; const RETRY_CHANCE: u16 = 5; @@ -142,14 +142,16 @@ fn init() -> HandlerImpl { // sdmmc_host.test_read_one_block(0, 0xf5500000); // TODO: Should tuning be possible to fail? - sdmmc_host - .tune_performance(Some(( + unsafe { + sdmmc_host + .tune_performance( unsafe_stolen_memory, - init_data_ioaddr, dummy_cache_invalidate_function, - ))) + init_data_ioaddr, + ) .unwrap_or_else(|error| panic!("SDMMC: Error at tuning performance {:?}", error)); - + } + unsafe { print_one_block(unsafe_stolen_memory.as_ptr(), 64); } @@ -211,9 +213,12 @@ impl Handler for HandlerImpl { let resp_status = BlkStatus::BlkRespOk; notify_virt = true; unsafe { + // The using try_into() to convert u32 to u16 should not be necessary unless + // there are bugs in the code can it fail + // change it later blk_enqueue_resp_helper( resp_status, - request.success_count / SDDF_TO_REAL_SECTOR, + (request.success_count / SDDF_TO_REAL_SECTOR).try_into().unwrap(), request.id, ); } @@ -224,7 +229,7 @@ impl Handler for HandlerImpl { unsafe { blk_enqueue_resp_helper( resp_status, - request.success_count / SDDF_TO_REAL_SECTOR, + (request.success_count / SDDF_TO_REAL_SECTOR).try_into().unwrap(), request.id, ); } @@ -255,18 +260,19 @@ impl Handler for HandlerImpl { count_to_do: 0, id: 0, }; + let mut sddf_count: u16 = 0; unsafe { blk_dequeue_req_helper( &mut request.request_code as *mut BlkOp, &mut request.io_or_offset as *mut u64, &mut request.block_number as *mut u64, - &mut request.count as *mut u16, + &mut sddf_count as *mut u16, &mut request.id as *mut u32, ); } // TODO: Consider how to add integer overflow check here request.block_number = request.block_number * SDDF_TO_REAL_SECTOR as u64; - request.count = request.count * SDDF_TO_REAL_SECTOR; + request.count = (sddf_count as u32) * SDDF_TO_REAL_SECTOR; // Print the retrieved values /* debug_println!("io_or_offset: 0x{:x}", request.io_or_offset);// Simple u64 @@ -309,7 +315,7 @@ impl Handler for HandlerImpl { ); if let Some(sdmmc) = self.sdmmc.take() { self.future = Some(Box::pin(sdmmc.read_block( - request.count_to_do as u32, + request.count_to_do, request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64, @@ -326,7 +332,7 @@ impl Handler for HandlerImpl { ); if let Some(sdmmc) = self.sdmmc.take() { self.future = Some(Box::pin(sdmmc.write_block( - request.count_to_do as u32, + request.count_to_do, request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64, diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs index 3636b46ef..0ef828fa8 100644 --- a/drivers/blk/sdmmc/src/sddf_blk/mod.rs +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -35,9 +35,9 @@ pub struct BlkRequest { pub request_code: BlkOp, pub io_or_offset: u64, pub block_number: u64, - pub count: u16, + pub count: u32, // I suggest use u32 here and change the count to use u32 in sddf_blk - pub success_count: u16, - pub count_to_do: u16, + pub success_count: u32, + pub count_to_do: u32, pub id: u32, } From 3ed37560a106f77ce6fa397653bf2a7d79308bf2 Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 24 Mar 2025 13:35:26 +1100 Subject: [PATCH 22/48] Update sdfgen config file Signed-off-by: Cheng --- examples/blk/meta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/blk/meta.py b/examples/blk/meta.py index 34ec6c9ca..6f637c877 100644 --- a/examples/blk/meta.py +++ b/examples/blk/meta.py @@ -115,7 +115,7 @@ def generate(sdf_file: str, output_dir: str, dtb: DeviceTree): if board.name == "odroidc4": gpio_mr = MemoryRegion("gpio", 0x1000, paddr=0xff800000) - blk_driver.add_map(Map(gpio_mr, 0xff800000, Map.Perms(r=True, w=True), cached=False)) + blk_driver.add_map(Map(gpio_mr, 0xff800000, "rw", cached=False)) sdf.add_mr(gpio_mr) serial_system.add_client(client) From 5b3bd36a770292f86128222154ed5c8c1d580134 Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 24 Mar 2025 14:19:00 +1100 Subject: [PATCH 23/48] Update the driver, pointing crates to my personal branch temporarily Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index a2102a4f3..2bf37a4bc 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" [dependencies] sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5826be1a6c83803faeaa79cd9f164c26a5a32e7c" } -sdmmc_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "f4ae8cd4afc77daf168b309ebd3c583058ff67b0" } -sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "f4ae8cd4afc77daf168b309ebd3c583058ff67b0", features = ["sel4-microkit"] } +sdmmc_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "4381ba2517426ae8de8500e76d556e2d9b9429e3" } +sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "4381ba2517426ae8de8500e76d556e2d9b9429e3", features = ["sel4-microkit"] } # sdmmc_hal = { path = "sdmmc_hal/meson" } # sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } From 791c7af91a50cd09c8784174edf3d932eeb30c84 Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 26 Mar 2025 16:20:49 +1100 Subject: [PATCH 24/48] Upstream the optimizations and fixes for the sdcard driver Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 8 +- .../blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 252 +++-- drivers/blk/sdmmc/sdmmc_protocol/lib.rs | 2 +- drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 869 +++++++++--------- .../sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs | 14 +- .../blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs | 86 ++ .../blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs | 102 +- drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs | 2 +- .../blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs | 121 ++- 9 files changed, 878 insertions(+), 578 deletions(-) create mode 100644 drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 2bf37a4bc..3f875cf3f 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -5,10 +5,10 @@ edition = "2021" [dependencies] sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5826be1a6c83803faeaa79cd9f164c26a5a32e7c" } -sdmmc_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "4381ba2517426ae8de8500e76d556e2d9b9429e3" } -sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "4381ba2517426ae8de8500e76d556e2d9b9429e3", features = ["sel4-microkit"] } -# sdmmc_hal = { path = "sdmmc_hal/meson" } -# sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } +# sdmmc_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "b32423484966e76101683c7231f545fc2296399a" } +# sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "b32423484966e76101683c7231f545fc2296399a", features = ["sel4-microkit"] } +sdmmc_hal = { path = "sdmmc_hal/meson" } +sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } [build] build = "build.rs" \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs index 8cc6f012a..e6996ab6a 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs @@ -1,14 +1,22 @@ use core::ptr; -use sdmmc_protocol::{sdmmc::{ - mmc_struct::{MmcBusWidth, MmcTiming, TuningState}, - sdmmc_capability::{ - MMC_CAP_4_BIT_DATA, MMC_CAP_CMD23, MMC_CAP_VOLTAGE_TUNE, MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS, MMC_VDD_31_32, MMC_VDD_32_33, MMC_VDD_33_34 +use sdmmc_protocol::{ + sdmmc::{ + mmc_struct::{MmcBusWidth, MmcTiming, TuningState}, + sdcard::Sdcard, + sdmmc_capability::{ + MMC_CAP_4_BIT_DATA, MMC_CAP_CMD23, MMC_CAP_VOLTAGE_TUNE, MMC_TIMING_LEGACY, + MMC_TIMING_SD_HS, MMC_TIMING_UHS, MMC_VDD_31_32, MMC_VDD_32_33, MMC_VDD_33_34, + }, + HostInfo, MmcData, MmcDataFlag, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, + SdmmcError, }, - HostInfo, MmcData, MmcDataFlag, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, SdmmcError - }, sdmmc_os::{debug_log, process_wait_unreliable}, sdmmc_traits::SdmmcHardware + sdmmc_os::{debug_log, process_wait_unreliable}, + sdmmc_traits::SdmmcHardware, }; +pub const SDIO_BASE: u64 = 0xffe05000; // Base address from DTS + // The always on gpio pin pull const AO_RTI_PIN_REGION_START: u64 = 0xff800014; const AO_RTI_PIN_REGION_END: u64 = 0xff800038; @@ -88,6 +96,7 @@ const STATUS_MASK: u32 = 0xFFFF; // GENMASK(15, 0) const STATUS_ERR_MASK: u32 = 0x1FFF; // GENMASK(12, 0) const STATUS_RXD_ERR_MASK: u32 = 0xFF; // GENMASK(7, 0) const STATUS_TXD_ERR: u32 = 1 << 8; // BIT(8) +const STATUS_EIO_ERR: u32 = STATUS_RXD_ERR_MASK | STATUS_TXD_ERR; // GENMASK(8, 0) const STATUS_DESC_ERR: u32 = 1 << 9; // BIT(9) const STATUS_RESP_ERR: u32 = 1 << 10; // BIT(10) const STATUS_RESP_TIMEOUT: u32 = 1 << 11; // BIT(11) @@ -118,7 +127,7 @@ const IRQ_EN_MASK: u32 = IRQ_ERR_MASK | IRQ_END_OF_CHAIN; const MESON_SDCARD_SECTOR_SIZE: u32 = 512; -pub const MAX_BLOCK_PER_TRANSFER: u16 = 0x1FF; +pub const MAX_BLOCK_PER_TRANSFER: u32 = 0x1FF; const WRITE_ADDR_UPPER: u32 = 0xFFFE0000; @@ -180,30 +189,33 @@ struct MesonSdmmcRegisters { } impl MesonSdmmcRegisters { - /// This function is unsafe because it tries to - unsafe fn new(base: u64) -> &'static mut MesonSdmmcRegisters { - &mut *(base as *mut MesonSdmmcRegisters) + /// This function is only safe to use if the sdmmc_register_base is the correct memory addr + /// of the sdmmc register base and accessible for the driver + unsafe fn new(sdmmc_register_base: u64) -> &'static mut MesonSdmmcRegisters { + &mut *(sdmmc_register_base as *mut MesonSdmmcRegisters) } } struct DelayConfig { timing: MmcTiming, // Clock rate in Hz current_delay: u32, // Delay value in some unit, e.g., nanoseconds - tried_lowest_delay: u32, - tried_highest_delay: u32, } pub struct SdmmcMesonHardware { register: &'static mut MesonSdmmcRegisters, delay: Option, + // Current timing timing: MmcTiming, + // Current frequency frequency: u32, + // Irq enabled + enabled_irq: u32, // Put other variables here } impl SdmmcMesonHardware { - pub unsafe fn new(base: u64) -> Self { - let register = MesonSdmmcRegisters::new(base); + pub unsafe fn new(sdmmc_register_base: u64) -> Self { + let register = MesonSdmmcRegisters::new(sdmmc_register_base); // TODO: Call reset function here SdmmcMesonHardware { @@ -213,14 +225,13 @@ impl SdmmcMesonHardware { timing: MmcTiming::SdHs, // Wrong value but should not have much impact frequency: MESON_MIN_FREQUENCY, + enabled_irq: 0, } } /// The meson_reset function reset the host register state /// However, this function does not try to reset the power state like operating voltage and signal voltage fn meson_reset(&mut self) { - let _ = self.sdmmc_set_power(MmcPowerMode::On); - // Stop execution unsafe { ptr::write_volatile(&mut self.register.start, 0); @@ -248,9 +259,7 @@ impl SdmmcMesonHardware { } // Set clock to a low freq - if self.sdmmc_config_timing(MmcTiming::CardSetup).is_err() { - panic!("Fatal fault in setting frequency when resetting"); - } + let _ = self.sdmmc_config_timing(MmcTiming::CardSetup); // Reset config register let mut cfg: u32 = 0; @@ -271,6 +280,8 @@ impl SdmmcMesonHardware { unsafe { ptr::write_volatile(&mut self.register.cfg, cfg); } + + self.delay = None; } // This function can be seen as a Rust version of meson_mmc_setup_cmd function in uboot @@ -284,6 +295,9 @@ impl SdmmcMesonHardware { meson_mmc_cmd |= CMD_CFG_RESP_128; } + // If the hardware does not have busy detection, polling for card datalines to be high is needed + // as the card will signal "busy" (by pulling the DAT line low) + // Odroid C4 have feature to wait until hardware exit busy state so we do not need to worry about it if cmd.resp_type & MMC_RSP_BUSY != 0 { meson_mmc_cmd |= CMD_CFG_R1B; } @@ -401,7 +415,10 @@ impl SdmmcMesonHardware { fn meson_wait_desc_stop(&self) -> Result<(), SdmmcError> { let mut start_time: u32 = 0; - while unsafe { ptr::read_volatile(&self.register.status) } & (STATUS_DESC_BUSY | STATUS_BUSY) != 0 { + while unsafe { ptr::read_volatile(&self.register.status) } + & (STATUS_DESC_BUSY | STATUS_BUSY) + != 0 + { if start_time > Self::DESC_STOP_TIMEOUT_NS { return Err(SdmmcError::EUNDEFINED); } @@ -427,10 +444,6 @@ impl SdmmcHardware for SdmmcMesonHardware { let ios: MmcIos = MmcIos { clock: MESON_MIN_FREQUENCY as u64, - // On odroid c4, the operating voltage is default to 3.3V - vdd: (MMC_VDD_33_34 | MMC_VDD_32_33 | MMC_VDD_31_32), - // TODO, figure out the correct value when we can power the card on and off - power_delay_ms: 10, power_mode: MmcPowerMode::On, bus_width: MmcBusWidth::Width1, signal_voltage: MmcSignalVoltage::Voltage330, @@ -443,33 +456,29 @@ impl SdmmcHardware for SdmmcMesonHardware { max_frequency: MESON_MAX_FREQUENCY as u64, min_frequency: MESON_MIN_FREQUENCY as u64, max_block_per_req: MAX_BLOCK_PER_TRANSFER, + // On odroid c4, the operating voltage is default to 3.3V + vdd: (MMC_VDD_33_34 | MMC_VDD_32_33 | MMC_VDD_31_32), + // TODO, figure out the correct value when we can power the card on and off + power_delay_ms: 5, }; return Ok((ios, info, cap)); } - fn sdmmc_tune_sampling(&mut self, state: TuningState) -> Result<(), SdmmcError> { - match state { - TuningState::TuningStart => { - if let Some(ref mut delay_config) = self.delay { - delay_config.tried_highest_delay = delay_config.current_delay; - delay_config.tried_lowest_delay = delay_config.current_delay; - } - return Ok(()); + fn sdmmc_execute_tuning(&mut self, memory: *mut [u8; 64]) -> Result<(), SdmmcError> { + let mut current_delay: u32 = 0; + + if let Some(ref config) = self.delay { + if config.timing == self.timing { + current_delay = config.current_delay; } - TuningState::TuningContinue => (), - TuningState::TuningComplete => return Ok(()), - } - - let mut delay_config = match self.delay.take() { - Some(config) if config.timing == self.timing => config, - _ => DelayConfig { - timing: self.timing, - current_delay: 0, - tried_lowest_delay: 0, - tried_highest_delay: 0, - }, - }; + } + // If there is no existing delay config, clear the adjust register anyway + else { + unsafe { + ptr::write_volatile(&mut self.register.adjust, 0); + } + } let mut adjust: u32 = SD_EMMC_ADJ_ENABLE; let clk: u32; @@ -488,28 +497,61 @@ impl SdmmcHardware for SdmmcMesonHardware { let max_div: u32 = div_round_up!(mux_clk_freq, self.frequency); - if max_div - delay_config.tried_highest_delay >= delay_config.tried_lowest_delay { - if max_div - delay_config.tried_highest_delay == 0 { - return Err(SdmmcError::EUNSUPPORTEDCARD); + let mut tried_lowest_delay: u32 = current_delay; + + let mut tried_highest_delay: u32 = current_delay; + + loop { + debug_log!( + "current delay: {}, lowest_tried: {}, highest_tried: {}\n", + current_delay, + tried_lowest_delay, + tried_highest_delay + ); + let res: Result<(), SdmmcError> = Sdcard::sdcard_test_tuning(self, memory); + + match res { + Ok(_) => { + self.delay = Some(DelayConfig { + timing: self.timing, + current_delay, + }); + break Ok(()); + } + Err(SdmmcError::EIO) => {} + Err(_) => { + self.delay = None; + unsafe { + ptr::write_volatile(&mut self.register.adjust, 0); + } + break Err(SdmmcError::EUNSUPPORTEDCARD); + } } - delay_config.tried_highest_delay += 1; - delay_config.current_delay = delay_config.tried_highest_delay; - } else { - delay_config.tried_lowest_delay -= 1; - delay_config.current_delay = delay_config.tried_lowest_delay; - } - adjust |= (delay_config.current_delay << SD_EMMC_ADJ) & SD_EMMC_ADJUST_ADJ_DELAY_MASK; + if max_div - tried_highest_delay == 0 { + self.delay = None; + unsafe { + ptr::write_volatile(&mut self.register.adjust, 0); + } + break Err(SdmmcError::EUNSUPPORTEDCARD); + } - debug_log!("Tuning sampling function: Current delay: {}, tried lowest delay: {}, tried highest delay: {}, final register value: 0x{:08x}\n", delay_config.current_delay, delay_config.tried_lowest_delay, delay_config.tried_highest_delay, adjust); + // The tuning process start from the current delay in the middle + // then approach both ends + if max_div - tried_highest_delay >= tried_lowest_delay { + tried_highest_delay += 1; + current_delay = tried_highest_delay; + } else { + tried_lowest_delay -= 1; + current_delay = tried_lowest_delay; + } - self.delay = Some(delay_config); + adjust |= (current_delay << SD_EMMC_ADJ) & SD_EMMC_ADJUST_ADJ_DELAY_MASK; - unsafe { - ptr::write_volatile(&mut self.register.adjust, adjust); + unsafe { + ptr::write_volatile(&mut self.register.adjust, adjust); + } } - - Ok(()) } fn sdmmc_config_timing(&mut self, timing: MmcTiming) -> Result { @@ -559,7 +601,7 @@ impl SdmmcHardware for SdmmcMesonHardware { * If CLK_CO_PHASE_270 is used, it's more stable than other. * Other SoCs use CLK_CO_PHASE_180 by default. * Linux default to use CLK_CO_PHASE_180 - * However, if CLK_CO_PHASE_180 is used without tuning, + * However, if CLK_CO_PHASE_180 is used without tuning, * Sdcard will not work in High speed mode on Odroid C4 */ meson_mmc_clk |= CLK_CO_PHASE_180; @@ -632,7 +674,7 @@ impl SdmmcHardware for SdmmcMesonHardware { // TODO: Check what if the addr is u32::MAX, will the sdcard still working? if mmc_data.addr >= (WRITE_ADDR_UPPER as u64) || mmc_data.blockcnt == 0 - || mmc_data.blockcnt > MAX_BLOCK_PER_TRANSFER as u32 + || mmc_data.blockcnt > MAX_BLOCK_PER_TRANSFER { debug_log!("SDMMC: INVALID INPUT VARIABLE!"); return Err(SdmmcError::EINVAL); @@ -684,34 +726,52 @@ impl SdmmcHardware for SdmmcMesonHardware { return Err(SdmmcError::EBUSY); } - if (status & STATUS_RESP_TIMEOUT) != 0 { - debug_log!( - "SDMMC: CARD TIMEOUT! Host status register: 0x{:08x}\n", - status - ); - // This could negatively impact the result of benchmarking in case of cmd error - self.meson_wait_desc_stop()?; - return Err(SdmmcError::ETIMEDOUT); - } - - let mut return_val: Result<(), SdmmcError> = Ok(()); + self.meson_read_response(cmd, response); if (status & STATUS_ERR_MASK) != 0 { + // For debug + debug_log!("SDMMC: Print out error request:\n"); + debug_log!("cmd idx: {}\n", cmd.cmdidx); + debug_log!("cmd arg: 0x{:x}\n", cmd.cmdarg); + + debug_log!("Odroidc4 status register: 0x{:08}\n", status); + + debug_log!("Card's first response register: 0x{:08}\n", response[0]); + + if (status & STATUS_RESP_TIMEOUT) != 0 { + debug_log!("SDMMC: CARD TIMEOUT!\n"); + + // The card will try to polling the status register until + // both descriptor and card are not in busy state + self.meson_wait_desc_stop()?; + return Err(SdmmcError::ETIMEDOUT); + } + + if (status & STATUS_EIO_ERR) != 0 { + debug_log!("SDMMC: CARD IO ERROR! Perform retuning\n"); + + self.meson_wait_desc_stop()?; + // Notified the card to retune the card + return Err(SdmmcError::EIO); + } + debug_log!( - "SDMMC: CARD IO ERROR! Host status register: 0x{:08x}\n", - status + "SDMMC: Unknown error, copy the prints from card init to end of the print and send it to me\n" ); + self.meson_wait_desc_stop()?; - return_val = Err(SdmmcError::EIO); + return Err(SdmmcError::EUNKNOWN); } - self.meson_read_response(cmd, response); - - return_val + Ok(()) } /// This function is meant to clear, acknowledge and then reenable the interrupt - fn sdmmc_config_interrupt(&mut self, enable_irq: bool, enable_sdio_irq: bool) -> Result<(), SdmmcError> { + fn sdmmc_config_interrupt( + &mut self, + enable_irq: bool, + enable_sdio_irq: bool, + ) -> Result<(), SdmmcError> { // Disable interrupt unsafe { ptr::write_volatile(&mut self.register.irq_en, 0); @@ -731,12 +791,15 @@ impl SdmmcHardware for SdmmcMesonHardware { unsafe { ptr::write_volatile(&mut self.register.irq_en, irq_bits_to_set); } + self.enabled_irq = irq_bits_to_set; return Ok(()); } fn sdmmc_ack_interrupt(&mut self) -> Result<(), SdmmcError> { - unsafe { - ptr::write_volatile(&mut self.register.status, IRQ_END_OF_CHAIN | IRQ_ERR_MASK | IRQ_SDIO); + if self.enabled_irq != 0 { + unsafe { + ptr::write_volatile(&mut self.register.status, self.enabled_irq); + } } return Ok(()); } @@ -792,12 +855,11 @@ impl SdmmcHardware for SdmmcMesonHardware { Ok(()) } - // Experimental function that tries to modify the pin that might control the power of the sdcard slot // This function should be pretty much the same with sdmmc_set_signal_voltage, the only difference // is the pin modified is gpioAO_3, busy wait is needed to be added after setting the power - fn sdmmc_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { - /* + fn sdmmc_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { + /* unsafe { debug_log!("In set power function\n"); let mut value: u32; @@ -810,7 +872,7 @@ impl SdmmcHardware for SdmmcMesonHardware { debug_log!("Address {:#x}: {:#x}\n", AO_RTI_OUTPUT_LEVEL_REG, value); value = ptr::read_volatile(AO_RTI_PULL_UP_EN_REG as *const u32); - debug_log!("Address {:#x}: {:#x}\n", AO_RTI_PULL_UP_EN_REG, value); + debug_log!("Address {:#x}: {:#x}\n", AO_RTI_PULL_UP_EN_REG, value); } */ @@ -853,4 +915,20 @@ impl SdmmcHardware for SdmmcMesonHardware { } Ok(()) } + + fn sdmmc_host_reset(&mut self) -> Result { + Self::meson_reset(self); + + let ios: MmcIos = MmcIos { + clock: MESON_MIN_FREQUENCY as u64, + power_mode: MmcPowerMode::On, + bus_width: MmcBusWidth::Width1, + signal_voltage: MmcSignalVoltage::Voltage330, + enabled_irq: false, + emmc: None, + spi: None, + }; + + Ok(ios) + } } diff --git a/drivers/blk/sdmmc/sdmmc_protocol/lib.rs b/drivers/blk/sdmmc/sdmmc_protocol/lib.rs index 07b0cb627..f102407e0 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/lib.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/lib.rs @@ -1,5 +1,5 @@ #![no_std] // Don't link the standard library pub mod sdmmc; -pub mod sdmmc_traits; pub mod sdmmc_os; +pub mod sdmmc_traits; diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs index fc76324a9..2428e1274 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -1,18 +1,32 @@ use core::{ future::Future, pin::Pin, + sync::atomic::Ordering, task::{Context, Poll, Waker}, }; -use mmc_struct::{MmcBusWidth, MmcDevice, MmcState, MmcTiming, TuningState}; -use sdcard::{Cid, Csd, Sdcard}; +use mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcDevice, MmcState, MmcTiming, TuningState}; +use sdcard::{Cid, Csd, Scr, Sdcard}; use sdmmc_capability::{ SdcardCapability, SdmmcHostCapability, MMC_CAP_4_BIT_DATA, MMC_CAP_VOLTAGE_TUNE, MMC_EMPTY_CAP, MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS_DDR50, MMC_TIMING_UHS_SDR104, - MMC_TIMING_UHS_SDR12, MMC_TIMING_UHS_SDR25, MMC_TIMING_UHS_SDR50 + MMC_TIMING_UHS_SDR12, MMC_TIMING_UHS_SDR25, MMC_TIMING_UHS_SDR50, }; use sdmmc_constant::{ - MMC_CMD_ALL_SEND_CID, MMC_CMD_APP_CMD, MMC_CMD_ERASE, MMC_CMD_GO_IDLE_STATE, MMC_CMD_READ_MULTIPLE_BLOCK, MMC_CMD_READ_SINGLE_BLOCK, MMC_CMD_SELECT_CARD, MMC_CMD_SEND_CSD, MMC_CMD_SET_BLOCK_COUNT, MMC_CMD_STOP_TRANSMISSION, MMC_CMD_WRITE_MULTIPLE_BLOCK, MMC_CMD_WRITE_SINGLE_BLOCK, OCR_BUSY, OCR_HCS, OCR_S18R, SD_CMD_APP_SEND_OP_COND, SD_CMD_APP_SET_BUS_WIDTH, SD_CMD_ERASE_WR_BLK_END, SD_CMD_ERASE_WR_BLK_START, SD_CMD_SEND_IF_COND, SD_CMD_SEND_RELATIVE_ADDR, SD_CMD_SWITCH_FUNC, SD_CMD_SWITCH_UHS18V, SD_ERASE_ARG, SD_SWITCH_FUNCTION_GROUP_ONE, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_SDHS, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR12, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR25, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR50, SD_SWITCH_FUNCTION_GROUP_ONE_SET_LEGACY, SD_SWITCH_FUNCTION_GROUP_ONE_SET_SDHS, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_DDR50, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR104, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR12, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR25, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR50, SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE + MMC_CMD_ALL_SEND_CID, MMC_CMD_APP_CMD, MMC_CMD_ERASE, MMC_CMD_GO_IDLE_STATE, + MMC_CMD_READ_MULTIPLE_BLOCK, MMC_CMD_READ_SINGLE_BLOCK, MMC_CMD_SELECT_CARD, MMC_CMD_SEND_CSD, + MMC_CMD_SET_BLOCK_COUNT, MMC_CMD_STOP_TRANSMISSION, MMC_CMD_WRITE_MULTIPLE_BLOCK, + MMC_CMD_WRITE_SINGLE_BLOCK, OCR_BUSY, OCR_HCS, OCR_S18R, SD_CMD_APP_SEND_OP_COND, + SD_CMD_APP_SET_BUS_WIDTH, SD_CMD_ERASE_WR_BLK_END, SD_CMD_ERASE_WR_BLK_START, + SD_CMD_SEND_IF_COND, SD_CMD_SEND_RELATIVE_ADDR, SD_CMD_SWITCH_FUNC, SD_CMD_SWITCH_UHS18V, + SD_ERASE_ARG, SD_SWITCH_FUNCTION_GROUP_ONE, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_SDHS, + SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104, + SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR12, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR25, + SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR50, SD_SWITCH_FUNCTION_GROUP_ONE_SET_LEGACY, + SD_SWITCH_FUNCTION_GROUP_ONE_SET_SDHS, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_DDR50, + SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR104, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR12, + SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR25, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR50, + SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE, }; use crate::sdmmc_traits::SdmmcHardware; @@ -20,7 +34,8 @@ use crate::sdmmc_traits::SdmmcHardware; use crate::sdmmc_os::{debug_log, process_wait_unreliable}; pub mod mmc_struct; -mod sdcard; +pub mod sd_ops; +pub mod sdcard; pub mod sdmmc_capability; mod sdmmc_constant; @@ -51,6 +66,8 @@ pub enum SdmmcError { ETIMEDOUT, EINVAL, EIO, + // Error that I currently not sure how to deal with... + EUNKNOWN, EUNSUPPORTEDCARD, ENOTIMPLEMENTED, // This error should not be triggered unless there are bugs in program @@ -170,23 +187,6 @@ pub struct MmcIos { /// data transfer occurs at higher rates. pub clock: u64, - /// The voltage range (VDD) used for powering the SD/MMC card. - /// - /// - This field stores the selected voltage range in a bit-encoded format. - /// It indicates the voltage level the card is operating at. - /// - Common voltage levels are 3.3V, 1.8V, and sometimes 1.2V (for eMMC). - /// - Cards often negotiate their operating voltage during initialization. - pub vdd: u32, - - /// The power delay (in milliseconds) used after powering the card to ensure - /// stable operation. - /// - /// - After powering up the card, the host controller typically waits for a - /// certain period before initiating communication to ensure that the card's - /// power supply is stable. - /// - This delay ensures the card is ready to respond to commands. - pub power_delay_ms: u32, - /// The current power supply mode for the SD/MMC card. /// /// - This field indicates whether the card is powered on, powered off, or @@ -237,7 +237,23 @@ pub struct MmcIos { pub struct HostInfo { pub max_frequency: u64, pub min_frequency: u64, - pub max_block_per_req: u16, + pub max_block_per_req: u32, + /// The voltage range (VDD) used for powering the SD/MMC card. + /// + /// - This field stores the selected voltage range in a bit-encoded format. + /// It indicates the voltage level the card is operating at. + /// - Common voltage levels are 3.3V, 1.8V, and sometimes 1.2V (for eMMC). + /// - Cards often negotiate their operating voltage during initialization. + pub vdd: u32, + + /// The power delay (in milliseconds) used after powering the card to ensure + /// stable operation. + /// + /// - After powering up the card, the host controller typically waits for a + /// certain period before initiating communication to ensure that the card's + /// power supply is stable. + /// - This delay ensures the card is ready to respond to commands. + pub power_delay_ms: u32, } /// TODO: Add more variables for SdmmcProtocol to track the state of the sdmmc controller and card correctly @@ -252,6 +268,8 @@ pub struct SdmmcProtocol { /// This mmc device is optional because there may not always be a card in the slot! mmc_device: Option, + + private_memory: Option<*mut [u8; 64]>, } impl Unpin for SdmmcProtocol where T: Unpin + SdmmcHardware {} @@ -266,6 +284,7 @@ impl SdmmcProtocol { host_info: info, host_capability: SdmmcHostCapability(cap), mmc_device: None, + private_memory: None, }) } @@ -294,7 +313,7 @@ impl SdmmcProtocol { cmdarg: 0x000001AA, // Voltage supply and check pattern }; - let res = Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp); + let res: Result<(), SdmmcError> = self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1); // If the result is OK and the resp is 0x1AA, the card we are initializing is a SDHC/SDXC // If the result is error, it is either the voltage not being set up correctly, which mean a bug in hardware layer @@ -317,10 +336,10 @@ impl SdmmcProtocol { }; // Send CMD55 - let res = Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp); - + let res = self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0); + match res { - Ok(_) => {}, + Ok(_) => {} Err(SdmmcError::ETIMEDOUT) => return Err(SdmmcError::EUNSUPPORTEDCARD), Err(_) => return res, } @@ -338,7 +357,7 @@ impl SdmmcProtocol { // Right now we deliberately not set XPC bit for maximum compatibility // Change this when we decide to support spi or SDSC as well - cmd.cmdarg |= self.mmc_ios.vdd & 0xff8000; + cmd.cmdarg |= self.host_info.vdd & 0xff8000; if self .host_capability @@ -352,7 +371,7 @@ impl SdmmcProtocol { } // Send ACMD41 - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; debug_log!("OCR: {:08x}\n", resp[0]); @@ -371,20 +390,23 @@ impl SdmmcProtocol { // Checking if the host and card is eligible for voltage switch if self - .host_capability - .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) && voltage_switch == true - && resp[0] & OCR_HCS == OCR_HCS && resp[0] & OCR_S18R == OCR_S18R { + .host_capability + .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) + && voltage_switch == true + && resp[0] & OCR_HCS == OCR_HCS + && resp[0] & OCR_S18R == OCR_S18R + { // TODO: If the sdcard fail at this stage, a power circle and reinit should be performed self.tune_sdcard_switch_uhs18v()?; self.mmc_ios.signal_voltage = MmcSignalVoltage::Voltage180; } - + Ok(()) } fn sdmmc_power_cycle(&mut self) -> Result<(), SdmmcError> { self.hardware.sdmmc_set_power(MmcPowerMode::Off)?; - process_wait_unreliable(5_000_000); + process_wait_unreliable(self.host_info.power_delay_ms as u64 * 1_000_000); self.hardware.sdmmc_set_power(MmcPowerMode::On)?; process_wait_unreliable(1_000_000); @@ -405,7 +427,7 @@ impl SdmmcProtocol { // TODO: Right now we know the power will always be up and this function should not be called // But when we encounter scenerio that may actually call this function, we should wait for the time specified in ios // Right now this whole power up related thing does not work - process_wait_unreliable(self.mmc_ios.power_delay_ms as u64 * 1000000); + process_wait_unreliable(self.host_info.power_delay_ms as u64 * 1_000_000); self.mmc_ios.power_mode = MmcPowerMode::On; } @@ -425,28 +447,35 @@ impl SdmmcProtocol { // There could be more complex retry logic implemented in the future 'sdcard_init: loop { match self.sdcard_init(voltage_switch) { - Ok(()) => {}, + Ok(()) => {} Err(SdmmcError::EUNSUPPORTEDCARD) => { - break 'sdcard_init SdmmcError::EUNSUPPORTEDCARD; - }, + break 'sdcard_init; + } Err(_) => { if voltage_switch == true { voltage_switch = false; + // Retry with voltage switch off debug_log!("Try to init the card without voltage switch\n"); self.sdmmc_power_cycle()?; + + self.mmc_ios = self.hardware.sdmmc_host_reset()?; + + // One bug that does not break anything here is + // sdmmc_host_reset will reset the clock to CardSetup timing and turn off the irq + // But those variable in mmc_ios is not changed accordingly + continue 'sdcard_init; - } - else { - break 'sdcard_init SdmmcError::ECARDINACTIVE; + } else { + break 'sdcard_init; } } } - + let card: Sdcard = self.setup_sdcard_cont()?; self.mmc_device = Some(MmcDevice::Sdcard(card)); return Ok(()); - }; + } // Unsupported card { @@ -475,7 +504,7 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R2, cmdarg: 0, }; - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1)?; let cid: Cid = Cid::new(resp); @@ -500,7 +529,7 @@ impl SdmmcProtocol { cmdarg: 0, }; - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1)?; let rca: u16 = (resp[0] >> 16) as u16; // Store RCA from response @@ -512,7 +541,8 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R2, cmdarg: (rca as u32) << 16, }; - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1)?; debug_log!( "CSD: {:08x} {:08x} {:08x} {:08x}\n", @@ -530,7 +560,8 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R1, cmdarg: (rca as u32) << 16, }; - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1)?; // SDHC/SDXC default to 512 bytes sector size so I did not manually set it here @@ -550,6 +581,7 @@ impl SdmmcProtocol { relative_card_addr: rca, card_state, card_cap: sdmmc_capability::SdcardCapability(MMC_EMPTY_CAP), + method: BlockTransmissionMode::StopTransmission, card_config: None, }) } @@ -559,6 +591,8 @@ impl SdmmcProtocol { /// Do NOT call this function again if your card is already tuned as this function is not that cheap! /// But you should call this function the card being turned into power saving mode /// `stolen_memory` is a specific memory region used to read data from the SD card through CMD6. + /// To use this function safely, the memory pointer passed in must be from a valid memory address + /// And must match the physical memory address that is being DMA into /// /// ### Important Considerations: /// @@ -597,9 +631,12 @@ impl SdmmcProtocol { /// /// # Returns /// - `Result<(), SdmmcError>`: `Ok(())` if tuning was successful, or an error otherwise. - pub fn tune_performance( + /// UB here waiting to be fixed: memory being a mutable reference is also accessed by DMA + pub unsafe fn tune_performance( &mut self, - memory_and_invalidate_cache_fn: Option<(&mut [u8; 64], u64, fn())>, + memory: *mut [u8; 64], + cache_invalidate_function: fn(), + physical_memory_addr: u64, ) -> Result<(), SdmmcError> { // For testing let mmc_device = self.mmc_device.as_mut().ok_or(SdmmcError::ENOCARD)?; @@ -607,10 +644,20 @@ impl SdmmcProtocol { // Turn down the clock frequency self.mmc_ios.clock = self.hardware.sdmmc_config_timing(MmcTiming::CardSetup)?; + // The private memory should be the physically memory + // However any attempt to dereference the pointer + // could crash the program if the driver don't have access + // to the phyical memory at the proper address + self.private_memory = Some(physical_memory_addr as *mut [u8; 64]); + match mmc_device { MmcDevice::Sdcard(sdcard) => { sdcard.card_state.timing = MmcTiming::CardSetup; - self.tune_sdcard_performance(memory_and_invalidate_cache_fn) + self.tune_sdcard_performance( + memory, + cache_invalidate_function, + physical_memory_addr, + ) } MmcDevice::EMmc(emmc) => Err(SdmmcError::ENOTIMPLEMENTED), MmcDevice::Unknown => Err(SdmmcError::ENOTIMPLEMENTED), @@ -621,22 +668,6 @@ impl SdmmcProtocol { self.host_info.clone() } - /// Helper function to print out the content of one block - #[allow(dead_code)] - unsafe fn print_one_block(ptr: *const u8, num: usize) { - unsafe { - // Iterate over the number of bytes and print each one in hexadecimal format - for i in 0..num { - let byte = *ptr.add(i); - if i % 16 == 0 { - debug_log!("\n{:04x}: ", i); - } - debug_log!("{:02x} ", byte); - } - debug_log!("\n"); - } - } - pub fn test_read_one_block(&mut self, start_idx: u64, destination: u64) { let data: MmcData = MmcData { blocksize: 512, @@ -652,68 +683,13 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R1, cmdarg: cmd_arg as u32, }; - if let Err(error) = - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, Some(&data), &mut resp) + if let Err(error) = self + .hardware + .sdmmc_do_request(&cmd, Some(&data), &mut resp, 0) { debug_log!("Error in reading\n"); } - unsafe { Self::print_one_block(destination as *mut u8, 512) }; - } - - fn test_sampling( - &mut self, - memory_addr: u64, - cache_invalidate_function: fn(), - ) -> Result<(), SdmmcError> { - let mut resp: [u32; 4] = [0; 4]; - - let data = MmcData { - blocksize: 64, - blockcnt: 1, - flags: MmcDataFlag::SdmmcDataRead, - addr: memory_addr, - }; - - let cmd = SdmmcCmd { - cmdidx: SD_CMD_SWITCH_FUNC, - resp_type: MMC_RSP_R1, - cmdarg: 0x00FFFFFF, - }; - - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, Some(&data), &mut resp) - // TODO: Add validate the content of data returned here, but it is optional because - // if there is not error it must has passed the CRC check! - } - - fn process_sampling( - &mut self, - memory_addr: u64, - cache_invalidate_function: fn(), - ) -> Result<(), SdmmcError> { - self.hardware - .sdmmc_tune_sampling(TuningState::TuningStart)?; - loop { - // Call test_sampling - match self.test_sampling(memory_addr, cache_invalidate_function) { - Ok(_) => { - self.hardware - .sdmmc_tune_sampling(TuningState::TuningComplete)?; - // If test_sampling succeeds, return Ok - return Ok(()); - } - Err(SdmmcError::EIO) => { - // If test_sampling fails with EIO, try tuning the sampling point - debug_log!("Try different sampling points\n"); - self.hardware - .sdmmc_tune_sampling(TuningState::TuningContinue)?; - } - Err(e) => { - // If test_sampling fails with an error other than EIO, return that error - debug_log!("Tuning sampling fails\n"); - return Err(e); - } - } - } + unsafe { print_one_block(destination as *mut u8, 512) }; } /// Switch voltage function @@ -725,20 +701,22 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R1, cmdarg: 0, // Argument for 4-bit mode (0 for 1-bit mode) }; - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + + self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; + debug_log!("Switch voltage prepared!\n"); self.mmc_ios.clock = self.hardware.sdmmc_config_timing(MmcTiming::ClockStop)?; // TODO: figuring out the optimal delay - process_wait_unreliable(100000); + process_wait_unreliable(100_000); let mut signal: u8 = 0xFF; for _ in 0..100 { signal = self.hardware.sdmmc_read_datalanes()?; // TODO: figuring out the optimal delay - process_wait_unreliable(100000); + process_wait_unreliable(100_000); debug_log!("data signal value: 0b{:b}\n", signal); if signal & 0xF == 0x0 { break; @@ -771,7 +749,8 @@ impl SdmmcProtocol { } if signal & 0xF != 0xF { - self.hardware.sdmmc_set_signal_voltage(MmcSignalVoltage::Voltage330)?; + self.hardware + .sdmmc_set_signal_voltage(MmcSignalVoltage::Voltage330)?; process_wait_unreliable(1_000_000); return Err(SdmmcError::ECARDINACTIVE); } @@ -785,18 +764,18 @@ impl SdmmcProtocol { /// Implement this sdcard switch function to avoid this hackiness! /// Like first get the speed classes the sdcard support by this function /// and then switch to the proper speed class! - fn sdcard_switch_speed( + unsafe fn sdcard_switch_speed( &mut self, target: MmcTiming, - memory: &mut [u8; 64], - memory_ioaddr: u64, + raw_memory: *mut [u8; 64], invalidate_cache_fn: fn(), + physical_memory_addr: u64, ) -> Result<(), SdmmcError> { let data: MmcData = MmcData { blocksize: 64, blockcnt: 1, flags: MmcDataFlag::SdmmcDataRead, - addr: memory_ioaddr, + addr: physical_memory_addr, }; let mut resp: [u32; 4] = [0; 4]; @@ -818,52 +797,69 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R1, cmdarg, }; - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, Some(&data), &mut resp)?; + self.hardware + .sdmmc_do_request(&cmd, Some(&data), &mut resp, 0)?; + + // The use of fence here is actually wrong + // As the fence(Ordering::Acquire) on arm platform + // The cache maintenance instructions are not ordered + // by the Load-Acquire and Store-Release instructions + // But I will just leave it here as I cannot figure out + // A more elegant way and code works fine anyway + core::sync::atomic::fence(Ordering::Acquire); + // Error handling here invalidate_cache_fn(); + // Since we are using u8 here, the endianness does matter // 0xF indicate function switch error - // TODO: Double check here and deliberately trigger swithc error to see if the field + // TODO: Double check here and deliberately trigger switch error to see if the field // is being set to 0xF - if memory[SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE] & 0xF == 0xF { + if unsafe { (*raw_memory)[SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE] & 0xF == 0xF } { return Err(SdmmcError::EINVAL); } Ok(()) } - fn sdcard_check_supported_speed_class( + /// Unsafe because trying to dereference raw pointer + unsafe fn sdcard_check_supported_speed_class( &mut self, - memory: &mut [u8; 64], - memory_ioaddr: u64, + raw_memory: *mut [u8; 64], invalidate_cache_fn: fn(), + physical_memory_addr: u64, ) -> Result<(), SdmmcError> { let data: MmcData = MmcData { blocksize: 64, blockcnt: 1, flags: MmcDataFlag::SdmmcDataRead, - addr: memory_ioaddr, + addr: physical_memory_addr, }; let mut resp: [u32; 4] = [0; 4]; let mut card_cap: SdcardCapability = sdmmc_capability::SdcardCapability(MMC_EMPTY_CAP); - let cmd = SdmmcCmd { + let cmd: SdmmcCmd = SdmmcCmd { cmdidx: SD_CMD_SWITCH_FUNC, resp_type: MMC_RSP_R1, cmdarg: 0x00FFFFFF, }; - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, Some(&data), &mut resp)?; + + self.hardware + .sdmmc_do_request(&cmd, Some(&data), &mut resp, 0)?; + + core::sync::atomic::fence(Ordering::Acquire); + invalidate_cache_fn(); // Typical speed class supported: 80 03 match self.mmc_ios.signal_voltage { MmcSignalVoltage::Voltage330 => { - let speed_class_byte: u8 = memory[SD_SWITCH_FUNCTION_GROUP_ONE]; + let speed_class_byte: u8 = unsafe { (*raw_memory)[SD_SWITCH_FUNCTION_GROUP_ONE] }; if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_SDHS != 0 { card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_SD_HS)); } } MmcSignalVoltage::Voltage180 => { - let speed_class_byte: u8 = memory[SD_SWITCH_FUNCTION_GROUP_ONE]; + let speed_class_byte: u8 = unsafe { (*raw_memory)[SD_SWITCH_FUNCTION_GROUP_ONE] }; if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR12 != 0 { card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_SDR12)); } @@ -893,12 +889,33 @@ impl SdmmcProtocol { } /// Since we are using u8 here, the endianness does matter + /// I should change the process of access raw memory to using volatile read fn tune_sdcard_performance( &mut self, - memory_and_invalidate_cache_fn: Option<(&mut [u8; 64], u64, fn())>, + memory: *mut [u8; 64], + cache_invalidate_function: fn(), + physical_memory_addr: u64, ) -> Result<(), SdmmcError> { let mut resp: [u32; 4] = [0; 4]; + if let Some(MmcDevice::Sdcard(ref mut sdcard)) = &mut self.mmc_device { + let scr: Scr = unsafe { + Sdcard::sdcard_get_configuration_register( + &mut self.hardware, + physical_memory_addr, + memory, + cache_invalidate_function, + sdcard.relative_card_addr, + )? + }; + + if scr.support_set_block_count { + sdcard.method = BlockTransmissionMode::SetBlockCount; + } + + sdcard.card_config = Some(scr); + } + if self.mmc_ios.bus_width == MmcBusWidth::Width1 && self .host_capability @@ -917,14 +934,14 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R1, cmdarg: (relative_card_address as u32) << 16, }; - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; let cmd = SdmmcCmd { cmdidx: SD_CMD_APP_SET_BUS_WIDTH, resp_type: MMC_RSP_R1, cmdarg: 2, // Argument for 4-bit mode (0 for 1-bit mode) }; - Self::send_cmd_and_receive_resp(&mut self.hardware, &cmd, None, &mut resp)?; + self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; self.hardware.sdmmc_config_bus_width(MmcBusWidth::Width4)?; @@ -935,148 +952,135 @@ impl SdmmcProtocol { // TODO: Change sdcard bus width here, or get rid of that field completely } - if let Some((memory, memory_ioaddr, cache_invalidate_function)) = memory_and_invalidate_cache_fn { - debug_log!("Checking supported speed classes\n"); - if let Some(MmcDevice::Sdcard(ref mut sdcard)) = self.mmc_device { - match self.mmc_ios.signal_voltage { - MmcSignalVoltage::Voltage330 => { - sdcard.card_state.timing = MmcTiming::Legacy; - if !sdcard - .card_cap - .contains(SdcardCapability(MMC_TIMING_LEGACY)) - { - // When the target speed is None, the sdcard_switch_speed update sdcard speed capability + debug_log!("Checking supported speed classes\n"); + + if let Some(MmcDevice::Sdcard(ref mut sdcard)) = self.mmc_device { + match self.mmc_ios.signal_voltage { + MmcSignalVoltage::Voltage330 => { + sdcard.card_state.timing = MmcTiming::Legacy; + if !sdcard + .card_cap + .contains(SdcardCapability(MMC_TIMING_LEGACY)) + { + // When the target speed is None, the sdcard_switch_speed update sdcard speed capability + unsafe { self.sdcard_check_supported_speed_class( memory, - memory_ioaddr, cache_invalidate_function, + physical_memory_addr, )?; } } - MmcSignalVoltage::Voltage180 => { - sdcard.card_state.timing = MmcTiming::UhsSdr12; - if !sdcard - .card_cap - .contains(SdcardCapability(MMC_TIMING_UHS_SDR12)) - { + } + MmcSignalVoltage::Voltage180 => { + sdcard.card_state.timing = MmcTiming::UhsSdr12; + if !sdcard + .card_cap + .contains(SdcardCapability(MMC_TIMING_UHS_SDR12)) + { + unsafe { self.sdcard_check_supported_speed_class( memory, - memory_ioaddr, cache_invalidate_function, + physical_memory_addr, )?; } } - MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EUNDEFINED), - }; - } else { - return Err(SdmmcError::EUNDEFINED); - } + } + MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EUNDEFINED), + }; + } else { + return Err(SdmmcError::EUNDEFINED); + } - let mut timing; - let sdcard_cap: SdcardCapability; - if let Some(MmcDevice::Sdcard(ref sdcard)) = self.mmc_device { - debug_log!("Switch to higher speed class\n"); - sdcard_cap = sdcard.card_cap.clone(); - timing = sdcard.card_state.timing; - // This 'tune_speed thing is a feature called labeled block in Rust - 'tune_speed: { - match self.mmc_ios.signal_voltage { - MmcSignalVoltage::Voltage330 => { - if sdcard_cap.contains(SdcardCapability(MMC_TIMING_SD_HS)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_SD_HS)) - { - // When the target speed is Some, the sdcard_switch_speed try to switch the speed class to target - self.sdcard_switch_speed( - MmcTiming::SdHs, - memory, - memory_ioaddr, - cache_invalidate_function, - )?; - timing = MmcTiming::SdHs; - } + let mut target_timing; + let sdcard_cap: SdcardCapability; + if let Some(MmcDevice::Sdcard(ref sdcard)) = self.mmc_device { + debug_log!("Switch to higher speed class\n"); + sdcard_cap = sdcard.card_cap.clone(); + target_timing = sdcard.card_state.timing; + // This 'tune_speed thing is a feature called labeled block in Rust + 'tune_speed: { + match self.mmc_ios.signal_voltage { + MmcSignalVoltage::Voltage330 => { + if sdcard_cap.contains(SdcardCapability(MMC_TIMING_SD_HS)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_SD_HS)) + { + target_timing = MmcTiming::SdHs; } - MmcSignalVoltage::Voltage180 => { - if sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR104)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR104)) - { - self.sdcard_switch_speed( - MmcTiming::UhsSdr104, - memory, - memory_ioaddr, - cache_invalidate_function, - )?; - timing = MmcTiming::UhsSdr104; - // If the switch speed succeed, terminate the block - break 'tune_speed; - } - if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_DDR50)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_UHS_DDR50)) - { - self.sdcard_switch_speed( - MmcTiming::UhsDdr50, - memory, - memory_ioaddr, - cache_invalidate_function, - )?; - timing = MmcTiming::UhsDdr50; - break 'tune_speed; - } - if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR50)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR50)) - { - self.sdcard_switch_speed( - MmcTiming::UhsSdr50, - memory, - memory_ioaddr, - cache_invalidate_function, - )?; - timing = MmcTiming::UhsSdr50; - break 'tune_speed; - } - if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR25)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR25)) - { - self.sdcard_switch_speed( - MmcTiming::UhsSdr25, - memory, - memory_ioaddr, - cache_invalidate_function, - )?; - timing = MmcTiming::UhsSdr25; - break 'tune_speed; - } + } + MmcSignalVoltage::Voltage180 => { + if sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR104)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR104)) + { + target_timing = MmcTiming::UhsSdr104; + // If the switch speed succeed, terminate the block + break 'tune_speed; + } + if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_DDR50)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_UHS_DDR50)) + { + target_timing = MmcTiming::UhsDdr50; + break 'tune_speed; + } + if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR50)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR50)) + { + target_timing = MmcTiming::UhsSdr50; + break 'tune_speed; + } + if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR25)) + && self + .host_capability + .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR25)) + { + target_timing = MmcTiming::UhsSdr25; + break 'tune_speed; } - MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EUNDEFINED), } - }; - self.mmc_ios.clock = self.hardware.sdmmc_config_timing(timing)?; - self.process_sampling(memory_ioaddr, cache_invalidate_function)?; - - debug_log!("Current frequency: {}Hz", self.mmc_ios.clock); - } else { - return Err(SdmmcError::EUNDEFINED); - } - if let Some(MmcDevice::Sdcard(ref mut sdcard)) = self.mmc_device { - sdcard.card_state.timing = timing; + MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EUNDEFINED), + } + }; + unsafe { + self.sdcard_switch_speed( + target_timing, + memory, + cache_invalidate_function, + physical_memory_addr, + )?; } + self.mmc_ios.clock = self.hardware.sdmmc_config_timing(target_timing)?; + + self.hardware + .sdmmc_execute_tuning(physical_memory_addr as *mut [u8; 64])?; + + debug_log!("Current frequency: {}Hz\n", self.mmc_ios.clock); + } else { + return Err(SdmmcError::EUNDEFINED); + } + if let Some(MmcDevice::Sdcard(ref mut sdcard)) = self.mmc_device { + sdcard.card_state.timing = target_timing; } Ok(()) } - pub fn config_interrupt(&mut self, enable_irq: bool, enable_sdio_irq: bool) -> Result<(), SdmmcError> { + pub fn config_interrupt( + &mut self, + enable_irq: bool, + enable_sdio_irq: bool, + ) -> Result<(), SdmmcError> { self.mmc_ios.enabled_irq = enable_irq | enable_sdio_irq; - self.hardware.sdmmc_config_interrupt(enable_irq, enable_sdio_irq) + self.hardware + .sdmmc_config_interrupt(enable_irq, enable_sdio_irq) } pub fn ack_interrupt(&mut self) -> Result<(), SdmmcError> { @@ -1089,8 +1093,22 @@ impl SdmmcProtocol { start_idx: u64, destination: u64, ) -> (Result<(), SdmmcError>, SdmmcProtocol) { + let trans_meth: BlockTransmissionMode = { + if let Some(ref device) = self.mmc_device { + match device { + MmcDevice::Sdcard(sdcard) => sdcard.method.clone(), + MmcDevice::EMmc(emmc) => emmc.method.clone(), + MmcDevice::Unknown => return (Err(SdmmcError::EUNSUPPORTEDCARD), self), + } + } else { + return (Err(SdmmcError::ENOCARD), self); + } + }; + let mut cmd: SdmmcCmd; let mut res: Result<(), SdmmcError>; + let mut turing: bool = false; + // TODO: Figure out a way to support cards with 4 KB sector size let data: MmcData = MmcData { blocksize: 512, @@ -1099,6 +1117,7 @@ impl SdmmcProtocol { addr: destination, }; let mut resp: [u32; 4] = [0; 4]; + // TODO: Add more validation check in the future // Like sdmmc card usually cannot transfer arbitrary number of blocks at once @@ -1112,56 +1131,62 @@ impl SdmmcProtocol { // For now we default to assume the card is high_capacity // TODO: Fix it when we properly implement card boot up // TODO: If we boot the card by ourself or reset the card, remember to send block len cmd - let cmd_arg: u64 = start_idx; - if blockcnt == 1 { - cmd = SdmmcCmd { - cmdidx: MMC_CMD_READ_SINGLE_BLOCK, - resp_type: MMC_RSP_R1, - cmdarg: cmd_arg as u32, - }; - let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, Some(&data), &mut resp); - res = future.await; - - // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt - // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(); - - return (res, self); - } else { - cmd = SdmmcCmd { - cmdidx: MMC_CMD_READ_MULTIPLE_BLOCK, - resp_type: MMC_RSP_R1, - cmdarg: cmd_arg as u32, - }; - let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, Some(&data), &mut resp); - res = future.await; - - // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt - // for read/write requests? - // Should I use if statement to check whether to ack irq or not based on if irq is enabled or not? - let _ = self.hardware.sdmmc_ack_interrupt(); - - if let Ok(()) = res { - // Uboot code for determine response type in this case - // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; - // TODO: Add mmc checks here + loop { + if blockcnt == 1 { cmd = SdmmcCmd { - cmdidx: MMC_CMD_STOP_TRANSMISSION, - resp_type: MMC_RSP_R1B, - cmdarg: 0, + cmdidx: MMC_CMD_READ_SINGLE_BLOCK, + resp_type: MMC_RSP_R1, + cmdarg: start_idx as u32, }; - let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); - res = future.await; + res = Self::sdmmc_async_request(&mut self.hardware, &cmd, Some(&data), &mut resp) + .await; + } else { + // TODO: Add if here to determine if the card support cmd23 or not to determine to use cmd23 or cmd12 + // Set the expected number of blocks - // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt - // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(); + cmd = SdmmcCmd { + cmdidx: MMC_CMD_READ_MULTIPLE_BLOCK, + resp_type: MMC_RSP_R1, + cmdarg: start_idx as u32, + }; - return (res.map_err(|_| SdmmcError::ESTOPCMD), self); - } else { - return (res, self); + res = Self::sdmmc_multi_blocks_io( + &mut self.hardware, + &cmd, + &data, + &mut resp, + trans_meth.clone(), + ) + .await; + } + match res { + Ok(()) => {} + Err(ref err) => { + let stop_cmd: SdmmcCmd = SdmmcCmd { + cmdidx: MMC_CMD_STOP_TRANSMISSION, + resp_type: MMC_RSP_R1B, + cmdarg: 0, + }; + // Sending stop command manually regardless of whether it has been sent or not + let _ = + Self::sdmmc_async_request(&mut self.hardware, &stop_cmd, None, &mut resp) + .await; + + if let SdmmcError::EIO = err { + if let Some(memory) = self.private_memory { + if turing == false { + turing = true; + if let Ok(()) = self.hardware.sdmmc_execute_tuning(memory) { + continue; + } + } + } + } + } } + break; } + (res, self) } // Almost the same with read_block aside from the cmd being sent is a bit different @@ -1173,8 +1198,20 @@ impl SdmmcProtocol { start_idx: u64, source: u64, ) -> (Result<(), SdmmcError>, SdmmcProtocol) { - let mut cmd: SdmmcCmd; - let mut res: Result<(), SdmmcError>; + let trans_meth: BlockTransmissionMode = { + if let Some(ref device) = self.mmc_device { + match device { + MmcDevice::Sdcard(sdcard) => sdcard.method.clone(), + MmcDevice::EMmc(emmc) => emmc.method.clone(), + MmcDevice::Unknown => return (Err(SdmmcError::EUNSUPPORTEDCARD), self), + } + } else { + return (Err(SdmmcError::ENOCARD), self); + } + }; + + let cmd: SdmmcCmd; + let res: Result<(), SdmmcError>; // TODO: Figure out a way to support cards with 4 KB sector size let data: MmcData = MmcData { blocksize: 512, @@ -1185,58 +1222,44 @@ impl SdmmcProtocol { let mut resp: [u32; 4] = [0; 4]; // TODO: Add more validation check in the future - let cmd_arg: u64 = start_idx; if blockcnt == 1 { cmd = SdmmcCmd { cmdidx: MMC_CMD_WRITE_SINGLE_BLOCK, resp_type: MMC_RSP_R1, - cmdarg: cmd_arg as u32, + cmdarg: start_idx as u32, }; - let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, Some(&data), &mut resp); - res = future.await; - - // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt - // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(); + res = Self::sdmmc_async_request(&mut self.hardware, &cmd, Some(&data), &mut resp).await; return (res, self); } else { // TODO: Add if here to determine if the card support cmd23 or not to determine to use cmd23 or cmd12 // Set the expected number of blocks + cmd = SdmmcCmd { - cmdidx: MMC_CMD_SET_BLOCK_COUNT, + cmdidx: MMC_CMD_WRITE_MULTIPLE_BLOCK, resp_type: MMC_RSP_R1, - cmdarg: blockcnt, // block count for the upcoming CMD25 operation + cmdarg: start_idx as u32, }; - let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); - res = future.await; - - // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt - // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(); + res = + Self::sdmmc_multi_blocks_io(&mut self.hardware, &cmd, &data, &mut resp, trans_meth) + .await; - - if let Ok(()) = res { - // Uboot code for determine response type in this case - // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; - // TODO: Add mmc checks here - cmd = SdmmcCmd { - cmdidx: MMC_CMD_WRITE_MULTIPLE_BLOCK, - resp_type: MMC_RSP_R1, - cmdarg: cmd_arg as u32, - }; - let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, Some(&data), &mut resp); - res = future.await; - - // TODO: Figure out a generic model for every sd controller, like what if the sd controller only send interrupt - // for read/write requests? - let _ = self.hardware.sdmmc_ack_interrupt(); - - return (res.map_err(|_| SdmmcError::ESTOPCMD), self); - } else { - return (res, self); + match res { + Ok(()) => {} + Err(_) => { + let cmd: SdmmcCmd = SdmmcCmd { + cmdidx: MMC_CMD_STOP_TRANSMISSION, + resp_type: MMC_RSP_R1B, + cmdarg: 0, + }; + // Sending stop command manually regardless of whether it has been sent or not + let _ = + Self::sdmmc_async_request(&mut self.hardware, &cmd, None, &mut resp).await; + } } + + return (res, self); } } @@ -1258,9 +1281,7 @@ impl SdmmcProtocol { cmdarg: start_idx as u32, }; - let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); - - res = future.await; + res = Self::sdmmc_async_request(&mut self.hardware, &cmd, None, &mut resp).await; if let Err(_) = res { return (res, self); @@ -1272,9 +1293,7 @@ impl SdmmcProtocol { cmdarg: end_idx as u32, }; - let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); - - res = future.await; + res = Self::sdmmc_async_request(&mut self.hardware, &cmd, None, &mut resp).await; if let Err(_) = res { return (res, self); @@ -1286,67 +1305,80 @@ impl SdmmcProtocol { cmdarg: SD_ERASE_ARG, }; - let future = SdmmcCmdFuture::new(&mut self.hardware, &cmd, None, &mut resp); - - res = future.await; + res = Self::sdmmc_async_request(&mut self.hardware, &cmd, None, &mut resp).await; - return (res, self) + return (res, self); } - // Not used right now, but would be useful in the future once we want to execute some command synchronously - fn send_cmd_and_receive_resp( + /// Function to execute one sdmmc request asynchronously + /// The resp could be used for future error parsing + async fn sdmmc_async_request( hardware: &mut T, cmd: &SdmmcCmd, data: Option<&MmcData>, resp: &mut [u32; 4], ) -> Result<(), SdmmcError> { - // TODO: Add temporarily disable interrupt here + // Send the command to the host sdcard + hardware.sdmmc_send_command(cmd, data)?; + // Wait until sdcard return the result to the driver + let res: Result<(), SdmmcError> = SdmmcCmdFuture::new(hardware, cmd, resp).await; + // Acknowledge interrupts + hardware.sdmmc_ack_interrupt()?; + + res + } - // Send the command using the hardware layer - let mut res = hardware.sdmmc_send_command(cmd, data); - if res.is_err() { - return res; - } + /// Change this function so that if a request report back error + /// MMC_CMD_STOP_TRANSMISSION must be sent in the end so the card + /// stop transmission correctly + async fn sdmmc_multi_blocks_io( + hardware: &mut T, + request_cmd: &SdmmcCmd, + data: &MmcData, + resp: &mut [u32; 4], + transmission_mode: BlockTransmissionMode, + ) -> Result<(), SdmmcError> { + // Trasmission stage + match transmission_mode { + BlockTransmissionMode::SetBlockCount => { + let cmd: SdmmcCmd = SdmmcCmd { + cmdidx: MMC_CMD_SET_BLOCK_COUNT, + resp_type: MMC_RSP_R1, + cmdarg: data.blockcnt, // block count for the upcoming CMD25 operation + }; - res = Err(SdmmcError::ETIMEDOUT); + Self::sdmmc_async_request(hardware, &cmd, None, resp).await?; - // TODO: Change it to use the sleep function provided by the hardware layer - // This is a busy poll retry, we could poll infinitely if we trust the device to be correct - let mut retry: u32 = 100_000_000; + Self::sdmmc_async_request(hardware, &request_cmd, Some(&data), resp).await + } + BlockTransmissionMode::StopTransmission => { + let temp_res: Result<(), SdmmcError> = + Self::sdmmc_async_request(hardware, &request_cmd, Some(&data), resp).await; - // sel4_microkit::debug_println!("Request sent! Let us wait!"); + // Uboot code for determine response type in this case + // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; + // TODO: Add mmc checks here + let cmd: SdmmcCmd = SdmmcCmd { + cmdidx: MMC_CMD_STOP_TRANSMISSION, + resp_type: MMC_RSP_R1B, + cmdarg: 0, + }; - while retry > 0 { - // Try to receive the response - res = hardware.sdmmc_receive_response(cmd, resp); + // Technically this command has very little possibility to fail + // Unless for hardware or environment issue + let _ = Self::sdmmc_async_request(hardware, &cmd, None, resp).await; - if let Err(SdmmcError::EBUSY) = res { - // Busy response, retry - retry -= 1; - // hardware.sleep(1); // Placeholder: Implement a sleep function in SdmmcHardware trait - } else { - // If any other error or success, break the loop - break; + temp_res + } + BlockTransmissionMode::AutoStop => { + Self::sdmmc_async_request(hardware, &request_cmd, Some(&data), resp).await } } - - if let Err(_) = res { - debug_log!("Resp[0] value {:08x}\n", resp[0]); - } - - // TODO: Add renable interrupt here - res // Return the final result (Ok or Err) } } - enum CmdState { - // Currently sending the command - // State at the start - NotSent, // Waiting for the response WaitingForResponse, - // Error encountered - Error, // Finished Finished, } @@ -1354,7 +1386,6 @@ enum CmdState { pub struct SdmmcCmdFuture<'a, 'b, 'c> { hardware: &'a mut dyn SdmmcHardware, cmd: &'b SdmmcCmd, - data: Option<&'b MmcData>, waker: Option, state: CmdState, response: &'c mut [u32; 4], @@ -1364,15 +1395,13 @@ impl<'a, 'b, 'c> SdmmcCmdFuture<'a, 'b, 'c> { pub fn new( hardware: &'a mut dyn SdmmcHardware, cmd: &'b SdmmcCmd, - data: Option<&'b MmcData>, response: &'c mut [u32; 4], ) -> SdmmcCmdFuture<'a, 'b, 'c> { SdmmcCmdFuture { hardware, cmd, - data, waker: None, - state: CmdState::NotSent, + state: CmdState::WaitingForResponse, response, } } @@ -1383,7 +1412,6 @@ impl<'a, 'b, 'c> SdmmcCmdFuture<'a, 'b, 'c> { /// We actually do not need an executor to execute the request /// The context can be ignored unless someone insist to use an executor for the requests /// So for now, the context is being stored in waker but this waker will not be used -/// Beside, we are in no std environment and can barely use any locks impl<'a, 'b, 'c> Future for SdmmcCmdFuture<'a, 'b, 'c> { type Output = Result<(), SdmmcError>; @@ -1395,26 +1423,8 @@ impl<'a, 'b, 'c> Future for SdmmcCmdFuture<'a, 'b, 'c> { self.waker = Some(cx.waker().clone()); match self.state { - CmdState::NotSent => { - let res: Result<(), SdmmcError>; - { - let this: &mut SdmmcCmdFuture<'a, 'b, 'c> = self.as_mut().get_mut(); - - // Now, you can pass `&SdmmcCmd` to `sdmmc_send_command` - let cmd: &SdmmcCmd = this.cmd; - let data: Option<&MmcData> = this.data; - res = this.hardware.sdmmc_send_command(cmd, data); - } - if let Ok(()) = res { - self.state = CmdState::WaitingForResponse; - return Poll::Pending; - } else { - self.state = CmdState::Error; - return Poll::Ready(res); - } - } CmdState::WaitingForResponse => { - let res; + let res: Result<(), SdmmcError>; { let this: &mut SdmmcCmdFuture<'a, 'b, 'c> = self.as_mut().get_mut(); let cmd: &SdmmcCmd = this.cmd; @@ -1424,16 +1434,29 @@ impl<'a, 'b, 'c> Future for SdmmcCmdFuture<'a, 'b, 'c> { } if let Err(SdmmcError::EBUSY) = res { return Poll::Pending; - } else if let Ok(()) = res { - self.state = CmdState::Finished; - return Poll::Ready(res); } else { - self.state = CmdState::Error; + self.state = CmdState::Finished; return Poll::Ready(res); } } - CmdState::Error => return Poll::Ready(Err(SdmmcError::EUNDEFINED)), + // Despite the status is ready, the state machine get polled again CmdState::Finished => return Poll::Ready(Err(SdmmcError::EUNDEFINED)), } } } + +/// Helper function to print out the content of one block +#[allow(dead_code)] +unsafe fn print_one_block(ptr: *const u8, num: usize) { + unsafe { + // Iterate over the number of bytes and print each one in hexadecimal format + for i in 0..num { + let byte = *ptr.add(i); + if i % 16 == 0 { + debug_log!("\n{:04x}: ", i); + } + debug_log!("{:02x} ", byte); + } + debug_log!("\n"); + } +} diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs index 71398d13c..7c632a541 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs @@ -17,7 +17,7 @@ pub enum TuningState { } // Timing modes (could be an enum or use the bitflags constants defined earlier) -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum MmcTiming { Legacy = 0, MmcHs = 1, @@ -37,7 +37,7 @@ pub enum MmcTiming { ClockStop = 15, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub(crate) struct MmcState { /// The timing specification that dictates how data is transferred between the host /// and the card. @@ -84,3 +84,13 @@ enum CardState { Disconnect, Unknown, } + +#[derive(Debug, Clone)] +pub enum BlockTransmissionMode { + // Using set block count cmd for multiblock transfer + SetBlockCount = 0, + // Using stop transmission count cmd for multiblock transfer + StopTransmission = 1, + // Host automatically send stop command without the need to driver interference + AutoStop = 2, +} diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs new file mode 100644 index 000000000..4f78fa4d3 --- /dev/null +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs @@ -0,0 +1,86 @@ +use core::sync::atomic::Ordering; + +use crate::sdmmc_traits::SdmmcHardware; + +use super::{ + sdcard::{Scr, Sdcard}, + sdmmc_constant::{MMC_CMD_APP_CMD, SD_CMD_APP_SEND_SCR, SD_CMD_SWITCH_FUNC}, + MmcData, MmcDataFlag, SdmmcCmd, SdmmcError, MMC_RSP_R1, +}; + +impl Sdcard { + /// Unsafe because dereference raw pointer + pub(crate) unsafe fn sdcard_get_configuration_register( + hardware: &mut T, + physical_memory: u64, + raw_memory: *mut [u8; 64], + invalidate_cache_fn: fn(), + rca: u16, + ) -> Result { + let mut resp: [u32; 4] = [0; 4]; + let mut cmd: SdmmcCmd = SdmmcCmd { + cmdidx: MMC_CMD_APP_CMD, + resp_type: MMC_RSP_R1, + cmdarg: (rca as u32) << 16, + }; + hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; + + cmd = SdmmcCmd { + cmdidx: SD_CMD_APP_SEND_SCR, + resp_type: MMC_RSP_R1, + cmdarg: 0, + }; + let data: MmcData = MmcData { + blocksize: 8, + blockcnt: 1, + flags: MmcDataFlag::SdmmcDataRead, + addr: physical_memory, + }; + + hardware.sdmmc_do_request(&cmd, Some(&data), &mut resp, 0)?; + + core::sync::atomic::fence(Ordering::Acquire); + + invalidate_cache_fn(); + + // print out the content of the SCR register + sel4_microkit_support::debug_log!("SCR register content: "); + unsafe { crate::sdmmc::print_one_block(raw_memory as *const u8, 8) }; + + // The sdcard register data is always in big endian format + // Now we construct the last 32 bits of the scr register + let scr_raw: u64 = unsafe { + ((((*raw_memory)[0] as u64) << 24) + + (((*raw_memory)[1] as u64) << 16) + + (((*raw_memory)[2] as u64) << 8) + + ((*raw_memory)[3] as u64)) + << 32 + }; + + let scr: Scr = Scr::new(scr_raw)?; + + Ok(scr) + } + + pub fn sdcard_test_tuning( + hardware: &mut T, + memory: *mut [u8; 64], + ) -> Result<(), SdmmcError> { + let mut resp: [u32; 4] = [0; 4]; + + let data = MmcData { + blocksize: 64, + blockcnt: 1, + flags: MmcDataFlag::SdmmcDataRead, + addr: memory as u64, + }; + + let cmd = SdmmcCmd { + cmdidx: SD_CMD_SWITCH_FUNC, + resp_type: MMC_RSP_R1, + cmdarg: 0x00FFFFFF, + }; + + hardware.sdmmc_do_request(&cmd, Some(&data), &mut resp, 1) + } +} diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs index aacaca250..ef34e3833 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs @@ -1,25 +1,29 @@ use core::fmt::{self, Write}; +use sel4_microkit_support::debug_log; + use super::{ - mmc_struct::{self, MmcState}, + mmc_struct::{self, BlockTransmissionMode, MmcBusWidth, MmcState}, sdmmc_capability::SdcardCapability, - SdmmcHardware, SdmmcProtocol, + SdmmcError, SdmmcHardware, SdmmcProtocol, }; -pub(crate) struct Sdcard { - pub card_id: u128, - pub manufacture_info: Cid, - pub card_specific_data: Csd, - pub card_version: SdVersion, - pub relative_card_addr: u16, - pub card_state: MmcState, - pub card_cap: SdcardCapability, - pub card_config: Option, +pub struct Sdcard { + pub(crate) card_id: u128, + pub(crate) manufacture_info: Cid, + pub(crate) card_specific_data: Csd, + pub(crate) card_version: SdVersion, + pub(crate) relative_card_addr: u16, + pub(crate) card_state: MmcState, + pub(crate) card_cap: SdcardCapability, + pub(crate) method: BlockTransmissionMode, + pub(crate) card_config: Option, } /// Placeholder eMMC struct that is not implemented pub struct EMmc { pub card_id: u128, + pub method: BlockTransmissionMode, } // Beware this struct is meant to track the cmd set that the sdcard should support @@ -28,14 +32,14 @@ pub struct EMmc { // The SD card specification is cumulative, meaning that if an SD card reports support for a // particular version (say 4.0), it implicitly supports all earlier versions as well. #[derive(Debug, PartialEq, Eq)] -pub enum SdVersion { +pub(crate) enum SdVersion { V1_0 = 1, V2_0 = 2, V3_0 = 3, V4_0 = 4, } -pub struct Cid { +pub(crate) struct Cid { manufacturer_id: u8, oem_id: u16, product_name: [u8; 5], @@ -111,7 +115,7 @@ impl ToArray for Cid { } // This struct is super unreliable, I am thinking -pub struct Csd { +pub(crate) struct Csd { csd_structure: u8, card_capacity: u64, max_read_block_len: u16, @@ -131,10 +135,11 @@ impl Csd { // Extract the CSD structure version let csd_structure = ((csd_combined >> 126) & 0x3) as u8; // Bits 126–127 let sd_version = match csd_structure { - 0 => SdVersion::V1_0, // CSD Version 1.0 - 1 => SdVersion::V2_0, // CSD Version 2.0 + 0 => SdVersion::V1_0, // CSD Version 1.0 + 1 => SdVersion::V2_0, // CSD Version 2.0 + // Even if the parsing csd fails, it should not crash the driver completely _ => panic!("Unsupported CSD structure version"), // CSD structures beyond 2.0 are not supported here - // Actually SDUC card are using CSD 3.0 so maybe add something here later. + // Actually SDUC card are using CSD 3.0 so maybe add something here later. }; // Parse fields based on CSD version @@ -189,14 +194,63 @@ impl Csd { } } -pub struct Scr { - sd_spec: u8, - data_stat_after_erase: bool, +pub(crate) struct Scr { + // Not extracted from Scr parsing yet + pub sd_spec: u32, + pub data_stat_after_erase: bool, + // Not extracted from Scr parsing yet sd_security: u8, - sd_bus_widths: u8, - sd_spec3: bool, - sd_spec4: bool, - supports_cmd23: bool, + pub sd_bus_width: MmcBusWidth, + pub support_speed_class_control: bool, + pub support_set_block_count: bool, + pub support_extersion_register_single_block: bool, + pub support_extersion_register_multi_block: bool, + pub support_security_transmission: bool, +} + +impl Scr { + // Input variable: raw scr data + pub fn new(scr_raw: u64) -> Result { + if scr_raw & (0b1 << 48) != (0b1 << 48) { + return Err(SdmmcError::EINVAL); + } + + let mut sd_bus_width: MmcBusWidth = MmcBusWidth::Width1; + + if scr_raw & (0b1 << 50) == (0b1 << 50) { + sd_bus_width = MmcBusWidth::Width4; + } + + // Extract bits 32-36 as a single value (5 bits = 0-31 range) + // Shift right 32 bits and mask with 0b11111 (31) + let command_support_bits = (scr_raw >> 32) & 0b11111; + + // Convert to bool array + let mut supported_cmd: [bool; 5] = [false; 5]; + for i in 0..5 { + supported_cmd[i] = (command_support_bits & (1 << i)) != 0; + } + + debug_log!("Supported cmd: {:?}\n", supported_cmd); + + let mut data_stat_after_erase: bool = false; + if scr_raw & (0b1 << 55) != 0 { + data_stat_after_erase = true; + } + debug_log!("Data status after erase: {:?}\n", data_stat_after_erase); + + Ok(Scr { + sd_spec: 0, + data_stat_after_erase, + sd_security: 0, + sd_bus_width, + support_speed_class_control: supported_cmd[0], + support_set_block_count: supported_cmd[1], + support_extersion_register_single_block: supported_cmd[2], + support_extersion_register_multi_block: supported_cmd[3], + support_security_transmission: supported_cmd[4], + }) + } } struct ArrayWriter<'a> { diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs index 37e0fa8be..3dee3460c 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs @@ -18,4 +18,4 @@ pub fn process_wait_unreliable(time_ns: u64) { // No operation, would be optimized out macro_rules! debug_log { ($($arg:tt)*) => {}; -} \ No newline at end of file +} diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs index 98a2e1192..28dfa3a2f 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs @@ -1,4 +1,16 @@ -use crate::sdmmc::{mmc_struct::{MmcBusWidth, MmcTiming, TuningState}, HostInfo, MmcData, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, SdmmcError}; +use sel4_microkit_support::{debug_log, process_wait_unreliable}; + +use crate::sdmmc::{ + mmc_struct::{MmcBusWidth, MmcTiming, TuningState}, + HostInfo, MmcData, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, SdmmcError, +}; + +const POLLING_INTERVAL_TIME_US: u32 = 1024; +const DATA_TRANSFER_POLLING_INTERVAL_TIME_US: u32 = 4096; +// The polling chances before time out is deliberately being set to a large value +// as the host is supposed to catch thetimeout request and report to us +const POLLING_CHANCE_BEFORE_TIME_OUT: u32 = 1024; +const DATA_TRANSFER_POLLING_CHANCE_BEFORE_TIME_OUT: u32 = 2048; #[allow(unused_variables)] /// Program async Rust can be very dangerous if you do not know what is happening understand the hood @@ -87,7 +99,11 @@ pub trait SdmmcHardware { } // Change the function signature to something like sdmmc_config_interrupt(&mut self, enable_irq: bool, enable_sdio_irq: bool); - fn sdmmc_config_interrupt(&mut self, enable_irq: bool, enable_sdio_irq: bool) -> Result<(), SdmmcError> { + fn sdmmc_config_interrupt( + &mut self, + enable_irq: bool, + enable_sdio_irq: bool, + ) -> Result<(), SdmmcError> { return Err(SdmmcError::ENOTIMPLEMENTED); } @@ -106,39 +122,72 @@ pub trait SdmmcHardware { /// signals sent from the host to the SD card. This would ensure that the SD card reliably receives data, especially /// at high frequencies. However, output timing tends to be more stable, and a specific function for tuning host-to-card /// data timing is often not implemented or needed, as seen in the Linux driver. - /// - /// The cooperation between protocol layer and hardware layer by this function is like: the protocol layer send - /// the MMC_CMD_SEND_TUNING_BLOCK request. And if the result receive back is in error, the protocol layer will call this - /// tune_sampling function once again. If tune sampling has run out of option, return an error. - /// It is suggest that hardware layer also do some book keeping about the suitable delay to making the tuning - /// sampling process faster. - /// Performs sampling point tuning for the SD/MMC interface. - /// - /// The `TuningState` enum controls the tuning process, guiding the adjustments - /// of the sampling point to achieve reliable communication at high speeds. - /// - /// # Tuning Process - /// - /// - Before tuning begins, the protocol layer calls `sdmmc_tune_sampling` - /// with the `TuningStart` state to initialize the tuning process. - /// - The protocol layer then attempts to verify if the current sampling adjustment is effective. - /// - If verification fails, the protocol layer calls `sdmmc_tune_sampling` - /// with the `TuningContinue` state to adjust the sampling point further. - /// - This adjustment and verification process repeats until a reliable sampling - /// point is found. - /// - Once a working delay is identified, the protocol layer calls - /// `sdmmc_tune_sampling` with the `TuningComplete` state to finalize the tuning process. - /// - /// # Parameters - /// - `state`: The current tuning state, represented by the `TuningState` enum: - /// - `TuningStart`: Initializes the tuning process. - /// - `TuningContinue`: Adjusts the sampling point incrementally. - /// - `TuningComplete`: Finalizes the tuning once a reliable sampling point is found. - /// - /// # Returns - /// - `Ok(())`: Indicates successful completion of the requested tuning step. - /// - `Err(SdmmcError)`: An error occurred during the tuning process. - fn sdmmc_tune_sampling(&mut self, state: TuningState) -> Result<(), SdmmcError> { + fn sdmmc_execute_tuning(&mut self, memory: *mut [u8; 64]) -> Result<(), SdmmcError> { return Err(SdmmcError::ENOTIMPLEMENTED); } -} \ No newline at end of file + + fn sdmmc_host_reset(&mut self) -> Result { + return Err(SdmmcError::ENOTIMPLEMENTED); + } + + fn sdmmc_do_request( + &mut self, + cmd: &SdmmcCmd, + data: Option<&MmcData>, + resp: &mut [u32; 4], + mut retry: u32, + ) -> Result<(), SdmmcError> { + 'command_retry: loop { + // Send the command using send command function provided by the hardware layer + self.sdmmc_send_command(cmd, data)?; + + let mut res: Result<(), SdmmcError>; + + match data { + // The flow with data transfer + Some(_) => { + for _ in 0..DATA_TRANSFER_POLLING_CHANCE_BEFORE_TIME_OUT { + process_wait_unreliable( + DATA_TRANSFER_POLLING_INTERVAL_TIME_US as u64 * 1000, + ); + res = self.sdmmc_receive_response(cmd, resp); + match res { + Err(SdmmcError::ETIMEDOUT) => { + if retry == 0 { + return Err(SdmmcError::ETIMEDOUT); + } + retry -= 1; + continue 'command_retry; + } + Err(SdmmcError::EBUSY) => continue, + Err(e) => return Err(e), + Ok(_) => return Ok(()), + } + } + } + // The flow without data transfer + None => { + for _ in 0..POLLING_CHANCE_BEFORE_TIME_OUT { + process_wait_unreliable(POLLING_INTERVAL_TIME_US as u64 * 1000); + res = self.sdmmc_receive_response(cmd, resp); + match res { + Err(SdmmcError::ETIMEDOUT) => { + if retry == 0 { + return Err(SdmmcError::ETIMEDOUT); + } + retry -= 1; + continue 'command_retry; + } + Err(SdmmcError::EBUSY) => continue, + Err(e) => return Err(e), + Ok(_) => return Ok(()), + } + } + } + } + break 'command_retry; + } + debug_log!("A timeout request not reported by the host, the host might be unreliable\n"); + Err(SdmmcError::EUNDEFINED) + } +} From 5e2a7ca6e835ec756f5c8855ede95a3f5a47d84a Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 9 Apr 2025 14:34:15 +1000 Subject: [PATCH 25/48] Update and fixes Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 10 ++++----- drivers/blk/sdmmc/rust-toolchain.toml | 5 +++-- drivers/blk/sdmmc/src/main.rs | 30 --------------------------- 3 files changed, 8 insertions(+), 37 deletions(-) diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 3f875cf3f..4661d3ae2 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -4,11 +4,11 @@ version = "0.1.0" edition = "2021" [dependencies] -sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5826be1a6c83803faeaa79cd9f164c26a5a32e7c" } -# sdmmc_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "b32423484966e76101683c7231f545fc2296399a" } -# sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "b32423484966e76101683c7231f545fc2296399a", features = ["sel4-microkit"] } -sdmmc_hal = { path = "sdmmc_hal/meson" } -sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "a12b43ee359ed8f1fa7433f0534217da3b83e9ef" } +sdmmc_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "e590aa23448cfeaa1398722a86472fa61122a7ba" } +sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "e590aa23448cfeaa1398722a86472fa61122a7ba", features = ["sel4-microkit"] } +# sdmmc_hal = { path = "sdmmc_hal/meson" } +# sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } [build] build = "build.rs" \ No newline at end of file diff --git a/drivers/blk/sdmmc/rust-toolchain.toml b/drivers/blk/sdmmc/rust-toolchain.toml index 40ab25b58..e4869f8e1 100644 --- a/drivers/blk/sdmmc/rust-toolchain.toml +++ b/drivers/blk/sdmmc/rust-toolchain.toml @@ -5,5 +5,6 @@ # [toolchain] -channel = "nightly-2024-10-26" -components = [ "rustfmt", "rust-src", "rustc-dev", "llvm-tools-preview" ] +channel = "nightly-2025-02-26" +components = [ "rust-src", "rustc-dev", "llvm-tools-preview" ] +profile = "default" diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 953d2bd83..a6ca22b52 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -23,31 +23,6 @@ use sdmmc_hal::meson_gx_mmc::SdmmcMesonHardware; use sdmmc_protocol::sdmmc::{SdmmcError, SdmmcProtocol}; use sdmmc_protocol::sdmmc_traits::SdmmcHardware; use sel4_microkit::{debug_print, debug_println, protection_domain, Channel, Handler, Infallible}; -/* XXX: Added to work with microkit 2.0 */ - -use sel4_microkit::var; - -#[cfg(not(feature = "extern-symbols"))] -macro_rules! maybe_extern_var { - ($symbol:ident: $ty:ty = $default:expr) => { - var! { - #[used(linker)] - $symbol: $ty = $default - } - }; -} - -#[cfg(feature = "extern-symbols")] -macro_rules! maybe_extern_var { - ($symbol:ident: $ty:ty = $default:expr) => {{ - extern "C" { - static $symbol: $ty; - } - - unsafe { &$symbol } - }}; -} -/* XXX: ^^^ Added to work with microkit 2.0 */ const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(0); const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(1); @@ -95,11 +70,6 @@ fn create_dummy_waker() -> Waker { #[protection_domain(heap_size = 0x10000)] fn init() -> HandlerImpl { -/* XXX: Added to work with microkit 2.0 */ - maybe_extern_var!(microkit_irqs: u64 = 0); - maybe_extern_var!(microkit_notifications: u64 = 0); - maybe_extern_var!(microkit_pps: u64 = 0); -/* XXX: Added to work with microkit 2.0 */ debug_println!("Driver init!"); unsafe { blk_queue_init_helper(); From aad34122fc1ad37c3d8e6b00914c65073cc9a9e3 Mon Sep 17 00:00:00 2001 From: Cheng Date: Fri, 20 Jun 2025 17:42:07 +1000 Subject: [PATCH 26/48] Add timer driver interface in Rust Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 1 + drivers/blk/sdmmc/src/main.rs | 4 ++++ include/sddf_rust/timer/Cargo.toml | 13 +++++++++++++ include/sddf_rust/timer/lib.rs | 3 +++ include/sddf_rust/timer/timer.rs | 30 ++++++++++++++++++++++++++++++ 5 files changed, 51 insertions(+) create mode 100644 include/sddf_rust/timer/Cargo.toml create mode 100644 include/sddf_rust/timer/lib.rs create mode 100644 include/sddf_rust/timer/timer.rs diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 4661d3ae2..2c649615a 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "a12b43ee359ed8f1fa7433f0534217da3b83e9ef" } sdmmc_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "e590aa23448cfeaa1398722a86472fa61122a7ba" } sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "e590aa23448cfeaa1398722a86472fa61122a7ba", features = ["sel4-microkit"] } +sddf_timer = { path = "../../../include/sddf_rust/timer" } # sdmmc_hal = { path = "sdmmc_hal/meson" } # sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index a6ca22b52..653efec73 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -18,6 +18,7 @@ use sddf_blk::{ blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkRequest, BlkStatus, blk_device_regs_vaddr, blk_device_init_data_vaddr, blk_device_init_data_ioaddr }; +use sddf_timer::timer::Timer; use sdmmc_hal::meson_gx_mmc::SdmmcMesonHardware; use sdmmc_protocol::sdmmc::{SdmmcError, SdmmcProtocol}; @@ -27,6 +28,9 @@ use sel4_microkit::{debug_print, debug_println, protection_domain, Channel, Hand const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(0); const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(1); +const TIMER_CHANNEL: usize = 2; +const TIMER: Timer = Timer::new(sel4_microkit::Channel::new(TIMER_CHANNEL)); + const SDCARD_SECTOR_SIZE: u32 = 512; const SDDF_TRANSFER_SIZE: u32 = 4096; const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE; diff --git a/include/sddf_rust/timer/Cargo.toml b/include/sddf_rust/timer/Cargo.toml new file mode 100644 index 000000000..2fbc153ae --- /dev/null +++ b/include/sddf_rust/timer/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "sddf_timer" +version = "0.1.0" +edition = "2021" +authors = ["Cheng Li 李澄 "] + +[lib] +name = "sddf_timer" +path = "lib.rs" + +[dependencies] +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "ac5627ba7a67e71f33a8eb1c5d05de09bf94ef5e" } +# sel4-microkit-message = { git = "https://github.com/seL4/rust-sel4.git", rev = "ac5627ba7a67e71f33a8eb1c5d05de09bf94ef5e" } \ No newline at end of file diff --git a/include/sddf_rust/timer/lib.rs b/include/sddf_rust/timer/lib.rs new file mode 100644 index 000000000..471cc85a6 --- /dev/null +++ b/include/sddf_rust/timer/lib.rs @@ -0,0 +1,3 @@ +#![no_std] // Don't link the standard library + +pub mod timer; diff --git a/include/sddf_rust/timer/timer.rs b/include/sddf_rust/timer/timer.rs new file mode 100644 index 000000000..cd7f36df7 --- /dev/null +++ b/include/sddf_rust/timer/timer.rs @@ -0,0 +1,30 @@ +use sel4_microkit::MessageInfo; + +enum SddfTimer { + GetTime = 0, + SetTimeout = 1, +} + +pub struct Timer { + channel: sel4_microkit::Channel, +} + +impl Timer { + pub const fn new(server_channel: sel4_microkit::Channel) -> Self { + Timer { + channel: server_channel, + } + } + + pub fn set_timeout(&self, timeout: u64) { + sel4_microkit::set_mr(0, timeout); + let msg_info: MessageInfo = MessageInfo::new(SddfTimer::SetTimeout as u64, 1); + self.channel.pp_call(msg_info); + } + + pub fn time_now(&self) -> u64 { + let msg_info: MessageInfo = MessageInfo::new(SddfTimer::GetTime as u64, 0); + self.channel.pp_call(msg_info); + sel4_microkit::get_mr(0) + } +} From 5e44b615c6437a1ce3679443e1be49e5d0b37732 Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 16 Jul 2025 16:21:37 +1000 Subject: [PATCH 27/48] Update rust-sel4 for timer Signed-off-by: Cheng --- include/sddf_rust/timer/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/sddf_rust/timer/Cargo.toml b/include/sddf_rust/timer/Cargo.toml index 2fbc153ae..f2cc7a4ae 100644 --- a/include/sddf_rust/timer/Cargo.toml +++ b/include/sddf_rust/timer/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sddf_timer" version = "0.1.0" -edition = "2021" +edition = "2024" authors = ["Cheng Li 李澄 "] [lib] @@ -9,5 +9,5 @@ name = "sddf_timer" path = "lib.rs" [dependencies] -sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "ac5627ba7a67e71f33a8eb1c5d05de09bf94ef5e" } -# sel4-microkit-message = { git = "https://github.com/seL4/rust-sel4.git", rev = "ac5627ba7a67e71f33a8eb1c5d05de09bf94ef5e" } \ No newline at end of file +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" } +# sel4-microkit-message = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" } \ No newline at end of file From c0625a785c72af4a4463edc3a1eb0db226d98602 Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 4 Aug 2025 13:41:41 +1000 Subject: [PATCH 28/48] Update the main.rs to work with the latest SD card driver Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 12 +- drivers/blk/sdmmc/blk_driver.mk | 3 +- .../sel4-microkit-support/Cargo.toml | 15 - .../sel4-microkit-support/lib.rs | 40 -- drivers/blk/sdmmc/rust-toolchain.toml | 2 +- drivers/blk/sdmmc/src/main.rs | 434 ++++++++++-------- drivers/blk/sdmmc/src/sddf_blk/mod.rs | 2 +- drivers/blk/sdmmc/src/sddf_helper.c | 2 +- drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs | 44 ++ .../sdmmc/src/sel4_microkit_os/odroidc4.rs | 136 ++++++ 10 files changed, 423 insertions(+), 267 deletions(-) delete mode 100644 drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/Cargo.toml delete mode 100644 drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/lib.rs create mode 100644 drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs create mode 100644 drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 2c649615a..c4bc48d7c 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -4,12 +4,14 @@ version = "0.1.0" edition = "2021" [dependencies] -sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "a12b43ee359ed8f1fa7433f0534217da3b83e9ef" } -sdmmc_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "e590aa23448cfeaa1398722a86472fa61122a7ba" } -sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "e590aa23448cfeaa1398722a86472fa61122a7ba", features = ["sel4-microkit"] } +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e", features = ["alloc"] } +sel4-panicking-env = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" } +meson_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "3fa77fa08c8ef9c7927a75bbd0b06083c6d21fe5", optional = true } +sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "3fa77fa08c8ef9c7927a75bbd0b06083c6d21fe5" } sddf_timer = { path = "../../../include/sddf_rust/timer" } -# sdmmc_hal = { path = "sdmmc_hal/meson" } -# sdmmc_protocol = { path = "sdmmc_protocol", features = ["sel4-microkit"] } + +[features] +meson = ["dep:meson_hal"] [build] build = "build.rs" \ No newline at end of file diff --git a/drivers/blk/sdmmc/blk_driver.mk b/drivers/blk/sdmmc/blk_driver.mk index 67c669d91..a0b067538 100644 --- a/drivers/blk/sdmmc/blk_driver.mk +++ b/drivers/blk/sdmmc/blk_driver.mk @@ -38,6 +38,7 @@ blk_driver.elf: $(BUILD_DIR) blk/sdmmc/meson/libsddfblk.a -Z build-std=core,alloc,compiler_builtins \ -Z build-std-features=compiler-builtins-mem \ --target-dir $(BUILD_DIR)/blk/sdmmc/meson/ \ - --target support/targets/aarch64-sel4-microkit-minimal.json && \ + --target support/targets/aarch64-sel4-microkit-minimal.json \ + --features "meson" cp $(BUILD_DIR)/blk/sdmmc/meson/aarch64-sel4-microkit-minimal/debug/blk_driver.elf $(BUILD_DIR) echo "Build complete: $(TARGET_ELF)" \ No newline at end of file diff --git a/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/Cargo.toml b/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/Cargo.toml deleted file mode 100644 index e91f47ed9..000000000 --- a/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "sel4-microkit-support" -version = "0.1.0" -edition = "2021" -authors = ["Cheng Li 李澄 "] - -[lib] -name = "sel4_microkit_support" -path = "lib.rs" - -# Optional OS-specific crates can be included to enhance functionality, -# such as providing improved debugging output and more efficient sleep functions -# (as opposed to simple spin-wait). -[dependencies] -sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "5826be1a6c83803faeaa79cd9f164c26a5a32e7c" } \ No newline at end of file diff --git a/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/lib.rs b/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/lib.rs deleted file mode 100644 index acdc4dcad..000000000 --- a/drivers/blk/sdmmc/optional_os_support/sel4-microkit-support/lib.rs +++ /dev/null @@ -1,40 +0,0 @@ -#![no_std] // Don't link the standard library - -use core::hint; - -/// Api should only accessible in this crate -#[doc(hidden)] -pub use sel4_microkit::debug_print; - -/// Spins for an approximate number of nanoseconds. -/// -/// This function is unreliable because it does not account for CPU frequency, -/// power-saving modes, or other hardware variations. It should be used only -/// in situations where approximate delays are sufficient. -/// -/// # Arguments -/// -/// * `time_ns` - The approximate number of nanoseconds to spin-wait. -/// -/// # Notes -/// -/// This function uses a simple busy-wait loop combined with `hint::spin_loop` -/// to reduce contention during the wait. The actual delay may vary significantly -/// depending on the hardware and system load. -/// -/// For accurate delays, use a hardware timer or platform-specific APIs. -/// TODO: Change this to using a timer to stop later -#[inline] -pub fn process_wait_unreliable(time_ns: u64) { - for _ in 0..time_ns { - hint::spin_loop(); // Use spin loop hint to reduce contention during the wait - } -} - -/// `sel4-microkit` specific serial implementation -#[macro_export] -macro_rules! debug_log { - ($($arg:tt)*) => { - $crate::debug_print!($($arg)*); - } -} diff --git a/drivers/blk/sdmmc/rust-toolchain.toml b/drivers/blk/sdmmc/rust-toolchain.toml index e4869f8e1..65e0b332e 100644 --- a/drivers/blk/sdmmc/rust-toolchain.toml +++ b/drivers/blk/sdmmc/rust-toolchain.toml @@ -5,6 +5,6 @@ # [toolchain] -channel = "nightly-2025-02-26" +channel = "nightly-2025-07-07" components = [ "rust-src", "rustc-dev", "llvm-tools-preview" ] profile = "default" diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 653efec73..d5f51fd6e 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -5,6 +5,7 @@ extern crate alloc; mod sddf_blk; +mod sel4_microkit_os; use core::{ future::Future, @@ -14,27 +15,34 @@ use core::{ use alloc::boxed::Box; use sddf_blk::{ - blk_dequeue_req_helper, blk_enqueue_resp_helper, blk_queue_empty_req_helper, + blk_dequeue_req_helper, blk_device_init_data_ioaddr, blk_device_init_data_vaddr, + blk_device_regs_vaddr, blk_enqueue_resp_helper, blk_queue_empty_req_helper, blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkRequest, BlkStatus, - blk_device_regs_vaddr, blk_device_init_data_vaddr, blk_device_init_data_ioaddr }; use sddf_timer::timer::Timer; -use sdmmc_hal::meson_gx_mmc::SdmmcMesonHardware; -use sdmmc_protocol::sdmmc::{SdmmcError, SdmmcProtocol}; -use sdmmc_protocol::sdmmc_traits::SdmmcHardware; -use sel4_microkit::{debug_print, debug_println, protection_domain, Channel, Handler, Infallible}; +use crate::sel4_microkit_os::{platform::VOLTAGE, SerialOps, TimerOps}; const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(0); const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(1); - const TIMER_CHANNEL: usize = 2; -const TIMER: Timer = Timer::new(sel4_microkit::Channel::new(TIMER_CHANNEL)); +const TIMER: TimerOps = TimerOps::new(Timer::new(sel4_microkit::Channel::new(TIMER_CHANNEL))); +const SERIAL: SerialOps = SerialOps::new(); + +use sdmmc_protocol::{sdmmc::{mmc_struct::CardInfo, HostInfo}, sdmmc_traits::SdmmcHardware}; +use sdmmc_protocol::{ + sdmmc::{SdmmcError, SdmmcProtocol}, + sdmmc_os::{Sleep, VoltageOps}, +}; +use sel4_microkit::{ + debug_print, debug_println, protection_domain, ChannelSet, Handler, Infallible, +}; const SDCARD_SECTOR_SIZE: u32 = 512; const SDDF_TRANSFER_SIZE: u32 = 4096; const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE; +// If people don't like the retry logic being here, maybe I can move it to the protocol layer? const RETRY_CHANCE: u16 = 5; // Debug function for printing out content in one block @@ -73,26 +81,19 @@ fn create_dummy_waker() -> Waker { } #[protection_domain(heap_size = 0x10000)] -fn init() -> HandlerImpl { +fn init() -> impl Handler { debug_println!("Driver init!"); - unsafe { - blk_queue_init_helper(); - } - let regs_base = unsafe { - blk_device_regs_vaddr() - }; - let meson_hal: SdmmcMesonHardware = unsafe { SdmmcMesonHardware::new(regs_base) }; + + let regs_base = unsafe { blk_device_regs_vaddr() }; + + let hal = unsafe { crate::sel4_microkit_os::platform::platform_hal() }; let unsafe_stolen_memory: &mut [u8; 64]; - // This line of code actually is very unsafe! + // This line of code actually is very unsafe!S // Considering the memory is stolen from the memory that has sdcard registers mapped in - let init_data_vaddr = unsafe { - blk_device_init_data_vaddr() - }; - let init_data_ioaddr = unsafe { - blk_device_init_data_ioaddr() - }; + let init_data_vaddr = unsafe { blk_device_init_data_vaddr() }; + let init_data_ioaddr = unsafe { blk_device_init_data_ioaddr() }; unsafe { let stolen_memory_addr = init_data_vaddr as *mut [u8; 64]; assert!(stolen_memory_addr as usize % 8 == 0); @@ -100,7 +101,7 @@ fn init() -> HandlerImpl { } // Handling result in two different ways, by matching and unwrap_or_else - let res = SdmmcProtocol::new(meson_hal); + let res = SdmmcProtocol::new(hal, TIMER, Some(VOLTAGE)); let mut sdmmc_host = match res { Ok(host) => host, Err(err) => panic!("SDMMC: Error at init {:?}", err), @@ -110,6 +111,13 @@ fn init() -> HandlerImpl { .setup_card() .unwrap_or_else(|error| panic!("SDMMC: Error at setup {:?}", error)); + let host_info: HostInfo = sdmmc_host.get_host_info(); + let card_info: CardInfo = sdmmc_host.card_info().unwrap(); + + unsafe { + blk_queue_init_helper(card_info.card_capacity); + } + let _ = sdmmc_host.config_interrupt(false, false); // Print out one block to check if read works @@ -118,14 +126,14 @@ fn init() -> HandlerImpl { // TODO: Should tuning be possible to fail? unsafe { sdmmc_host - .tune_performance( - unsafe_stolen_memory, - dummy_cache_invalidate_function, - init_data_ioaddr, - ) - .unwrap_or_else(|error| panic!("SDMMC: Error at tuning performance {:?}", error)); + .tune_performance( + unsafe_stolen_memory, + dummy_cache_invalidate_function, + init_data_ioaddr, + ) + .unwrap_or_else(|error| panic!("SDMMC: Error at tuning performance {:?}", error)); } - + unsafe { print_one_block(unsafe_stolen_memory.as_ptr(), 64); } @@ -137,216 +145,236 @@ fn init() -> HandlerImpl { sdmmc: Some(sdmmc_host), request: None, retry: RETRY_CHANCE, + host_info, + card_info, } } -struct HandlerImpl { - future: Option, SdmmcProtocol)>>>>, - sdmmc: Option>, +struct HandlerImpl { + future: Option, SdmmcProtocol)>>>>, + sdmmc: Option>, request: Option, retry: u16, + host_info: HostInfo, + card_info: CardInfo, } -impl Handler for HandlerImpl { +impl Handler for HandlerImpl +where + T: SdmmcHardware + 'static, + S: Sleep + 'static, + V: VoltageOps + 'static, +{ type Error = Infallible; - fn notified(&mut self, channel: Channel) -> Result<(), Self::Error> { - if channel.index() != INTERRUPT.index() && channel.index() != BLK_VIRTUALIZER.index() { - debug_println!( - "SDMMC_DRIVER: Unknown channel sent me message: {}", - channel.index() - ); - return Ok(()); - } + fn notified(&mut self, channel_set: ChannelSet) -> Result<(), Self::Error> { + for channel in channel_set.iter() { + if channel.index() != INTERRUPT.index() && channel.index() != BLK_VIRTUALIZER.index() { + debug_println!( + "SDMMC_DRIVER: Unknown channel sent me message: {}", + channel.index() + ); + return Ok(()); + } - let mut notify_virt: bool = false; + let mut notify_virt: bool = false; - 'process_notification: { - // Polling if receive interrupt notification - if channel.index() == INTERRUPT.index() { - if let Some(request) = &mut self.request { - if let Some(future) = &mut self.future { - let waker = create_dummy_waker(); - let mut cx = Context::from_waker(&waker); - match future.as_mut().poll(&mut cx) { - Poll::Ready((result, sdmmc)) => { - // debug_println!("SDMMC_DRIVER: Future completed with result"); - self.future = None; // Reset the future once done - self.sdmmc = Some(sdmmc); - if result.is_err() { - debug_println!( - "SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possibly retry!" - ); - self.retry -= 1; - } else { - // Deduct finished count from count - request.success_count += request.count_to_do; - request.count -= request.count_to_do; - } - if request.count == 0 { - let resp_status = BlkStatus::BlkRespOk; - notify_virt = true; - unsafe { - // The using try_into() to convert u32 to u16 should not be necessary unless - // there are bugs in the code can it fail - // change it later - blk_enqueue_resp_helper( - resp_status, - (request.success_count / SDDF_TO_REAL_SECTOR).try_into().unwrap(), - request.id, + 'process_notification: { + // Polling if receive interrupt notification + if channel.index() == INTERRUPT.index() { + if let Some(request) = &mut self.request { + if let Some(future) = &mut self.future { + let waker = create_dummy_waker(); + let mut cx = Context::from_waker(&waker); + match future.as_mut().poll(&mut cx) { + Poll::Ready((result, sdmmc)) => { + // debug_println!("SDMMC_DRIVER: Future completed with result"); + self.future = None; // Reset the future once done + self.sdmmc = Some(sdmmc); + if result.is_err() { + debug_println!( + "SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possibly retry!" ); + self.retry -= 1; + } else { + // Deduct finished count from count + request.success_count += request.count_to_do; + request.count -= request.count_to_do; } - self.request = None; - } else if self.retry == 0 { - let resp_status = BlkStatus::BlkRespSeekError; - notify_virt = true; - unsafe { - blk_enqueue_resp_helper( - resp_status, - (request.success_count / SDDF_TO_REAL_SECTOR).try_into().unwrap(), - request.id, - ); + if request.count == 0 { + let resp_status = BlkStatus::BlkRespOk; + notify_virt = true; + unsafe { + // The using try_into() to convert u32 to u16 should not be necessary unless + // there are bugs in the code cause the operation to fail + blk_enqueue_resp_helper( + resp_status, + (request.success_count / SDDF_TO_REAL_SECTOR) + .try_into() + .unwrap(), + request.id, + ); + } + self.request = None; + } else if self.retry == 0 { + let resp_status = BlkStatus::BlkRespSeekError; + notify_virt = true; + unsafe { + blk_enqueue_resp_helper( + resp_status, + (request.success_count / SDDF_TO_REAL_SECTOR) + .try_into() + .unwrap(), + request.id, + ); + } + self.request = None; } - self.request = None; + } + Poll::Pending => { + // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); + // Since the future is not ready, no other request can be dequeued, exit the big loop + break 'process_notification; } } - Poll::Pending => { - // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); - // Since the future is not ready, no other request can be dequeued, exit the big loop - break 'process_notification; - } + } else { + panic!( + "SDMMC: Receive a hardware interrupt despite not having a future!" + ); } - } else { - panic!("SDMMC: Receive a hardware interrupt despite not having a future!"); } } - } - while self.request.is_none() - && unsafe { blk_queue_empty_req_helper() == 0 && blk_queue_full_resp_helper() == 0 } - { - let mut request: BlkRequest = BlkRequest { - request_code: BlkOp::BlkReqFlush, - io_or_offset: 0, - block_number: 0, - count: 0, - success_count: 0, - count_to_do: 0, - id: 0, - }; - let mut sddf_count: u16 = 0; - unsafe { - blk_dequeue_req_helper( - &mut request.request_code as *mut BlkOp, - &mut request.io_or_offset as *mut u64, - &mut request.block_number as *mut u64, - &mut sddf_count as *mut u16, - &mut request.id as *mut u32, - ); - } - // TODO: Consider how to add integer overflow check here - request.block_number = request.block_number * SDDF_TO_REAL_SECTOR as u64; - request.count = (sddf_count as u32) * SDDF_TO_REAL_SECTOR; - // Print the retrieved values - /* - debug_println!("io_or_offset: 0x{:x}", request.io_or_offset);// Simple u64 - debug_println!("block_number: {}", request.block_number); // Simple u32 - debug_println!("count: {}", request.count); // Simple u16 - debug_println!("id: {}", request.id); // Simple u32 - */ - match request.request_code { - BlkOp::BlkReqRead => { - // Reset retry chance here - self.retry = RETRY_CHANCE; - self.request = Some(request); - break; + while self.request.is_none() + && unsafe { + blk_queue_empty_req_helper() == 0 && blk_queue_full_resp_helper() == 0 } - BlkOp::BlkReqWrite => { - // Reset retry chance here - self.retry = RETRY_CHANCE; - self.request = Some(request); - break; + { + let mut request: BlkRequest = BlkRequest { + request_code: BlkOp::BlkReqFlush, + io_or_offset: 0, + block_number: 0, + count: 0, + success_count: 0, + count_to_do: 0, + id: 0, + }; + let mut sddf_count: u16 = 0; + unsafe { + blk_dequeue_req_helper( + &mut request.request_code as *mut BlkOp, + &mut request.io_or_offset as *mut u64, + &mut request.block_number as *mut u64, + &mut sddf_count as *mut u16, + &mut request.id as *mut u32, + ); } - _ => { - // For other request, enqueue response - notify_virt = true; - unsafe { - blk_enqueue_resp_helper(BlkStatus::BlkRespOk, 0, request.id); - } - } - } - } - - // If future is empty - if let Some(request) = &mut self.request { - if let None = self.future { + // TODO: Consider how to add integer overflow check here + request.block_number = request.block_number * SDDF_TO_REAL_SECTOR as u64; + request.count = (sddf_count as u32) * SDDF_TO_REAL_SECTOR; + // Print the retrieved values + /* + debug_println!("io_or_offset: 0x{:x}", request.io_or_offset);// Simple u64 + debug_println!("block_number: {}", request.block_number); // Simple u32 + debug_println!("count: {}", request.count); // Simple u16 + debug_println!("id: {}", request.id); // Simple u32 + */ match request.request_code { BlkOp::BlkReqRead => { - // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer - request.count_to_do = core::cmp::min( - request.count, - sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER, - ); - if let Some(sdmmc) = self.sdmmc.take() { - self.future = Some(Box::pin(sdmmc.read_block( - request.count_to_do, - request.block_number as u64 + request.success_count as u64, - request.io_or_offset - + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64, - ))); - } else { - panic!("SDMMC_DRIVER: The sdmmc should be here since the future should be empty!!!") - } + // Reset retry chance here + self.retry = RETRY_CHANCE; + self.request = Some(request); + break; } BlkOp::BlkReqWrite => { - // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer - request.count_to_do = core::cmp::min( - request.count, - sdmmc_hal::meson_gx_mmc::MAX_BLOCK_PER_TRANSFER, - ); - if let Some(sdmmc) = self.sdmmc.take() { - self.future = Some(Box::pin(sdmmc.write_block( - request.count_to_do, - request.block_number as u64 + request.success_count as u64, - request.io_or_offset - + request.success_count as u64 * SDCARD_SECTOR_SIZE as u64, - ))); - } else { - panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") - } + // Reset retry chance here + self.retry = RETRY_CHANCE; + self.request = Some(request); + break; } _ => { - panic!("SDMMC_DRIVER: You should not reach here!") + // For other request, enqueue response + notify_virt = true; + unsafe { + blk_enqueue_resp_helper(BlkStatus::BlkRespOk, 0, request.id); + } } } - let waker = create_dummy_waker(); - let mut cx = Context::from_waker(&waker); - // Poll the future once to make it start working! - if let Some(ref mut future) = self.future { - match future.as_mut().poll(&mut cx) { - Poll::Ready(_) => { - panic!( - "SDMMC: The newly created future returned immediately! + } + + // If future is empty + if let Some(request) = &mut self.request { + if let None = self.future { + match request.request_code { + BlkOp::BlkReqRead => { + // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer + request.count_to_do = core::cmp::min( + request.count, + self.host_info.max_block_per_req, + ); + if let Some(sdmmc) = self.sdmmc.take() { + self.future = Some(Box::pin(sdmmc.read_block( + request.count_to_do, + request.block_number as u64 + request.success_count as u64, + request.io_or_offset + + request.success_count as u64 + * SDCARD_SECTOR_SIZE as u64, + ))); + } else { + panic!("SDMMC_DRIVER: The sdmmc should be here since the future should be empty!!!") + } + } + BlkOp::BlkReqWrite => { + // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer + request.count_to_do = core::cmp::min( + request.count, + self.host_info.max_block_per_req, + ); + if let Some(sdmmc) = self.sdmmc.take() { + self.future = Some(Box::pin(sdmmc.write_block( + request.count_to_do, + request.block_number as u64 + request.success_count as u64, + request.io_or_offset + + request.success_count as u64 + * SDCARD_SECTOR_SIZE as u64, + ))); + } else { + panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") + } + } + _ => { + panic!("SDMMC_DRIVER: You should not reach here!") + } + } + let waker = create_dummy_waker(); + let mut cx = Context::from_waker(&waker); + // Poll the future once to make it start working! + if let Some(ref mut future) = self.future { + match future.as_mut().poll(&mut cx) { + Poll::Ready(_) => { + panic!( + "SDMMC: The newly created future returned immediately! Most likely the future contain an invalid request! Double check request sanitize process!" - ) + ) + } + Poll::Pending => break 'process_notification, } - Poll::Pending => break 'process_notification, } } } } - } - if notify_virt == true { - // debug_println!("SDMMC_DRIVER: Notify the BLK_VIRTUALIZER!"); - BLK_VIRTUALIZER.notify(); - } - // Ack irq - if channel.index() == INTERRUPT.index() { - let err = channel.irq_ack(); - if err.is_err() { - panic!("SDMMC: Cannot acknowledge interrupt for CPU!") + if notify_virt == true { + // debug_println!("SDMMC_DRIVER: Notify the BLK_VIRTUALIZER!"); + BLK_VIRTUALIZER.notify(); + } + // Ack irq + if channel.index() == INTERRUPT.index() { + let err = channel.irq_ack(); + if err.is_err() { + panic!("SDMMC: Cannot acknowledge interrupt for CPU!") + } } } Ok(()) diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs index 0ef828fa8..5a4ae3fb7 100644 --- a/drivers/blk/sdmmc/src/sddf_blk/mod.rs +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -1,5 +1,5 @@ unsafe extern "C" { - pub unsafe fn blk_queue_init_helper(); + pub unsafe fn blk_queue_init_helper(capacity: u64); pub unsafe fn blk_device_regs_vaddr() -> u64; pub unsafe fn blk_device_init_data_vaddr() -> u64; diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/sdmmc/src/sddf_helper.c index 0544e4e55..71552edc8 100644 --- a/drivers/blk/sdmmc/src/sddf_helper.c +++ b/drivers/blk/sdmmc/src/sddf_helper.c @@ -8,7 +8,7 @@ __attribute__((__section__(".blk_driver_config"))) blk_driver_config_t config; blk_queue_handle_t blk_queue; -void blk_queue_init_helper() { +void blk_queue_init_helper(uint64_t capacity) { blk_queue_init(&blk_queue, config.virt.req_queue.vaddr, config.virt.resp_queue.vaddr, config.virt.num_buffers); blk_storage_info_t *storage_info = config.virt.storage_info.vaddr; diff --git a/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs b/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs new file mode 100644 index 000000000..1a2cfc33f --- /dev/null +++ b/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs @@ -0,0 +1,44 @@ +use sddf_timer::timer::Timer; +use sdmmc_protocol::sdmmc_os::{Log, Sleep}; +use sel4_panicking_env::__debug_print_macro_helper; + +#[cfg(feature = "meson")] +mod odroidc4; + +#[cfg(feature = "meson")] +pub(crate) mod platform { + pub(crate) use crate::sel4_microkit_os::odroidc4::{platform_hal, VOLTAGE}; +} + +const NS_IN_US: u64 = 1000; + +/// Wrapper to work around Rust's orphan rule +pub struct TimerOps { + timer: Timer, +} + +impl TimerOps { + pub const fn new(timer: Timer) -> Self { + TimerOps { timer } + } +} + +impl Sleep for TimerOps { + fn usleep(&mut self, time_us: u32) { + self.timer.set_timeout(time_us as u64 * NS_IN_US); + } +} + +pub struct SerialOps {} + +impl SerialOps { + pub const fn new() -> Self { + SerialOps {} + } +} + +impl Log for SerialOps { + fn log(&self, args: core::fmt::Arguments) { + __debug_print_macro_helper(args); + } +} diff --git a/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs b/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs new file mode 100644 index 000000000..b05a27f91 --- /dev/null +++ b/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs @@ -0,0 +1,136 @@ +use core::ptr; + +use sdmmc_protocol::{ + sdmmc::{MmcPowerMode, MmcSignalVoltage, SdmmcError}, + sdmmc_os::VoltageOps, +}; + +use meson_hal::meson_gx_mmc::SdmmcMesonHardware; +use sel4_microkit::var; + +pub struct Odroidc4VoltageSwitch; +pub(crate) const VOLTAGE: Odroidc4VoltageSwitch = Odroidc4VoltageSwitch::new(); + +/// Check the AO_GPIO_O_EN_N register in S905X3 datasheet +const AO_RTI_OUTPUT_ENABLE_REG: u64 = 0xff800024; +const AO_RTI_OUTPUT_LEVEL_REG: u64 = 0xff800034; +const AO_RTI_PULL_UP_EN_REG: u64 = 0xff800030; + +/// Check GPIOAO_x Multi-Function Pin register in in S905X3 datasheet +/// We probably need to ensure the pinmux is set up correctly to use the correct pin func +/// Which is missing now!!! This is especially important for GPIOAO_6 as the default function +/// does not seems to be the one we want to use, for GPIO_AO_3 it should be fine as the default +/// one is the one we want to use +const GPIO_AO_3: u32 = 1 << 3; +const GPIO_AO_6: u32 = 1 << 6; + +impl Odroidc4VoltageSwitch { + pub const fn new() -> Self { + Odroidc4VoltageSwitch {} + } +} + +impl VoltageOps for Odroidc4VoltageSwitch { + fn card_voltage_switch(&mut self, voltage: MmcSignalVoltage) -> Result<(), SdmmcError> { + match voltage { + MmcSignalVoltage::Voltage330 => { + let mut value: u32; + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); + } + value &= !GPIO_AO_6; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); + } + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + value &= !GPIO_AO_6; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); + } + } + MmcSignalVoltage::Voltage180 => { + let mut value: u32; + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); + } + value &= !GPIO_AO_6; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); + } + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + value |= GPIO_AO_6; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); + } + } + MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EINVAL), + } + // Disable pull-up/down for gpioAO_6 + let mut value: u32; + + unsafe { + value = ptr::read_volatile(AO_RTI_PULL_UP_EN_REG as *const u32); + } + value &= !GPIO_AO_6; + + unsafe { + ptr::write_volatile(AO_RTI_PULL_UP_EN_REG as *mut u32, value); + } + + Ok(()) + } + + fn card_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { + let mut value: u32; + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); + } + // If the GPIO pin is not being set as output, set it to output first + if value & GPIO_AO_3 != 0 { + value &= !GPIO_AO_3; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); + } + } + match power_mode { + MmcPowerMode::On => { + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + if value & GPIO_AO_3 == 0 { + value |= GPIO_AO_3; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); + } + } + self.card_voltage_switch(MmcSignalVoltage::Voltage330)?; + } + MmcPowerMode::Off => { + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + if value & GPIO_AO_3 != 0 { + value &= !GPIO_AO_3; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); + } + } + } + _ => return Err(SdmmcError::EINVAL), + } + Ok(()) + } +} + +pub unsafe fn platform_hal() -> SdmmcMesonHardware { + // Use this var! will actually leak the memory that use to store the address + let reg_base: &u64 = var!(sdmmc: u64 = 0x0); + if *reg_base == 0x0 { + panic!("SDMMC: SDMMC register address is not being set correctly!") + } + unsafe { SdmmcMesonHardware::new(*reg_base) } +} From f6735fdd4d0a76fe3c7669eff6ccebb3652f6c12 Mon Sep 17 00:00:00 2001 From: Cheng Date: Tue, 5 Aug 2025 16:46:02 +1000 Subject: [PATCH 29/48] Code compiled. Cannot use sddf timer interfaces due to duplicate definition of microkit symbol. Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 1 - drivers/blk/sdmmc/blk_driver.mk | 7 -- drivers/blk/sdmmc/src/main.rs | 105 +++++++++--------- drivers/blk/sdmmc/src/sddf_helper.c | 2 +- drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs | 11 +- .../sdmmc/src/sel4_microkit_os/odroidc4.rs | 9 +- 6 files changed, 57 insertions(+), 78 deletions(-) diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index c4bc48d7c..438cad493 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -8,7 +8,6 @@ sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf7 sel4-panicking-env = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" } meson_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "3fa77fa08c8ef9c7927a75bbd0b06083c6d21fe5", optional = true } sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "3fa77fa08c8ef9c7927a75bbd0b06083c6d21fe5" } -sddf_timer = { path = "../../../include/sddf_rust/timer" } [features] meson = ["dep:meson_hal"] diff --git a/drivers/blk/sdmmc/blk_driver.mk b/drivers/blk/sdmmc/blk_driver.mk index a0b067538..aeebfd92c 100644 --- a/drivers/blk/sdmmc/blk_driver.mk +++ b/drivers/blk/sdmmc/blk_driver.mk @@ -1,10 +1,3 @@ -# -# Copyright 2023, Colias Group, LLC -# -# SPDX-License-Identifier: BSD-2-Clause -# - - # Get current dir SDMMC_DRIVER_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index d5f51fd6e..2c4adb326 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -19,17 +19,18 @@ use sddf_blk::{ blk_device_regs_vaddr, blk_enqueue_resp_helper, blk_queue_empty_req_helper, blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkRequest, BlkStatus, }; -use sddf_timer::timer::Timer; use crate::sel4_microkit_os::{platform::VOLTAGE, SerialOps, TimerOps}; const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(0); const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(1); -const TIMER_CHANNEL: usize = 2; -const TIMER: TimerOps = TimerOps::new(Timer::new(sel4_microkit::Channel::new(TIMER_CHANNEL))); +const TIMER: TimerOps = TimerOps::new(); const SERIAL: SerialOps = SerialOps::new(); -use sdmmc_protocol::{sdmmc::{mmc_struct::CardInfo, HostInfo}, sdmmc_traits::SdmmcHardware}; +use sdmmc_protocol::{ + sdmmc::{mmc_struct::CardInfo, HostInfo}, + sdmmc_traits::SdmmcHardware, +}; use sdmmc_protocol::{ sdmmc::{SdmmcError, SdmmcProtocol}, sdmmc_os::{Sleep, VoltageOps}, @@ -42,9 +43,6 @@ const SDCARD_SECTOR_SIZE: u32 = 512; const SDDF_TRANSFER_SIZE: u32 = 4096; const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE; -// If people don't like the retry logic being here, maybe I can move it to the protocol layer? -const RETRY_CHANCE: u16 = 5; - // Debug function for printing out content in one block #[allow(dead_code)] unsafe fn print_one_block(ptr: *const u8, num: usize) { @@ -82,18 +80,21 @@ fn create_dummy_waker() -> Waker { #[protection_domain(heap_size = 0x10000)] fn init() -> impl Handler { - debug_println!("Driver init!"); + unsafe { + sdmmc_protocol::sdmmc_os::set_logger(&SERIAL).unwrap(); + } let regs_base = unsafe { blk_device_regs_vaddr() }; - let hal = unsafe { crate::sel4_microkit_os::platform::platform_hal() }; + let hal = unsafe { crate::sel4_microkit_os::platform::platform_hal(regs_base) }; let unsafe_stolen_memory: &mut [u8; 64]; - // This line of code actually is very unsafe!S + // This line of code actually is very unsafe! // Considering the memory is stolen from the memory that has sdcard registers mapped in let init_data_vaddr = unsafe { blk_device_init_data_vaddr() }; let init_data_ioaddr = unsafe { blk_device_init_data_ioaddr() }; + unsafe { let stolen_memory_addr = init_data_vaddr as *mut [u8; 64]; assert!(stolen_memory_addr as usize % 8 == 0); @@ -114,10 +115,6 @@ fn init() -> impl Handler { let host_info: HostInfo = sdmmc_host.get_host_info(); let card_info: CardInfo = sdmmc_host.card_info().unwrap(); - unsafe { - blk_queue_init_helper(card_info.card_capacity); - } - let _ = sdmmc_host.config_interrupt(false, false); // Print out one block to check if read works @@ -140,11 +137,15 @@ fn init() -> impl Handler { // Should always succeed, at least for odroid C4 let _ = sdmmc_host.config_interrupt(true, false); + + unsafe { + blk_queue_init_helper(card_info.card_capacity); + } + HandlerImpl { future: None, sdmmc: Some(sdmmc_host), request: None, - retry: RETRY_CHANCE, host_info, card_info, } @@ -154,7 +155,6 @@ struct HandlerImpl { future: Option, SdmmcProtocol)>>>>, sdmmc: Option>, request: Option, - retry: u16, host_info: HostInfo, card_info: CardInfo, } @@ -191,32 +191,28 @@ where // debug_println!("SDMMC_DRIVER: Future completed with result"); self.future = None; // Reset the future once done self.sdmmc = Some(sdmmc); - if result.is_err() { - debug_println!( - "SDMMC_DRIVER: DISK ERROR ENCOUNTERED, possibly retry!" - ); - self.retry -= 1; - } else { + if result.is_ok() { // Deduct finished count from count request.success_count += request.count_to_do; request.count -= request.count_to_do; - } - if request.count == 0 { - let resp_status = BlkStatus::BlkRespOk; - notify_virt = true; - unsafe { - // The using try_into() to convert u32 to u16 should not be necessary unless - // there are bugs in the code cause the operation to fail - blk_enqueue_resp_helper( - resp_status, - (request.success_count / SDDF_TO_REAL_SECTOR) - .try_into() - .unwrap(), - request.id, - ); + + if request.count == 0 { + let resp_status = BlkStatus::BlkRespOk; + notify_virt = true; + unsafe { + // The using try_into() to convert u32 to u16 should not be necessary unless + // there are bugs in the code cause the operation to fail + blk_enqueue_resp_helper( + resp_status, + (request.success_count / SDDF_TO_REAL_SECTOR) + .try_into() + .unwrap(), + request.id, + ); + } + self.request = None; } - self.request = None; - } else if self.retry == 0 { + } else { let resp_status = BlkStatus::BlkRespSeekError; notify_virt = true; unsafe { @@ -279,16 +275,23 @@ where debug_println!("count: {}", request.count); // Simple u16 debug_println!("id: {}", request.id); // Simple u32 */ + + // Check if the request is valid + if (request.count as u64 + request.block_number as u64) + * SDCARD_SECTOR_SIZE as u64 + > self.card_info.card_capacity + { + unsafe { + blk_enqueue_resp_helper(BlkStatus::BlkRespSeekError, 0, request.id); + } + } + match request.request_code { BlkOp::BlkReqRead => { - // Reset retry chance here - self.retry = RETRY_CHANCE; self.request = Some(request); break; } BlkOp::BlkReqWrite => { - // Reset retry chance here - self.retry = RETRY_CHANCE; self.request = Some(request); break; } @@ -308,10 +311,8 @@ where match request.request_code { BlkOp::BlkReqRead => { // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer - request.count_to_do = core::cmp::min( - request.count, - self.host_info.max_block_per_req, - ); + request.count_to_do = + core::cmp::min(request.count, self.host_info.max_block_per_req); if let Some(sdmmc) = self.sdmmc.take() { self.future = Some(Box::pin(sdmmc.read_block( request.count_to_do, @@ -326,10 +327,8 @@ where } BlkOp::BlkReqWrite => { // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer - request.count_to_do = core::cmp::min( - request.count, - self.host_info.max_block_per_req, - ); + request.count_to_do = + core::cmp::min(request.count, self.host_info.max_block_per_req); if let Some(sdmmc) = self.sdmmc.take() { self.future = Some(Box::pin(sdmmc.write_block( request.count_to_do, @@ -352,11 +351,7 @@ where if let Some(ref mut future) = self.future { match future.as_mut().poll(&mut cx) { Poll::Ready(_) => { - panic!( - "SDMMC: The newly created future returned immediately! - Most likely the future contain an invalid request! - Double check request sanitize process!" - ) + panic!("SDMMC: RECEIVED INVALID REQUEST! Check request validation code!"); } Poll::Pending => break 'process_notification, } diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/sdmmc/src/sddf_helper.c index 71552edc8..1148896a9 100644 --- a/drivers/blk/sdmmc/src/sddf_helper.c +++ b/drivers/blk/sdmmc/src/sddf_helper.c @@ -14,7 +14,7 @@ void blk_queue_init_helper(uint64_t capacity) { blk_storage_info_t *storage_info = config.virt.storage_info.vaddr; storage_info->sector_size = 512; storage_info->block_size = 1; - storage_info->capacity = 0xFFFFFFFFFF; + storage_info->capacity = capacity; storage_info->ready = true; } diff --git a/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs b/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs index 1a2cfc33f..35383355c 100644 --- a/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs +++ b/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs @@ -1,4 +1,3 @@ -use sddf_timer::timer::Timer; use sdmmc_protocol::sdmmc_os::{Log, Sleep}; use sel4_panicking_env::__debug_print_macro_helper; @@ -13,19 +12,17 @@ pub(crate) mod platform { const NS_IN_US: u64 = 1000; /// Wrapper to work around Rust's orphan rule -pub struct TimerOps { - timer: Timer, -} +pub struct TimerOps {} impl TimerOps { - pub const fn new(timer: Timer) -> Self { - TimerOps { timer } + pub const fn new() -> Self { + TimerOps {} } } impl Sleep for TimerOps { fn usleep(&mut self, time_us: u32) { - self.timer.set_timeout(time_us as u64 * NS_IN_US); + sdmmc_protocol::sdmmc_os::process_wait_unreliable(time_us as u64 * NS_IN_US); } } diff --git a/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs b/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs index b05a27f91..1980f23a4 100644 --- a/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs +++ b/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs @@ -126,11 +126,6 @@ impl VoltageOps for Odroidc4VoltageSwitch { } } -pub unsafe fn platform_hal() -> SdmmcMesonHardware { - // Use this var! will actually leak the memory that use to store the address - let reg_base: &u64 = var!(sdmmc: u64 = 0x0); - if *reg_base == 0x0 { - panic!("SDMMC: SDMMC register address is not being set correctly!") - } - unsafe { SdmmcMesonHardware::new(*reg_base) } +pub unsafe fn platform_hal(regs_base: u64) -> SdmmcMesonHardware { + unsafe { SdmmcMesonHardware::new(regs_base) } } From 6eb890893e314b1dd47f2d5283978d66f68c7b82 Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 6 Aug 2025 12:19:39 +1000 Subject: [PATCH 30/48] Add the startup text Signed-off-by: Cheng --- drivers/blk/sdmmc/src/main.rs | 8 +++++--- drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 2c4adb326..9f34f8c61 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -112,6 +112,8 @@ fn init() -> impl Handler { .setup_card() .unwrap_or_else(|error| panic!("SDMMC: Error at setup {:?}", error)); + sdmmc_host.print_card_info(); + let host_info: HostInfo = sdmmc_host.get_host_info(); let card_info: CardInfo = sdmmc_host.card_info().unwrap(); @@ -131,9 +133,9 @@ fn init() -> impl Handler { .unwrap_or_else(|error| panic!("SDMMC: Error at tuning performance {:?}", error)); } - unsafe { - print_one_block(unsafe_stolen_memory.as_ptr(), 64); - } + // unsafe { + // print_one_block(unsafe_stolen_memory.as_ptr(), 64); + // } // Should always succeed, at least for odroid C4 let _ = sdmmc_host.config_interrupt(true, false); diff --git a/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs b/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs index 1980f23a4..ded89424e 100644 --- a/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs +++ b/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs @@ -6,7 +6,6 @@ use sdmmc_protocol::{ }; use meson_hal::meson_gx_mmc::SdmmcMesonHardware; -use sel4_microkit::var; pub struct Odroidc4VoltageSwitch; pub(crate) const VOLTAGE: Odroidc4VoltageSwitch = Odroidc4VoltageSwitch::new(); From d29ea73d4820da7bb208e5528d724ba7fa3756fd Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 6 Aug 2025 12:51:44 +1000 Subject: [PATCH 31/48] Update the building process Signed-off-by: Cheng --- examples/blk/blk.mk | 1 + examples/blk/meta.py | 10 +--------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/examples/blk/blk.mk b/examples/blk/blk.mk index e17d337d5..940507cad 100644 --- a/examples/blk/blk.mk +++ b/examples/blk/blk.mk @@ -63,6 +63,7 @@ else ifeq ($(strip $(MICROKIT_BOARD)), maaxboard) else ifeq ($(strip $(MICROKIT_BOARD)), odroidc4) ARCH := aarch64 CPU := cortex-a55 + SERIAL_DRIVER_DIR := meson BLK_DRIVER_DIR := sdmmc else $(error Unsupported MICROKIT_BOARD given) diff --git a/examples/blk/meta.py b/examples/blk/meta.py index 6f637c877..4e508f5c0 100644 --- a/examples/blk/meta.py +++ b/examples/blk/meta.py @@ -46,15 +46,6 @@ class Board: timer="soc@0/bus@30000000/timer@302d0000", serial="soc@0/bus@30800000/serial@30860000", ), - - Board( - name="odroidc4", - arch=SystemDescription.Arch.AARCH64, - paddr_top=0x80000000, - partition=0, - blk="soc/sd@ffe05000", - timer=None, - ), Board( name="odroidc4", arch=SystemDescription.Arch.AARCH64, @@ -62,6 +53,7 @@ class Board: partition=0, blk="soc/sd@ffe05000", timer=None, + serial="soc/bus@ff800000/serial@3000", ), Board( name="qemu_virt_riscv64", From 0fde0d1d232796c6729420e8cee850d85357f815 Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 6 Aug 2025 13:13:06 +1000 Subject: [PATCH 32/48] Format the files and deleted unused example Signed-off-by: Cheng --- drivers/blk/sdmmc/src/sddf_helper.c | 25 ++- .../aarch64-sel4-microkit-minimal.json | 66 ++++---- examples/blk/board/odroidc4/blk.system | 72 -------- examples/blk/meta.py | 5 +- examples/blk_benchmark/Makefile | 29 ---- examples/blk_benchmark/blk.mk | 136 ---------------- examples/blk_benchmark/blk_config.h | 97 ----------- .../blk_benchmark/board/odroidc4/blk.system | 68 -------- examples/blk_benchmark/client.c | 154 ------------------ 9 files changed, 53 insertions(+), 599 deletions(-) delete mode 100644 examples/blk/board/odroidc4/blk.system delete mode 100644 examples/blk_benchmark/Makefile delete mode 100644 examples/blk_benchmark/blk.mk delete mode 100644 examples/blk_benchmark/blk_config.h delete mode 100644 examples/blk_benchmark/board/odroidc4/blk.system delete mode 100644 examples/blk_benchmark/client.c diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/sdmmc/src/sddf_helper.c index 1148896a9..89c85775a 100644 --- a/drivers/blk/sdmmc/src/sddf_helper.c +++ b/drivers/blk/sdmmc/src/sddf_helper.c @@ -8,7 +8,8 @@ __attribute__((__section__(".blk_driver_config"))) blk_driver_config_t config; blk_queue_handle_t blk_queue; -void blk_queue_init_helper(uint64_t capacity) { +void blk_queue_init_helper(uint64_t capacity) +{ blk_queue_init(&blk_queue, config.virt.req_queue.vaddr, config.virt.resp_queue.vaddr, config.virt.num_buffers); blk_storage_info_t *storage_info = config.virt.storage_info.vaddr; @@ -18,27 +19,33 @@ void blk_queue_init_helper(uint64_t capacity) { storage_info->ready = true; } -uint64_t blk_device_regs_vaddr() { +uint64_t blk_device_regs_vaddr() +{ return (uint64_t)device_resources.regions[0].region.vaddr; } -uint64_t blk_device_init_data_vaddr() { +uint64_t blk_device_init_data_vaddr() +{ return (uint64_t)device_resources.regions[1].region.vaddr; } -uint64_t blk_device_init_data_ioaddr() { +uint64_t blk_device_init_data_ioaddr() +{ return (uint64_t)device_resources.regions[1].io_addr; } -uint8_t blk_queue_empty_req_helper() { +uint8_t blk_queue_empty_req_helper() +{ return blk_queue_empty_req(&blk_queue); } -uint8_t blk_queue_full_resp_helper() { +uint8_t blk_queue_full_resp_helper() +{ return blk_queue_full_resp(&blk_queue); } -uint8_t blk_enqueue_resp_helper(uint8_t status, uint16_t success, uint32_t id) { +uint8_t blk_enqueue_resp_helper(uint8_t status, uint16_t success, uint32_t id) +{ // It would be better if we do not use int but use int8_t if (blk_enqueue_resp(&blk_queue, status, success, id) == 0) { return 0; @@ -46,7 +53,9 @@ uint8_t blk_enqueue_resp_helper(uint8_t status, uint16_t success, uint32_t id) { return 1; } -uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint64_t *block_number, uint16_t *count, uint32_t *id) { +uint8_t blk_dequeue_req_helper(uint8_t *code, uintptr_t *io_or_offset, uint64_t *block_number, uint16_t *count, + uint32_t *id) +{ // It would be better if we do not use int but use int8_t // uint16_t temp_count = 0; if (blk_dequeue_req(&blk_queue, (blk_req_code_t *)code, io_or_offset, block_number, count, id) == 0) { diff --git a/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json b/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json index 93c5b7fac..d8cea28f7 100644 --- a/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json +++ b/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json @@ -1,35 +1,35 @@ { - "arch": "aarch64", - "crt-objects-fallback": "false", - "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32", - "disable-redzone": true, - "exe-suffix": ".elf", - "features": "+v8a,+strict-align,+neon,+fp-armv8", - "link-script": "__sel4_ipc_buffer_obj = (__ehdr_start & ~(4096 - 1)) - 4096;", - "linker": "rust-lld", - "linker-flavor": "gnu-lld", - "llvm-target": "aarch64-unknown-none", - "max-atomic-width": 128, - "metadata": { - "description": null, - "host_tools": null, - "std": null, - "tier": null - }, - "panic-strategy": "abort", - "pre-link-args": { - "gnu-lld": [ - "-z", - "max-page-size=4096" - ] - }, - "relocation-model": "static", - "stack-probes": { - "kind": "inline" - }, - "supported-sanitizers": [ - "kcfi", - "kernel-address" - ], - "target-pointer-width": "64" + "arch": "aarch64", + "crt-objects-fallback": "false", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32", + "disable-redzone": true, + "exe-suffix": ".elf", + "features": "+v8a,+strict-align,+neon,+fp-armv8", + "link-script": "__sel4_ipc_buffer_obj = (__ehdr_start & ~(4096 - 1)) - 4096;", + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "llvm-target": "aarch64-unknown-none", + "max-atomic-width": 128, + "metadata": { + "description": null, + "host_tools": null, + "std": null, + "tier": null + }, + "panic-strategy": "abort", + "pre-link-args": { + "gnu-lld": [ + "-z", + "max-page-size=4096" + ] + }, + "relocation-model": "static", + "stack-probes": { + "kind": "inline" + }, + "supported-sanitizers": [ + "kcfi", + "kernel-address" + ], + "target-pointer-width": "64" } \ No newline at end of file diff --git a/examples/blk/board/odroidc4/blk.system b/examples/blk/board/odroidc4/blk.system deleted file mode 100644 index 35a6b982d..000000000 --- a/examples/blk/board/odroidc4/blk.system +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/blk/meta.py b/examples/blk/meta.py index 4e508f5c0..4ca4a6537 100644 --- a/examples/blk/meta.py +++ b/examples/blk/meta.py @@ -12,6 +12,7 @@ MemoryRegion = SystemDescription.MemoryRegion Map = SystemDescription.Map + @dataclass class Board: name: str @@ -106,8 +107,8 @@ def generate(sdf_file: str, output_dir: str, dtb: DeviceTree): blk_system.add_client(client, partition=partition) if board.name == "odroidc4": - gpio_mr = MemoryRegion("gpio", 0x1000, paddr=0xff800000) - blk_driver.add_map(Map(gpio_mr, 0xff800000, "rw", cached=False)) + gpio_mr = MemoryRegion("gpio", 0x1000, paddr=0xFF800000) + blk_driver.add_map(Map(gpio_mr, 0xFF800000, "rw", cached=False)) sdf.add_mr(gpio_mr) serial_system.add_client(client) diff --git a/examples/blk_benchmark/Makefile b/examples/blk_benchmark/Makefile deleted file mode 100644 index faa33ee6c..000000000 --- a/examples/blk_benchmark/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2024, UNSW -# -# SPDX-License-Identifier: BSD-2-Clause -# - -ifeq ($(strip $(MICROKIT_SDK)),) -$(error MICROKIT_SDK must be specified) -endif - -BUILD_DIR ?= build -export SDDF=$(abspath ../..) -export BUILD_DIR:=$(abspath ${BUILD_DIR}) -export override MICROKIT_SDK:=$(abspath ${MICROKIT_SDK}) -export MICROKIT_BOARD ?= odroidc4 - -IMAGE_FILE:= ${BUILD_DIR}/loader.img -REPORT_FILE:= ${BUILD_DIR}/report.txt - -all: ${IMAGE_FILE} - -qemu ${IMAGE_FILE} ${REPORT_FILE} clean clobber: ${BUILD_DIR}/Makefile FORCE - ${MAKE} -C ${BUILD_DIR} MICROKIT_SDK=${MICROKIT_SDK} $(notdir $@) - -${BUILD_DIR}/Makefile: blk.mk - mkdir -p ${BUILD_DIR} - cp blk.mk $@ - -FORCE: diff --git a/examples/blk_benchmark/blk.mk b/examples/blk_benchmark/blk.mk deleted file mode 100644 index cb4725b69..000000000 --- a/examples/blk_benchmark/blk.mk +++ /dev/null @@ -1,136 +0,0 @@ -# -# Copyright 2024, UNSW -# -# SPDX-License-Identifier: BSD-2-Clause -# -# This Makefile is copied into the build directory -# and operated on from there. -# - -ifeq ($(strip $(MICROKIT_SDK)),) -$(error MICROKIT_SDK must be specified) -endif - -ifeq ($(strip $(SDDF)),) -$(error SDDF must be specified) -endif - -ifeq ($(strip $(TOOLCHAIN)),) - TOOLCHAIN := aarch64-none-elf -endif - -ifeq ($(strip $(TOOLCHAIN)), clang) - CC := clang -target aarch64-none-elf - LD := ld.lld - AR := llvm-ar - RANLIB := llvm-ranlib -else - CC := $(TOOLCHAIN)-gcc - LD := $(TOOLCHAIN)-ld - AS := $(TOOLCHAIN)-as - AR := $(TOOLCHAIN)-ar - RANLIB := $(TOOLCHAIN)-ranlib -endif - -QEMU := qemu-system-aarch64 - -BUILD_DIR ?= build -MICROKIT_CONFIG ?= debug - -TIMER_DRIVER_DIR := arm - -TOP := ${SDDF}/examples/blk_benchmark -CONFIGS_INCLUDE := ${TOP} - -MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit - -BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) - -IMAGES := client.elf blk_virt.elf timer_driver.elf - -ifeq ($(strip $(MICROKIT_BOARD)), odroidc4) - IMAGES += sdmmc_driver.elf - BLK_DRIVER_DIR := sdmmc - BLK_DRIVER_MK := sdmmc_driver.mk - TIMER_DRIVER_DIR := meson - CPU := cortex-a53 -else ifeq ($(strip $(MICROKIT_BOARD)), qemu_virt_aarch64) - IMAGES += blk_driver.elf - BLK_DRIVER_DIR := virtio - BLK_DRIVER_MK := blk_driver.mk - CPU := cortex-a53 - QEMU := qemu-system-aarch64 -else - $(error Unsupported MICROKIT_BOARD given) -endif - -# CFLAGS := -mcpu=$(CPU) \ - -mstrict-align \ - -nostdlib \ - -ffreestanding \ - -g3 \ - -O3 \ - -Wall -Wno-unused-function -Werror -Wno-unused-command-line-argument \ - -I$(BOARD_DIR)/include \ - -I$(SDDF)/include \ - -I$(CONFIGS_INCLUDE) - -CFLAGS := -mcpu=$(CPU) \ - -mstrict-align \ - -nostdlib \ - -ffreestanding \ - -g3 \ - -O3 \ - -I$(BOARD_DIR)/include \ - -I$(SDDF)/include \ - -I$(CONFIGS_INCLUDE) - -LDFLAGS := -L$(BOARD_DIR)/lib -LIBS := --start-group -lmicrokit -Tmicrokit.ld libsddf_util_debug.a --end-group - -IMAGE_FILE := loader.img -REPORT_FILE := report.txt -SYSTEM_FILE := ${TOP}/board/$(MICROKIT_BOARD)/blk.system - -BLK_DRIVER := $(SDDF)/drivers/blk/${BLK_DRIVER_DIR} -TIMER_DRIVER := $(SDDF)/drivers/timer/${TIMER_DRIVER_DIR} - -BLK_COMPONENTS := $(SDDF)/blk/components - -all: $(IMAGE_FILE) - -include ${BLK_DRIVER}/$(BLK_DRIVER_MK) -include ${TIMER_DRIVER}/timer_driver.mk - -include ${SDDF}/util/util.mk -include ${BLK_COMPONENTS}/blk_components.mk - -${IMAGES}: libsddf_util_debug.a - -client.o: ${TOP}/client.c - $(CC) -c $(CFLAGS) -I. $< -o client.o -client.elf: client.o - $(LD) $(LDFLAGS) $< $(LIBS) -o $@ - -$(IMAGE_FILE) $(REPORT_FILE): $(IMAGES) $(SYSTEM_FILE) - $(MICROKIT_TOOL) $(SYSTEM_FILE) --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) - -qemu_disk: - ../mkvirtdisk mydisk 1 512 16777216 - -qemu: ${IMAGE_FILE} qemu_disk - $(QEMU) -machine virt,virtualization=on \ - -cpu cortex-a53 \ - -serial mon:stdio \ - -device loader,file=$(IMAGE_FILE),addr=0x70000000,cpu-num=0 \ - -m size=2G \ - -nographic \ - -global virtio-mmio.force-legacy=false \ - -d guest_errors \ - -drive file=mydisk,if=none,format=raw,id=hd \ - -device virtio-blk-device,drive=hd - -clean:: - rm -f client.o -clobber:: clean - rm -f client.elf ${IMAGE_FILE} ${REPORT_FILE} diff --git a/examples/blk_benchmark/blk_config.h b/examples/blk_benchmark/blk_config.h deleted file mode 100644 index 613c416ec..000000000 --- a/examples/blk_benchmark/blk_config.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024, UNSW - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -#define BLK_NUM_CLIENTS 1 - -#define BLK_NAME_CLI0 "client" - -#define BLK_QUEUE_CAPACITY_CLI0 512 -#define BLK_QUEUE_CAPACITY_DRIV BLK_QUEUE_CAPACITY_CLI0 - -#define BLK_QUEUE_REGION_SIZE 0x200000 -#define BLK_DATA_REGION_SIZE_CLI0 BLK_QUEUE_REGION_SIZE -#define BLK_DATA_REGION_SIZE_DRIV BLK_QUEUE_REGION_SIZE - -#define BLK_QUEUE_REGION_SIZE_CLI0 BLK_QUEUE_REGION_SIZE -#define BLK_QUEUE_REGION_SIZE_DRIV BLK_QUEUE_REGION_SIZE - -/* Mapping from client index to disk partition that the client will have access to. */ -// Please ensure this point to a partition that you do not mind to courrupt as the example involves write data to that component -static const int blk_partition_mapping[BLK_NUM_CLIENTS] = { 2 }; - -static inline blk_storage_info_t *blk_virt_cli_storage_info(blk_storage_info_t *info, unsigned int id) -{ - switch (id) { - case 0: - return info; - default: - return NULL; - } -} - -static inline uintptr_t blk_virt_cli_data_region(uintptr_t data, unsigned int id) -{ - switch (id) { - case 0: - return data; - default: - return 0; - } -} - -static inline uint64_t blk_virt_cli_data_region_size(unsigned int id) -{ - switch (id) { - case 0: - return BLK_DATA_REGION_SIZE_CLI0; - default: - return 0; - } -} - -static inline blk_req_queue_t *blk_virt_cli_req_queue(blk_req_queue_t *req, unsigned int id) -{ - switch (id) { - case 0: - return req; - default: - return NULL; - } -} - -static inline blk_resp_queue_t *blk_virt_cli_resp_queue(blk_resp_queue_t *resp, unsigned int id) -{ - switch (id) { - case 0: - return resp; - default: - return NULL; - } -} - -static inline uint32_t blk_virt_cli_queue_capacity(unsigned int id) -{ - switch (id) { - case 0: - return BLK_QUEUE_CAPACITY_CLI0; - default: - return 0; - } -} - -static inline uint32_t blk_cli_queue_capacity(char *pd_name) -{ - if (!sddf_strcmp(pd_name, BLK_NAME_CLI0)) { - return BLK_QUEUE_CAPACITY_CLI0; - } else { - return 0; - } -} diff --git a/examples/blk_benchmark/board/odroidc4/blk.system b/examples/blk_benchmark/board/odroidc4/blk.system deleted file mode 100644 index 45284d4a4..000000000 --- a/examples/blk_benchmark/board/odroidc4/blk.system +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/blk_benchmark/client.c b/examples/blk_benchmark/client.c deleted file mode 100644 index 3f69f8043..000000000 --- a/examples/blk_benchmark/client.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2024, UNSW - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_CLIENT(...) do{ sddf_dprintf("CLIENT|INFO: "); sddf_dprintf(__VA_ARGS__); }while(0) -#define LOG_CLIENT_ERR(...) do{ sddf_printf("CLIENT|ERROR: "); sddf_printf(__VA_ARGS__); }while(0) - -#define QUEUE_SIZE 512 -#define VIRT_CH 0 -#define TIMER 1 - -#define BENCHMARK_TOTAL_SIZE ONE_HUNDRED_MB -#define BLK_TRANSFER_SIZE 4096 -#define BENCHMARK_CHUNKSIZE ONE_HUNDRED_MB -#define ONE_HUNDRED_MB 0x6400000 -#define ONE_MB 0x100000 -#define BLOCK_READ_INTERVAL ONE_MB -#define BYTES_TO_MB 1048576.0 // 1 MB = 1048576 bytes - -static_assert(BENCHMARK_TOTAL_SIZE % BENCHMARK_CHUNKSIZE == 0, "The chunksize must be aligned to the total size"); -static_assert(BENCHMARK_CHUNKSIZE % BLK_TRANSFER_SIZE == 0, "The chunksize must be a multiple of the transfer size"); - -blk_storage_info_t *blk_storage_info; -uintptr_t blk_request; -uintptr_t blk_response; -uintptr_t blk_data; - -uint64_t blk_data_paddr; - -uint64_t request_id = 1; - -static blk_queue_handle_t blk_queue; - -enum benchmark_state { - START, - WRITE, - READ, - FINISH, -}; - -enum benchmark_state benchmark_state = START; - -uint64_t start_time = 0; -uint64_t end_time = 0; - -bool benchmark() -{ - switch (benchmark_state) { - case START: { - LOG_CLIENT("Benchmark: START state\n"); - - int err = blk_enqueue_req(&blk_queue, BLK_REQ_READ, blk_data_paddr, 0, 2, request_id); - - // assert(!err); - - request_id++; - benchmark_state = READ; - - break; - } - case READ: { - LOG_CLIENT("Benchmark: READ state\n"); - /* Check that our previous write was successful */ - blk_resp_status_t status = -1; - uint16_t count = -1; - uint32_t id = -1; - int err = blk_dequeue_resp(&blk_queue, &status, &count, &id); - // assert(!err); - // assert(status == BLK_RESP_OK); - // assert(count == 2); - // assert(id == 0); - - uint64_t read_start_sector = 1024; - uint64_t times = 0; - // A static check should be added here to check if the read/write requests may go out of disk capacity boundary - while (BENCHMARK_TOTAL_SIZE - times * BENCHMARK_CHUNKSIZE) { - err = blk_enqueue_req(&blk_queue, BLK_REQ_READ, blk_data_paddr + times * BENCHMARK_CHUNKSIZE, - read_start_sector + times * (BENCHMARK_CHUNKSIZE / BLK_TRANSFER_SIZE + BLOCK_READ_INTERVAL / BLK_TRANSFER_SIZE), - (BENCHMARK_CHUNKSIZE / BLK_TRANSFER_SIZE), request_id); - request_id++; - times++; - // assert(!err); - } - - benchmark_state = FINISH; - - start_time = sddf_timer_time_now(TIMER); - - break; - } - case FINISH: { - end_time = sddf_timer_time_now(TIMER); - - // assert(end_time > start_time); - - uint64_t time_passed = end_time - start_time; - - LOG_CLIENT("Benchmark: FINISH state\n"); - - double speed_bytes_per_sec = (double)BENCHMARK_TOTAL_SIZE * 1000000000.0 / time_passed; - - // Convert to MB/s - double speed_mb_per_sec = speed_bytes_per_sec / BYTES_TO_MB; - - sddf_dprintf("Benchmarking speed: %.5fByte/Second\n", speed_bytes_per_sec); - sddf_dprintf("Benchmark Speed: %.5f MB/s\n", speed_mb_per_sec); - sddf_dprintf("Time has passed: %ld nanosecond\n", time_passed); - - LOG_CLIENT("Benchmark: successfully finished!\n"); - - return true; - } - default: - LOG_CLIENT_ERR("internal error, invalid state\n"); - // assert(false); - } - - return false; -} - -void init(void) -{ - LOG_CLIENT("starting\n"); - - blk_queue_init(&blk_queue, (blk_req_queue_t *)blk_request, (blk_resp_queue_t *)blk_response, QUEUE_SIZE); - - /* Want to print out configuration information, so wait until the config is ready. */ - while (!blk_storage_is_ready(blk_storage_info)); - LOG_CLIENT("device config ready\n"); - - LOG_CLIENT("device size: 0x%lx bytes\n", blk_storage_info->capacity * BLK_TRANSFER_SIZE); - - benchmark(); - microkit_notify(VIRT_CH); -} - -void notified(microkit_channel ch) -{ - // assert(ch == VIRT_CH); - - if (!benchmark()) { - microkit_notify(VIRT_CH); - } -} From 304132f710aba90e58da60a12670820866a617de Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 6 Aug 2025 13:34:57 +1000 Subject: [PATCH 33/48] Merge the latest changes from my repo Signed-off-by: Cheng --- drivers/blk/sdmmc/Cargo.toml | 6 +- drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml | 14 +- .../blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 210 ++-------- drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml | 10 +- drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 360 +++++++++++------- .../sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs | 23 +- .../blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs | 14 +- .../blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs | 293 +++++++------- .../sdmmc_protocol/sdmmc/sdmmc_capability.rs | 3 - drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs | 115 +++++- .../blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs | 77 ++-- 11 files changed, 587 insertions(+), 538 deletions(-) diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 438cad493..789a0bfe4 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "blk_driver" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e", features = ["alloc"] } sel4-panicking-env = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" } -meson_hal = { git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "3fa77fa08c8ef9c7927a75bbd0b06083c6d21fe5", optional = true } -sdmmc_protocol = {git = "https://github.com/Cheng-Li1/sdcard-emmc-rs", rev = "3fa77fa08c8ef9c7927a75bbd0b06083c6d21fe5" } +meson_hal = { path = "sdmmc_hal/meson", optional = true } +sdmmc_protocol = { path = "sdmmc_protocol" } [features] meson = ["dep:meson_hal"] diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml b/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml index eb97b1ac2..195e583f1 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "sdmmc_hal" +name = "meson_hal" version = "0.1.0" -edition = "2021" +edition = "2024" authors = ["Cheng Li 李澄 "] [lib] -name = "sdmmc_hal" +name = "meson_hal" path = "lib.rs" # The Hardware Abstraction Layer (HAL) is designed to be: @@ -13,16 +13,8 @@ path = "lib.rs" # - Operating system (OS) agnostic # - Minimally dependent to ensure cross-platform flexibility # -# Optional OS-specific crates can be included to enhance functionality, -# such as providing improved debugging output and more efficient sleep functions -# (as opposed to simple spin-wait). -# # Required Dependencies: # - sdmmc_protocol: Essential for core functionality. -# -# Optional Dependencies: -# - sel4-microkit: Provides additional support for debugging and optimized sleep functions -# specifically on the seL4 microkernel platform. [dependencies] sdmmc_protocol = { path = "../../sdmmc_protocol" } diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs index e6996ab6a..8c7898d14 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs @@ -1,31 +1,23 @@ use core::ptr; use sdmmc_protocol::{ + dev_log, info, sdmmc::{ - mmc_struct::{MmcBusWidth, MmcTiming, TuningState}, + HostInfo, MmcData, MmcDataFlag, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, + SdmmcError, + mmc_struct::{MmcBusWidth, MmcTiming}, sdcard::Sdcard, sdmmc_capability::{ - MMC_CAP_4_BIT_DATA, MMC_CAP_CMD23, MMC_CAP_VOLTAGE_TUNE, MMC_TIMING_LEGACY, - MMC_TIMING_SD_HS, MMC_TIMING_UHS, MMC_VDD_31_32, MMC_VDD_32_33, MMC_VDD_33_34, + MMC_CAP_4_BIT_DATA, MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS, MMC_VDD_31_32, + MMC_VDD_32_33, MMC_VDD_33_34, }, - HostInfo, MmcData, MmcDataFlag, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, - SdmmcError, }, - sdmmc_os::{debug_log, process_wait_unreliable}, + sdmmc_os::{Sleep, process_wait_unreliable}, sdmmc_traits::SdmmcHardware, }; pub const SDIO_BASE: u64 = 0xffe05000; // Base address from DTS -// The always on gpio pin pull -const AO_RTI_PIN_REGION_START: u64 = 0xff800014; -const AO_RTI_PIN_REGION_END: u64 = 0xff800038; -const AO_RTI_PULL_UP_REG: u64 = 0xff80002c; -const AO_RTI_OUTPUT_ENABLE_REG: u64 = 0xff800024; -const AO_RTI_OUTPUT_LEVEL_REG: u64 = 0xff800034; -const AO_RTI_PULL_UP_EN_REG: u64 = 0xff800030; -const GPIO_AO_3: u32 = 1 << 3; - macro_rules! div_round_up { ($n:expr, $d:expr) => { (($n + $d - 1) / $d) @@ -85,12 +77,6 @@ const CMD_CFG_TIMEOUT_4S: u32 = 12 << 12; const CMD_CFG_OWNER: u32 = 1 << 31; const CMD_CFG_END_OF_CHAIN: u32 = 1 << 11; -// MMC_RSP constants -const MMC_RSP_PRESENT: u32 = 1 << 0; -const MMC_RSP_136: u32 = 1 << 1; -const MMC_RSP_CRC: u32 = 1 << 2; -const MMC_RSP_BUSY: u32 = 1 << 3; - // STATUS register masks and flags const STATUS_MASK: u32 = 0xFFFF; // GENMASK(15, 0) const STATUS_ERR_MASK: u32 = 0x1FFF; // GENMASK(12, 0) @@ -112,11 +98,10 @@ const IRQ_RXD_ERR_MASK: u32 = 0xFF; // Equivalent to GENMASK(7, 0) const IRQ_TXD_ERR: u32 = 1 << 8; const IRQ_DESC_ERR: u32 = 1 << 9; const IRQ_RESP_ERR: u32 = 1 << 10; -// Equivalent to (IRQ_RXD_ERR_MASK | IRQ_TXD_ERR | IRQ_DESC_ERR | IRQ_RESP_ERR) const IRQ_CRC_ERR: u32 = IRQ_RXD_ERR_MASK | IRQ_TXD_ERR | IRQ_DESC_ERR | IRQ_RESP_ERR; const IRQ_RESP_TIMEOUT: u32 = 1 << 11; const IRQ_DESC_TIMEOUT: u32 = 1 << 12; -// Equivalent to (IRQ_RESP_TIMEOUT | IRQ_DESC_TIMEOUT) + const IRQ_TIMEOUTS: u32 = IRQ_RESP_TIMEOUT | IRQ_DESC_TIMEOUT; const IRQ_END_OF_CHAIN: u32 = 1 << 13; const IRQ_RESP_STATUS: u32 = 1 << 14; @@ -125,8 +110,6 @@ const IRQ_ERR_MASK: u32 = IRQ_CRC_ERR | IRQ_TIMEOUTS; const IRQ_EN_MASK: u32 = IRQ_ERR_MASK | IRQ_END_OF_CHAIN; -const MESON_SDCARD_SECTOR_SIZE: u32 = 512; - pub const MAX_BLOCK_PER_TRANSFER: u32 = 0x1FF; const WRITE_ADDR_UPPER: u32 = 0xFFFE0000; @@ -135,11 +118,6 @@ const WRITE_ADDR_UPPER: u32 = 0xFFFE0000; // const VALID_DMA_ADDR_UPPER: u32 = 0x10000000; // const VALID_DMA_ADDR_MASK: u32 = 0x80000003; -fn ilog2(x: u32) -> u32 { - assert!(x > 0); - 31 - x.leading_zeros() -} - // Structure representing the SDIO controller's registers /* * Those register mapping are taken from meson-gx-mmc.c in Linux source code, @@ -192,7 +170,7 @@ impl MesonSdmmcRegisters { /// This function is only safe to use if the sdmmc_register_base is the correct memory addr /// of the sdmmc register base and accessible for the driver unsafe fn new(sdmmc_register_base: u64) -> &'static mut MesonSdmmcRegisters { - &mut *(sdmmc_register_base as *mut MesonSdmmcRegisters) + unsafe { &mut *(sdmmc_register_base as *mut MesonSdmmcRegisters) } } } @@ -215,7 +193,8 @@ pub struct SdmmcMesonHardware { impl SdmmcMesonHardware { pub unsafe fn new(sdmmc_register_base: u64) -> Self { - let register = MesonSdmmcRegisters::new(sdmmc_register_base); + let register: &'static mut MesonSdmmcRegisters = + unsafe { MesonSdmmcRegisters::new(sdmmc_register_base) }; // TODO: Call reset function here SdmmcMesonHardware { @@ -290,19 +269,19 @@ impl SdmmcMesonHardware { meson_mmc_cmd |= (cmd.cmdidx & 0x3F) << CMD_CFG_CMD_INDEX_SHIFT; - if cmd.resp_type & MMC_RSP_PRESENT != 0 { - if cmd.resp_type & MMC_RSP_136 != 0 { + if cmd.resp_type & sdmmc_protocol::sdmmc::MMC_RSP_PRESENT != 0 { + if cmd.resp_type & sdmmc_protocol::sdmmc::MMC_RSP_136 != 0 { meson_mmc_cmd |= CMD_CFG_RESP_128; } // If the hardware does not have busy detection, polling for card datalines to be high is needed // as the card will signal "busy" (by pulling the DAT line low) // Odroid C4 have feature to wait until hardware exit busy state so we do not need to worry about it - if cmd.resp_type & MMC_RSP_BUSY != 0 { + if cmd.resp_type & sdmmc_protocol::sdmmc::MMC_RSP_BUSY != 0 { meson_mmc_cmd |= CMD_CFG_R1B; } - if cmd.resp_type & MMC_RSP_CRC == 0 { + if cmd.resp_type & sdmmc_protocol::sdmmc::MMC_RSP_CRC == 0 { meson_mmc_cmd |= CMD_CFG_RESP_NOCRC; } } else { @@ -314,10 +293,10 @@ impl SdmmcMesonHardware { cfg &= !CFG_BL_LEN_MASK; - cfg |= ilog2(data.blocksize) << CFG_BL_LEN_SHIFT; + cfg |= data.blocksize.ilog2() << CFG_BL_LEN_SHIFT; // TODO: Maybe add blocksize is power of 2 check here? - // debug_log!("Configure register value: 0x{:08x}", cfg); + // dev_log!("Configure register value: 0x{:08x}", cfg); unsafe { ptr::write_volatile(&mut self.register.cfg, cfg); @@ -341,7 +320,7 @@ impl SdmmcMesonHardware { let [rsp0, rsp1, rsp2, rsp3] = response; // Assign values by reading the respective registers - if cmd.resp_type & MMC_RSP_136 != 0 { + if cmd.resp_type & sdmmc_protocol::sdmmc::MMC_RSP_136 != 0 { unsafe { // Yes, this is in a reverse order as rsp0 and self.cmd_rsp3 is the least significant // Check uboot read response code for more details @@ -350,7 +329,7 @@ impl SdmmcMesonHardware { *rsp2 = ptr::read_volatile(&self.register.cmd_rsp1); *rsp3 = ptr::read_volatile(&self.register.cmd_rsp); } - } else if cmd.resp_type & MMC_RSP_PRESENT != 0 { + } else if cmd.resp_type & sdmmc_protocol::sdmmc::MMC_RSP_PRESENT != 0 { unsafe { *rsp0 = ptr::read_volatile(&self.register.cmd_rsp); } @@ -432,12 +411,7 @@ impl SdmmcMesonHardware { impl SdmmcHardware for SdmmcMesonHardware { fn sdmmc_init(&mut self) -> Result<(MmcIos, HostInfo, u128), SdmmcError> { - let cap: u128 = MMC_TIMING_LEGACY - | MMC_TIMING_SD_HS - | MMC_TIMING_UHS - | MMC_CAP_VOLTAGE_TUNE - | MMC_CAP_4_BIT_DATA - | MMC_CAP_CMD23; + let cap: u128 = MMC_TIMING_LEGACY | MMC_TIMING_SD_HS | MMC_TIMING_UHS | MMC_CAP_4_BIT_DATA; // Reset host state self.meson_reset(); @@ -465,7 +439,11 @@ impl SdmmcHardware for SdmmcMesonHardware { return Ok((ios, info, cap)); } - fn sdmmc_execute_tuning(&mut self, memory: *mut [u8; 64]) -> Result<(), SdmmcError> { + fn sdmmc_execute_tuning( + &mut self, + memory: *mut [u8; 64], + sleep: &mut dyn Sleep, + ) -> Result<(), SdmmcError> { let mut current_delay: u32 = 0; if let Some(ref config) = self.delay { @@ -502,13 +480,13 @@ impl SdmmcHardware for SdmmcMesonHardware { let mut tried_highest_delay: u32 = current_delay; loop { - debug_log!( + dev_log!( "current delay: {}, lowest_tried: {}, highest_tried: {}\n", current_delay, tried_lowest_delay, tried_highest_delay ); - let res: Result<(), SdmmcError> = Sdcard::sdcard_test_tuning(self, memory); + let res: Result<(), SdmmcError> = Sdcard::sdcard_test_tuning(self, sleep, memory); match res { Ok(_) => { @@ -594,7 +572,7 @@ impl SdmmcHardware for SdmmcMesonHardware { clk_src = CLK_SRC_24M; } - let clk_div = div_round_up!(clk, clock_freq); + let clk_div: u32 = div_round_up!(clk, clock_freq); /* * From uboot meson_gx_mmc.c * SM1 SoCs doesn't work fine over 50MHz with CLK_CO_PHASE_180 @@ -676,7 +654,7 @@ impl SdmmcHardware for SdmmcMesonHardware { || mmc_data.blockcnt == 0 || mmc_data.blockcnt > MAX_BLOCK_PER_TRANSFER { - debug_log!("SDMMC: INVALID INPUT VARIABLE!"); + info!("SDMMC: INVALID INPUT VARIABLE!"); return Err(SdmmcError::EINVAL); } // Depend on the flag and hardware, the cache should be flushed accordingly @@ -730,16 +708,16 @@ impl SdmmcHardware for SdmmcMesonHardware { if (status & STATUS_ERR_MASK) != 0 { // For debug - debug_log!("SDMMC: Print out error request:\n"); - debug_log!("cmd idx: {}\n", cmd.cmdidx); - debug_log!("cmd arg: 0x{:x}\n", cmd.cmdarg); + info!("SDMMC: Print out error request:"); + info!("cmd idx: {}", cmd.cmdidx); + info!("cmd arg: 0x{:x}", cmd.cmdarg); - debug_log!("Odroidc4 status register: 0x{:08}\n", status); + info!("Odroidc4 status register: 0x{:08x}", status); - debug_log!("Card's first response register: 0x{:08}\n", response[0]); + info!("Card's first response register: 0x{:08x}", response[0]); if (status & STATUS_RESP_TIMEOUT) != 0 { - debug_log!("SDMMC: CARD TIMEOUT!\n"); + info!("SDMMC: CARD TIMEOUT!"); // The card will try to polling the status register until // both descriptor and card are not in busy state @@ -748,15 +726,15 @@ impl SdmmcHardware for SdmmcMesonHardware { } if (status & STATUS_EIO_ERR) != 0 { - debug_log!("SDMMC: CARD IO ERROR! Perform retuning\n"); + info!("SDMMC: CARD IO ERROR! Perform retuning"); self.meson_wait_desc_stop()?; // Notified the card to retune the card return Err(SdmmcError::EIO); } - debug_log!( - "SDMMC: Unknown error, copy the prints from card init to end of the print and send it to me\n" + info!( + "SDMMC: Unknown error, Please copy the entire log from driver and send it to Cheng Li lichengchaoreng@gmail.com" ); self.meson_wait_desc_stop()?; @@ -804,118 +782,6 @@ impl SdmmcHardware for SdmmcMesonHardware { return Ok(()); } - fn sdmmc_set_signal_voltage(&mut self, voltage: MmcSignalVoltage) -> Result<(), SdmmcError> { - match voltage { - MmcSignalVoltage::Voltage330 => { - let mut value: u32; - unsafe { - value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); - } - value &= !(1 << 6); - unsafe { - ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); - } - unsafe { - value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); - } - value &= !(1 << 6); - unsafe { - ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); - } - } - MmcSignalVoltage::Voltage180 => { - let mut value: u32; - unsafe { - value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); - } - value &= !(1 << 6); - unsafe { - ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); - } - unsafe { - value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); - } - value |= 1 << 6; - unsafe { - ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); - } - } - MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EINVAL), - } - // Disable pull-up/down for gpioAO_6 - let mut value: u32; - unsafe { - value = ptr::read_volatile(AO_RTI_PULL_UP_EN_REG as *const u32); - } - value &= !(1 << 6); // Disable pull-up/down for gpioAO_6 - unsafe { - ptr::write_volatile(AO_RTI_PULL_UP_EN_REG as *mut u32, value); - } - - Ok(()) - } - - // Experimental function that tries to modify the pin that might control the power of the sdcard slot - // This function should be pretty much the same with sdmmc_set_signal_voltage, the only difference - // is the pin modified is gpioAO_3, busy wait is needed to be added after setting the power - fn sdmmc_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { - /* - unsafe { - debug_log!("In set power function\n"); - let mut value: u32; - - // Read the value using ptr::read_volatile - value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); - debug_log!("Address {:#x}: {:#x}\n", AO_RTI_OUTPUT_ENABLE_REG, value); - - value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); - debug_log!("Address {:#x}: {:#x}\n", AO_RTI_OUTPUT_LEVEL_REG, value); - - value = ptr::read_volatile(AO_RTI_PULL_UP_EN_REG as *const u32); - debug_log!("Address {:#x}: {:#x}\n", AO_RTI_PULL_UP_EN_REG, value); - } - */ - - let mut value: u32; - unsafe { - value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); - } - // If the GPIO pin is not being set as output, set it to output first - if value & GPIO_AO_3 != 0 { - value &= !GPIO_AO_3; - unsafe { - ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); - } - } - match power_mode { - MmcPowerMode::On => { - unsafe { - value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); - } - if value & GPIO_AO_3 == 0 { - value |= GPIO_AO_3; - unsafe { - ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); - } - } - self.sdmmc_set_signal_voltage(MmcSignalVoltage::Voltage330)?; - } - MmcPowerMode::Off => { - unsafe { - value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); - } - if value & GPIO_AO_3 != 0 { - value &= !GPIO_AO_3; - unsafe { - ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); - } - } - } - _ => return Err(SdmmcError::EINVAL), - } - Ok(()) - } - fn sdmmc_host_reset(&mut self) -> Result { Self::meson_reset(self); diff --git a/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml b/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml index 102c640b5..ac26ceed3 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml +++ b/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sdmmc_protocol" version = "0.1.0" -edition = "2021" +edition = "2024" authors = ["Cheng Li 李澄 "] [lib] @@ -10,10 +10,8 @@ path = "lib.rs" [dependencies] bitflags = "2.6.0" -sel4-microkit-support = { path = "../optional_os_support/sel4-microkit-support", optional = true } [features] -# Feature Flags: -# - sel4-microkit: Enables support for the optional `sel4-microkit` crate, -# adding seL4-specific functionalities such as debug output and optimized sleep functions. -sel4-microkit = ["dep:sel4-microkit-support"] \ No newline at end of file +default = [] +# A feature specifically for enabling developer-level logging. The driver will always try to log errors +dev-logs = [] \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs index 2428e1274..03dc467a7 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs @@ -1,3 +1,10 @@ +pub mod mmc_struct; +pub mod sd_ops; +pub mod sdcard; +pub mod sdmmc_capability; + +mod sdmmc_constant; + use core::{ future::Future, pin::Pin, @@ -5,12 +12,12 @@ use core::{ task::{Context, Poll, Waker}, }; -use mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcDevice, MmcState, MmcTiming, TuningState}; +use mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcDevice, MmcState, MmcTiming}; use sdcard::{Cid, Csd, Scr, Sdcard}; use sdmmc_capability::{ - SdcardCapability, SdmmcHostCapability, MMC_CAP_4_BIT_DATA, MMC_CAP_VOLTAGE_TUNE, MMC_EMPTY_CAP, - MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS_DDR50, MMC_TIMING_UHS_SDR104, - MMC_TIMING_UHS_SDR12, MMC_TIMING_UHS_SDR25, MMC_TIMING_UHS_SDR50, + MMC_CAP_4_BIT_DATA, MMC_EMPTY_CAP, MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS_DDR50, + MMC_TIMING_UHS_SDR12, MMC_TIMING_UHS_SDR25, MMC_TIMING_UHS_SDR50, MMC_TIMING_UHS_SDR104, + SdcardCapability, SdmmcHostCapability, }; use sdmmc_constant::{ MMC_CMD_ALL_SEND_CID, MMC_CMD_APP_CMD, MMC_CMD_ERASE, MMC_CMD_GO_IDLE_STATE, @@ -20,24 +27,21 @@ use sdmmc_constant::{ SD_CMD_APP_SET_BUS_WIDTH, SD_CMD_ERASE_WR_BLK_END, SD_CMD_ERASE_WR_BLK_START, SD_CMD_SEND_IF_COND, SD_CMD_SEND_RELATIVE_ADDR, SD_CMD_SWITCH_FUNC, SD_CMD_SWITCH_UHS18V, SD_ERASE_ARG, SD_SWITCH_FUNCTION_GROUP_ONE, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_SDHS, - SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104, - SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR12, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR25, - SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR50, SD_SWITCH_FUNCTION_GROUP_ONE_SET_LEGACY, + SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR12, + SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR25, SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR50, + SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104, SD_SWITCH_FUNCTION_GROUP_ONE_SET_LEGACY, SD_SWITCH_FUNCTION_GROUP_ONE_SET_SDHS, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_DDR50, - SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR104, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR12, - SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR25, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR50, + SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR12, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR25, + SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR50, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR104, SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE, }; -use crate::sdmmc_traits::SdmmcHardware; - -use crate::sdmmc_os::{debug_log, process_wait_unreliable}; - -pub mod mmc_struct; -pub mod sd_ops; -pub mod sdcard; -pub mod sdmmc_capability; -mod sdmmc_constant; +use crate::{ + dev_log, + sdmmc::mmc_struct::CardInfo, + sdmmc_os::{Sleep, VoltageOps}, + sdmmc_traits::SdmmcHardware, +}; pub struct SdmmcCmd { pub cmdidx: u32, @@ -79,11 +83,14 @@ pub enum SdmmcError { } // Define the MMC response flags -const MMC_RSP_PRESENT: u32 = 1 << 0; -const MMC_RSP_136: u32 = 1 << 1; // 136-bit response -const MMC_RSP_CRC: u32 = 1 << 2; // Expect valid CRC -const MMC_RSP_BUSY: u32 = 1 << 3; // Card may send busy -const MMC_RSP_OPCODE: u32 = 1 << 4; // Response contains opcode +pub const MMC_RSP_PRESENT: u32 = 1 << 0; +pub const MMC_RSP_136: u32 = 1 << 1; // 136-bit response +pub const MMC_RSP_CRC: u32 = 1 << 2; // Expect valid CRC +pub const MMC_RSP_BUSY: u32 = 1 << 3; // Card may send busy +pub const MMC_RSP_OPCODE: u32 = 1 << 4; // Response contains opcode + +// How many times should we try to init the card +const CARD_INIT_RETRY: u16 = 2; // Define the MMC response types pub const MMC_RSP_NONE: u32 = 0; @@ -257,8 +264,12 @@ pub struct HostInfo { } /// TODO: Add more variables for SdmmcProtocol to track the state of the sdmmc controller and card correctly -pub struct SdmmcProtocol { - pub hardware: T, +pub struct SdmmcProtocol { + hardware: T, + + sleep: S, + + voltage_ops: Option, mmc_ios: MmcIos, @@ -272,14 +283,22 @@ pub struct SdmmcProtocol { private_memory: Option<*mut [u8; 64]>, } -impl Unpin for SdmmcProtocol where T: Unpin + SdmmcHardware {} +impl Unpin for SdmmcProtocol +where + T: Unpin + SdmmcHardware, + S: Unpin + Sleep, + V: Unpin + VoltageOps, +{ +} -impl SdmmcProtocol { - pub fn new(mut hardware: T) -> Result { +impl SdmmcProtocol { + pub fn new(mut hardware: T, sleep: S, voltage_ops: Option) -> Result { let (ios, info, cap) = hardware.sdmmc_init()?; Ok(SdmmcProtocol { hardware, + sleep, + voltage_ops, mmc_ios: ios, host_info: info, host_capability: SdmmcHostCapability(cap), @@ -297,15 +316,15 @@ impl SdmmcProtocol { cmdarg: 0, }; - debug_log!("Try to send go idle cmd\n"); + dev_log!("Try to send go idle cmd\n"); // Go idle command does not expect a response self.hardware.sdmmc_send_command(&cmd, None)?; // Linux use 1ms delay here, we use 2ms - process_wait_unreliable(2_000_000); + self.sleep.usleep(2_000); - debug_log!("Try to send check operating voltage cmd\n"); + dev_log!("Try to send check operating voltage cmd\n"); cmd = SdmmcCmd { cmdidx: SD_CMD_SEND_IF_COND, @@ -313,7 +332,9 @@ impl SdmmcProtocol { cmdarg: 0x000001AA, // Voltage supply and check pattern }; - let res: Result<(), SdmmcError> = self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1); + let res: Result<(), SdmmcError> = + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 1); // If the result is OK and the resp is 0x1AA, the card we are initializing is a SDHC/SDXC // If the result is error, it is either the voltage not being set up correctly, which mean a bug in hardware layer @@ -327,7 +348,7 @@ impl SdmmcProtocol { let mut retry: u16 = 1000; loop { - debug_log!("Sending SD_CMD_APP_SEND_OP_COND!\n"); + dev_log!("Sending SD_CMD_APP_SEND_OP_COND!\n"); // Prepare CMD55 (APP_CMD) cmd = SdmmcCmd { cmdidx: MMC_CMD_APP_CMD, @@ -336,7 +357,9 @@ impl SdmmcProtocol { }; // Send CMD55 - let res = self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0); + let res = self + .hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 0); match res { Ok(_) => {} @@ -359,11 +382,10 @@ impl SdmmcProtocol { // Change this when we decide to support spi or SDSC as well cmd.cmdarg |= self.host_info.vdd & 0xff8000; - if self - .host_capability - .contains(sdmmc_capability::SdmmcHostCapability( - MMC_TIMING_UHS_SDR12 | MMC_CAP_VOLTAGE_TUNE, - )) + if voltage_switch == true + && self + .host_capability + .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) { cmd.cmdarg |= OCR_S18R; // It seems that cards will not respond to commands that have MMC_VDD_165_195 bit set, even if the card supports UHS-I @@ -371,9 +393,10 @@ impl SdmmcProtocol { } // Send ACMD41 - self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 0)?; - debug_log!("OCR: {:08x}\n", resp[0]); + dev_log!("OCR: {:08x}\n", resp[0]); // Check if card is ready (OCR_BUSY bit) if (resp[0] & OCR_BUSY) != 0 { @@ -382,17 +405,17 @@ impl SdmmcProtocol { // retry handling if retry <= 0 { - debug_log!("SDMMC: SEND_OP_COND failed, card not supported!\n"); + dev_log!("SDMMC: SEND_OP_COND failed, card not supported!\n"); return Err(SdmmcError::EUNSUPPORTEDCARD); } retry -= 1; } // Checking if the host and card is eligible for voltage switch - if self - .host_capability - .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) - && voltage_switch == true + if voltage_switch == true + && self + .host_capability + .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) && resp[0] & OCR_HCS == OCR_HCS && resp[0] & OCR_S18R == OCR_S18R { @@ -404,32 +427,30 @@ impl SdmmcProtocol { Ok(()) } - fn sdmmc_power_cycle(&mut self) -> Result<(), SdmmcError> { - self.hardware.sdmmc_set_power(MmcPowerMode::Off)?; - process_wait_unreliable(self.host_info.power_delay_ms as u64 * 1_000_000); + fn sdmmc_power_cycle( + sleep: &mut S, + voltage_ops: &mut V, + power_delay_ms: u32, + ) -> Result<(), SdmmcError> { + voltage_ops.card_set_power(MmcPowerMode::Off)?; + + sleep.usleep(power_delay_ms * 1_000); + + voltage_ops.card_set_power(MmcPowerMode::On)?; - self.hardware.sdmmc_set_power(MmcPowerMode::On)?; - process_wait_unreliable(1_000_000); + sleep.usleep(power_delay_ms * 1_000); - self.mmc_ios.power_mode = MmcPowerMode::On; Ok(()) } - // Funtion that is not completed + // Function that is not completed pub fn setup_card(&mut self) -> Result<(), SdmmcError> { // Disable all irqs here self.hardware.sdmmc_config_interrupt(false, false)?; // TODO: Different sdcard and eMMC support different voltages, figure those out if self.mmc_ios.power_mode != MmcPowerMode::On { - debug_log!("Turn the power up"); - self.hardware.sdmmc_set_power(MmcPowerMode::On)?; - // TODO: Right now we know the power will always be up and this function should not be called - // But when we encounter scenerio that may actually call this function, we should wait for the time specified in ios - // Right now this whole power up related thing does not work - process_wait_unreliable(self.host_info.power_delay_ms as u64 * 1_000_000); - - self.mmc_ios.power_mode = MmcPowerMode::On; + return Err(SdmmcError::EINVAL); } let clock = self.hardware.sdmmc_config_timing(MmcTiming::CardSetup)?; @@ -441,37 +462,44 @@ impl SdmmcProtocol { self.mmc_ios.bus_width = MmcBusWidth::Width1; - let mut voltage_switch: bool = true; - // Use labeled block here for better clarification + // For card initialization, we retry 2 times for each card // There could be more complex retry logic implemented in the future - 'sdcard_init: loop { - match self.sdcard_init(voltage_switch) { - Ok(()) => {} - Err(SdmmcError::EUNSUPPORTEDCARD) => { - break 'sdcard_init; - } - Err(_) => { - if voltage_switch == true { - voltage_switch = false; - - // Retry with voltage switch off - debug_log!("Try to init the card without voltage switch\n"); - self.sdmmc_power_cycle()?; - - self.mmc_ios = self.hardware.sdmmc_host_reset()?; - + let res: Result<(), SdmmcError> = 'sdcard_init: { + let mut voltage_switch_init: bool = self + .host_capability + .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)); + let mut init_error: SdmmcError = SdmmcError::EUNSUPPORTEDCARD; + for _ in 0..CARD_INIT_RETRY { + match self.sdcard_init(voltage_switch_init) { + Ok(_) => break 'sdcard_init Ok(()), + Err(SdmmcError::EUNSUPPORTEDCARD) => { + break 'sdcard_init Err(SdmmcError::EUNSUPPORTEDCARD); + } + Err(e) => { + init_error = e; + if let Some(ref mut voltage_ops) = self.voltage_ops { + // Reset the signaling voltage back to 3.3V + voltage_ops.card_voltage_switch(MmcSignalVoltage::Voltage330)?; + self.sleep.usleep(1_000); + Self::sdmmc_power_cycle( + &mut self.sleep, + voltage_ops, + self.host_info.power_delay_ms, + )?; + } // One bug that does not break anything here is // sdmmc_host_reset will reset the clock to CardSetup timing and turn off the irq // But those variable in mmc_ios is not changed accordingly - - continue 'sdcard_init; - } else { - break 'sdcard_init; + self.mmc_ios = self.hardware.sdmmc_host_reset()?; + voltage_switch_init = false; } } } + Err(init_error) + }; + if res.is_ok() { let card: Sdcard = self.setup_sdcard_cont()?; self.mmc_device = Some(MmcDevice::Sdcard(card)); return Ok(()); @@ -483,8 +511,10 @@ impl SdmmcProtocol { // or the card is eMMC or legacy sdcard // For now, we only deal with the situation it is a sdcard // TODO: Implement setup for eMMC and legacy sdcard(SDSC) here - debug_log!("Driver right now only support SDHC/SDXC card, please check if you are running this driver on SDIO/SDSC/EMMC card!\n"); - Err(SdmmcError::EUNSUPPORTEDCARD) + dev_log!( + "Driver right now only support SDHC/SDXC card, please check if you are running this driver on SDIO/SDSC/EMMC card!\n" + ); + res } } @@ -504,7 +534,8 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R2, cmdarg: 0, }; - self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1)?; + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 1)?; let cid: Cid = Cid::new(resp); @@ -513,8 +544,8 @@ impl SdmmcProtocol { | ((resp[2] as u128) << 32) | (resp[3] as u128); - // TODO: Figure out a better way to do this than adding microkit crate - debug_log!( + // Print out the CID number + dev_log!( "CID: {:08x} {:08x} {:08x} {:08x}\n", resp[0], resp[1], @@ -529,11 +560,13 @@ impl SdmmcProtocol { cmdarg: 0, }; - self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1)?; + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 1)?; let rca: u16 = (resp[0] >> 16) as u16; // Store RCA from response - debug_log!("RCA: {:04x}\n", rca); + // Print out the RCA number we have got + dev_log!("RCA: {:04x}\n", rca); // Send CMD9 to get the CSD register cmd = SdmmcCmd { @@ -542,9 +575,10 @@ impl SdmmcProtocol { cmdarg: (rca as u32) << 16, }; - self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1)?; + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 1)?; - debug_log!( + dev_log!( "CSD: {:08x} {:08x} {:08x} {:08x}\n", resp[0], resp[1], @@ -552,7 +586,7 @@ impl SdmmcProtocol { resp[3] ); - let (csd, card_version) = Csd::new(resp); + let (csd, card_version) = Csd::new(resp)?; // Send CMD7 to select the card cmd = SdmmcCmd { @@ -561,7 +595,8 @@ impl SdmmcProtocol { cmdarg: (rca as u32) << 16, }; - self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 1)?; + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 1)?; // SDHC/SDXC default to 512 bytes sector size so I did not manually set it here @@ -659,15 +694,11 @@ impl SdmmcProtocol { physical_memory_addr, ) } - MmcDevice::EMmc(emmc) => Err(SdmmcError::ENOTIMPLEMENTED), + MmcDevice::EMmc(_emmc) => Err(SdmmcError::ENOTIMPLEMENTED), MmcDevice::Unknown => Err(SdmmcError::ENOTIMPLEMENTED), } } - pub fn get_host_info(&mut self) -> HostInfo { - self.host_info.clone() - } - pub fn test_read_one_block(&mut self, start_idx: u64, destination: u64) { let data: MmcData = MmcData { blocksize: 512, @@ -675,7 +706,7 @@ impl SdmmcProtocol { flags: MmcDataFlag::SdmmcDataRead, addr: destination, }; - debug_log!("Gonna test read one block!\n"); + dev_log!("Gonna test read one block!\n"); let mut resp: [u32; 4] = [0; 4]; let cmd_arg: u64 = start_idx; let cmd = SdmmcCmd { @@ -683,18 +714,19 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R1, cmdarg: cmd_arg as u32, }; - if let Err(error) = self - .hardware - .sdmmc_do_request(&cmd, Some(&data), &mut resp, 0) + if let Err(error) = + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, Some(&data), &mut resp, 0) { - debug_log!("Error in reading\n"); + dev_log!("Error: {:?} in reading\n", error); } unsafe { print_one_block(destination as *mut u8, 512) }; } /// Switch voltage function fn tune_sdcard_switch_uhs18v(&mut self) -> Result<(), SdmmcError> { - debug_log!("Entering tune sdcard signal voltage function!\n"); + dev_log!("Entering tune sdcard signal voltage function!\n"); + let mut resp: [u32; 4] = [0; 4]; let cmd = SdmmcCmd { cmdidx: SD_CMD_SWITCH_UHS18V, @@ -702,22 +734,23 @@ impl SdmmcProtocol { cmdarg: 0, // Argument for 4-bit mode (0 for 1-bit mode) }; - self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 0)?; - debug_log!("Switch voltage prepared!\n"); + dev_log!("Switch voltage prepared!\n"); self.mmc_ios.clock = self.hardware.sdmmc_config_timing(MmcTiming::ClockStop)?; // TODO: figuring out the optimal delay - process_wait_unreliable(100_000); + self.sleep.usleep(100); let mut signal: u8 = 0xFF; for _ in 0..100 { signal = self.hardware.sdmmc_read_datalanes()?; // TODO: figuring out the optimal delay - process_wait_unreliable(100_000); - debug_log!("data signal value: 0b{:b}\n", signal); + self.sleep.usleep(100); + dev_log!("data signal value: 0b{:b}\n", signal); if signal & 0xF == 0x0 { break; } @@ -727,31 +760,32 @@ impl SdmmcProtocol { return Err(SdmmcError::ECARDINACTIVE); } - self.hardware - .sdmmc_set_signal_voltage(MmcSignalVoltage::Voltage180)?; + if let Some(ref mut voltage_ops) = self.voltage_ops { + voltage_ops.card_voltage_switch(MmcSignalVoltage::Voltage180)?; + } else { + self.hardware + .sdmmc_voltage_switch(MmcSignalVoltage::Voltage180)? + } // TODO: figuring out the optimal delay - process_wait_unreliable(10_000_000); + self.sleep.usleep(10_000); self.mmc_ios.clock = self.hardware.sdmmc_config_timing(MmcTiming::CardSetup)?; // TODO: figuring out the optimal delay - process_wait_unreliable(100_000); + self.sleep.usleep(100); for _ in 0..100 { signal = self.hardware.sdmmc_read_datalanes()?; // TODO: figuring out the optimal delay - process_wait_unreliable(100_000); - debug_log!("data signal value: 0b{:b}\n", signal); + self.sleep.usleep(100); + dev_log!("data signal value: 0b{:b}\n", signal); if signal & 0xF == 0xF { break; } } if signal & 0xF != 0xF { - self.hardware - .sdmmc_set_signal_voltage(MmcSignalVoltage::Voltage330)?; - process_wait_unreliable(1_000_000); return Err(SdmmcError::ECARDINACTIVE); } @@ -798,11 +832,11 @@ impl SdmmcProtocol { cmdarg, }; self.hardware - .sdmmc_do_request(&cmd, Some(&data), &mut resp, 0)?; + .sdmmc_do_request(&mut self.sleep, &cmd, Some(&data), &mut resp, 0)?; // The use of fence here is actually wrong // As the fence(Ordering::Acquire) on arm platform - // The cache maintenance instructions are not ordered + // The cache maintenance instructions are not ordered // by the Load-Acquire and Store-Release instructions // But I will just leave it here as I cannot figure out // A more elegant way and code works fine anyway @@ -845,7 +879,7 @@ impl SdmmcProtocol { }; self.hardware - .sdmmc_do_request(&cmd, Some(&data), &mut resp, 0)?; + .sdmmc_do_request(&mut self.sleep, &cmd, Some(&data), &mut resp, 0)?; core::sync::atomic::fence(Ordering::Acquire); @@ -880,7 +914,7 @@ impl SdmmcProtocol { MmcSignalVoltage::Voltage120 => return Err(SdmmcError::EUNDEFINED), } // Add the card cap to self - if let Some(MmcDevice::Sdcard(ref mut sdcard)) = &mut self.mmc_device { + if let Some(MmcDevice::Sdcard(sdcard)) = &mut self.mmc_device { sdcard.card_cap |= card_cap; } else { return Err(SdmmcError::EINVAL); @@ -898,10 +932,11 @@ impl SdmmcProtocol { ) -> Result<(), SdmmcError> { let mut resp: [u32; 4] = [0; 4]; - if let Some(MmcDevice::Sdcard(ref mut sdcard)) = &mut self.mmc_device { + if let Some(MmcDevice::Sdcard(sdcard)) = &mut self.mmc_device { let scr: Scr = unsafe { Sdcard::sdcard_get_configuration_register( &mut self.hardware, + &mut self.sleep, physical_memory_addr, memory, cache_invalidate_function, @@ -934,25 +969,27 @@ impl SdmmcProtocol { resp_type: MMC_RSP_R1, cmdarg: (relative_card_address as u32) << 16, }; - self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 0)?; let cmd = SdmmcCmd { cmdidx: SD_CMD_APP_SET_BUS_WIDTH, resp_type: MMC_RSP_R1, cmdarg: 2, // Argument for 4-bit mode (0 for 1-bit mode) }; - self.hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; + self.hardware + .sdmmc_do_request(&mut self.sleep, &cmd, None, &mut resp, 0)?; self.hardware.sdmmc_config_bus_width(MmcBusWidth::Width4)?; - debug_log!("Tuning datalanes succeed!\n"); + dev_log!("Tuning datalanes succeed!\n"); // If any of the cmd above fail, the card should be completely reinit self.mmc_ios.bus_width = MmcBusWidth::Width4; // TODO: Change sdcard bus width here, or get rid of that field completely } - debug_log!("Checking supported speed classes\n"); + dev_log!("Checking supported speed classes\n"); if let Some(MmcDevice::Sdcard(ref mut sdcard)) = self.mmc_device { match self.mmc_ios.signal_voltage { @@ -993,10 +1030,10 @@ impl SdmmcProtocol { return Err(SdmmcError::EUNDEFINED); } - let mut target_timing; + let mut target_timing: MmcTiming; let sdcard_cap: SdcardCapability; if let Some(MmcDevice::Sdcard(ref sdcard)) = self.mmc_device { - debug_log!("Switch to higher speed class\n"); + dev_log!("Switch to higher speed class\n"); sdcard_cap = sdcard.card_cap.clone(); target_timing = sdcard.card_state.timing; // This 'tune_speed thing is a feature called labeled block in Rust @@ -1060,9 +1097,9 @@ impl SdmmcProtocol { self.mmc_ios.clock = self.hardware.sdmmc_config_timing(target_timing)?; self.hardware - .sdmmc_execute_tuning(physical_memory_addr as *mut [u8; 64])?; + .sdmmc_execute_tuning(physical_memory_addr as *mut [u8; 64], &mut self.sleep)?; - debug_log!("Current frequency: {}Hz\n", self.mmc_ios.clock); + dev_log!("Current frequency: {}Hz\n", self.mmc_ios.clock); } else { return Err(SdmmcError::EUNDEFINED); } @@ -1092,7 +1129,7 @@ impl SdmmcProtocol { blockcnt: u32, start_idx: u64, destination: u64, - ) -> (Result<(), SdmmcError>, SdmmcProtocol) { + ) -> (Result<(), SdmmcError>, SdmmcProtocol) { let trans_meth: BlockTransmissionMode = { if let Some(ref device) = self.mmc_device { match device { @@ -1176,7 +1213,9 @@ impl SdmmcProtocol { if let Some(memory) = self.private_memory { if turing == false { turing = true; - if let Ok(()) = self.hardware.sdmmc_execute_tuning(memory) { + if let Ok(()) = + self.hardware.sdmmc_execute_tuning(memory, &mut self.sleep) + { continue; } } @@ -1197,7 +1236,7 @@ impl SdmmcProtocol { blockcnt: u32, start_idx: u64, source: u64, - ) -> (Result<(), SdmmcError>, SdmmcProtocol) { + ) -> (Result<(), SdmmcError>, SdmmcProtocol) { let trans_meth: BlockTransmissionMode = { if let Some(ref device) = self.mmc_device { match device { @@ -1269,7 +1308,7 @@ impl SdmmcProtocol { mut self, start_idx: u64, end_idx: u64, - ) -> (Result<(), SdmmcError>, SdmmcProtocol) { + ) -> (Result<(), SdmmcError>, SdmmcProtocol) { let mut cmd: SdmmcCmd; let mut res: Result<(), SdmmcError>; @@ -1375,7 +1414,50 @@ impl SdmmcProtocol { } } } + + pub fn get_host_info(&mut self) -> HostInfo { + self.host_info.clone() + } + + pub fn print_card_info(&self) { + if let Some(ref device) = self.mmc_device { + match device { + MmcDevice::Sdcard(sdcard) => { + sdcard.print_info(); + } + MmcDevice::EMmc(_emmc) => { + dev_log!("eMMC card support is not available yet!\n"); + } + MmcDevice::Unknown => { + dev_log!("Unknown card!\n"); + } + } + } else { + dev_log!("No available device! Try initialize the card first\n"); + } + } + + pub fn card_info(&self) -> Result { + let res: Result; + if let Some(ref device) = self.mmc_device { + match device { + MmcDevice::Sdcard(sdcard) => { + res = Ok(sdcard.sdcard_info()); + } + MmcDevice::EMmc(_emmc) => { + res = Err(SdmmcError::ENOTIMPLEMENTED); + } + MmcDevice::Unknown => { + res = Err(SdmmcError::ENOTIMPLEMENTED); + } + } + } else { + res = Err(SdmmcError::ENOCARD); + } + res + } } + enum CmdState { // Waiting for the response WaitingForResponse, @@ -1453,10 +1535,10 @@ unsafe fn print_one_block(ptr: *const u8, num: usize) { for i in 0..num { let byte = *ptr.add(i); if i % 16 == 0 { - debug_log!("\n{:04x}: ", i); + dev_log!("\n{:04x}: ", i); } - debug_log!("{:02x} ", byte); + dev_log!("{:02x} ", byte); } - debug_log!("\n"); + dev_log!("\n"); } } diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs index 7c632a541..f8e90e52a 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs @@ -9,13 +9,6 @@ pub enum MmcBusWidth { Width8 = 3, } -#[derive(Debug)] -pub enum TuningState { - TuningStart = 0, - TuningContinue = 1, - TuningComplete = 2, -} - // Timing modes (could be an enum or use the bitflags constants defined earlier) #[derive(Debug, Copy, Clone, PartialEq)] pub enum MmcTiming { @@ -38,7 +31,7 @@ pub enum MmcTiming { } #[derive(Debug, Clone)] -pub(crate) struct MmcState { +pub struct MmcState { /// The timing specification that dictates how data is transferred between the host /// and the card. /// @@ -48,7 +41,7 @@ pub(crate) struct MmcState { /// - `Timing::Legacy`: Legacy slower transfer mode. /// - `Timing::SdHs`: SD high-speed mode. /// - `Timing::MmcHs200`: eMMC HS200 mode for high-speed data transfers. - pub(crate) timing: MmcTiming, + pub timing: MmcTiming, /// The width of the data bus used for communication between the host and the card. /// @@ -59,10 +52,11 @@ pub(crate) struct MmcState { /// - `BusWidth::Width1`: 1-bit data width (lowest speed, used during initialization). /// - `BusWidth::Width4`: 4-bit data width (common for SD cards). /// - `BusWidth::Width8`: 8-bit data width (mainly for eMMC). - pub(crate) bus_width: MmcBusWidth, + pub bus_width: MmcBusWidth, } /// Some of the MmcDevice is reserved for future use +#[allow(dead_code)] pub(crate) enum MmcDevice { Sdcard(Sdcard), EMmc(EMmc), @@ -71,6 +65,8 @@ pub(crate) enum MmcDevice { } /// Represents the different states of an SD or eMMC card. +/// Not used yet +#[allow(dead_code)] #[derive(Debug, PartialEq)] enum CardState { Idle, @@ -94,3 +90,10 @@ pub enum BlockTransmissionMode { // Host automatically send stop command without the need to driver interference AutoStop = 2, } + +#[derive(Debug)] +pub struct CardInfo { + pub card_id: u128, + pub card_capacity: u64, + pub card_state: MmcState, +} diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs index 4f78fa4d3..bb798628d 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs @@ -1,17 +1,18 @@ use core::sync::atomic::Ordering; -use crate::sdmmc_traits::SdmmcHardware; +use crate::{sdmmc_os::Sleep, sdmmc_traits::SdmmcHardware}; use super::{ + MMC_RSP_R1, MmcData, MmcDataFlag, SdmmcCmd, SdmmcError, sdcard::{Scr, Sdcard}, sdmmc_constant::{MMC_CMD_APP_CMD, SD_CMD_APP_SEND_SCR, SD_CMD_SWITCH_FUNC}, - MmcData, MmcDataFlag, SdmmcCmd, SdmmcError, MMC_RSP_R1, }; impl Sdcard { /// Unsafe because dereference raw pointer pub(crate) unsafe fn sdcard_get_configuration_register( hardware: &mut T, + sleep: &mut dyn Sleep, physical_memory: u64, raw_memory: *mut [u8; 64], invalidate_cache_fn: fn(), @@ -23,7 +24,7 @@ impl Sdcard { resp_type: MMC_RSP_R1, cmdarg: (rca as u32) << 16, }; - hardware.sdmmc_do_request(&cmd, None, &mut resp, 0)?; + hardware.sdmmc_do_request(sleep, &cmd, None, &mut resp, 0)?; cmd = SdmmcCmd { cmdidx: SD_CMD_APP_SEND_SCR, @@ -37,14 +38,14 @@ impl Sdcard { addr: physical_memory, }; - hardware.sdmmc_do_request(&cmd, Some(&data), &mut resp, 0)?; + hardware.sdmmc_do_request(sleep, &cmd, Some(&data), &mut resp, 0)?; core::sync::atomic::fence(Ordering::Acquire); invalidate_cache_fn(); // print out the content of the SCR register - sel4_microkit_support::debug_log!("SCR register content: "); + crate::dev_log!("SCR register content: "); unsafe { crate::sdmmc::print_one_block(raw_memory as *const u8, 8) }; // The sdcard register data is always in big endian format @@ -64,6 +65,7 @@ impl Sdcard { pub fn sdcard_test_tuning( hardware: &mut T, + sleep: &mut dyn Sleep, memory: *mut [u8; 64], ) -> Result<(), SdmmcError> { let mut resp: [u32; 4] = [0; 4]; @@ -81,6 +83,6 @@ impl Sdcard { cmdarg: 0x00FFFFFF, }; - hardware.sdmmc_do_request(&cmd, Some(&data), &mut resp, 1) + hardware.sdmmc_do_request(sleep, &cmd, Some(&data), &mut resp, 1) } } diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs index ef34e3833..61fb467a0 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs @@ -1,13 +1,12 @@ -use core::fmt::{self, Write}; - -use sel4_microkit_support::debug_log; +use crate::{dev_log, info, sdmmc::mmc_struct::CardInfo}; use super::{ - mmc_struct::{self, BlockTransmissionMode, MmcBusWidth, MmcState}, + SdmmcError, + mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcState}, sdmmc_capability::SdcardCapability, - SdmmcError, SdmmcHardware, SdmmcProtocol, }; +#[allow(dead_code)] pub struct Sdcard { pub(crate) card_id: u128, pub(crate) manufacture_info: Cid, @@ -20,6 +19,113 @@ pub struct Sdcard { pub(crate) card_config: Option, } +impl Sdcard { + pub fn print_info(&self) { + const LABEL_WIDTH: usize = 20; + const DATA_WIDTH: usize = 25; + let capacity_bytes = self.card_specific_data.card_capacity; + + const KB: u64 = 1024; + const MB: u64 = 1024 * KB; + const GB: u64 = 1024 * MB; + const TB: u64 = 1024 * GB; + + info!("\n\n╔═════════════════════════════════════════════════╗"); + info!("║ SDCARD INFORMATION ║"); + info!("╠═════════════════════════════════════════════════╣"); + info!( + "║ {:= TB => (c / TB, (c % TB) / GB, "TB"), + c if c >= GB => (c / GB, (c % GB) / MB, "GB"), + c if c >= MB => (c / MB, (c % MB) / KB, "MB"), + c if c >= KB => (c / KB, c % KB, "KB"), + c => (c, 0, "Bytes"), + }; + if fraction == 0 { + info!( + "║ {: CardInfo { + CardInfo { + card_id: self.card_id, + card_capacity: self.card_specific_data.card_capacity, + card_state: self.card_state.clone(), + } + } +} + /// Placeholder eMMC struct that is not implemented pub struct EMmc { pub card_id: u128, @@ -31,6 +137,7 @@ pub struct EMmc { // But mean that the sdcard support cmd at least up to specification 3.0 // The SD card specification is cumulative, meaning that if an SD card reports support for a // particular version (say 4.0), it implicitly supports all earlier versions as well. +#[allow(dead_code)] #[derive(Debug, PartialEq, Eq)] pub(crate) enum SdVersion { V1_0 = 1, @@ -39,6 +146,7 @@ pub(crate) enum SdVersion { V4_0 = 4, } +#[derive(Debug)] pub(crate) struct Cid { manufacturer_id: u8, oem_id: u16, @@ -51,7 +159,7 @@ pub(crate) struct Cid { impl Cid { pub fn new(cid: [u32; 4]) -> Cid { // Combine the 4 u32 words into a single 128-bit CID value for easy bit manipulation - let cid_combined = ((cid[0] as u128) << 96) + let cid_combined: u128 = ((cid[0] as u128) << 96) | ((cid[1] as u128) << 64) | ((cid[2] as u128) << 32) | (cid[3] as u128); @@ -61,7 +169,7 @@ impl Cid { let oem_id = ((cid_combined >> 104) & 0xFFFF) as u16; // Extract product name, which is 5 bytes (40 bits) - let product_name = [ + let product_name: [u8; 5] = [ ((cid_combined >> 96) & 0xFF) as u8, ((cid_combined >> 88) & 0xFF) as u8, ((cid_combined >> 80) & 0xFF) as u8, @@ -69,12 +177,12 @@ impl Cid { ((cid_combined >> 64) & 0xFF) as u8, ]; - let product_revision = ((cid_combined >> 56) & 0xFF) as u8; - let serial_number = ((cid_combined >> 24) & 0xFFFFFFFF) as u32; + let product_revision: u8 = ((cid_combined >> 56) & 0xFF) as u8; + let serial_number: u32 = ((cid_combined >> 24) & 0xFFFFFFFF) as u32; // Extract year and month from the manufacturing date - let year = ((cid_combined >> 12) & 0x0F) as u32 + 2000; - let month = ((cid_combined >> 8) & 0x0F) as u8; + let year: u32 = ((cid_combined >> 12) & 0xFF) as u32 + 2000; + let month: u8 = ((cid_combined >> 8) & 0x0F) as u8; Cid { manufacturer_id, @@ -87,34 +195,9 @@ impl Cid { } } -trait ToArray { - fn to_array(&self) -> [u8; 128]; // A fixed buffer size that you can change as needed -} - -impl ToArray for Cid { - fn to_array(&self) -> [u8; 128] { - let mut buf = [0u8; 128]; - let mut writer = ArrayWriter::new(&mut buf); - - write!( - writer, - "Manufacturer ID: {}\nOEM ID: {}\nProduct Name: {}\nProduct Revision: {}\n\ - Serial Number: {}\nManufacturing Date: {}-{}\n", - self.manufacturer_id, - self.oem_id, - core::str::from_utf8(&self.product_name).unwrap_or("?????"), - self.product_revision, - self.serial_number, - self.manufacturing_date.0, - self.manufacturing_date.1, - ) - .ok(); - - buf - } -} - // This struct is super unreliable, I am thinking +#[allow(dead_code)] +#[derive(Debug)] pub(crate) struct Csd { csd_structure: u8, card_capacity: u64, @@ -125,62 +208,63 @@ pub(crate) struct Csd { } impl Csd { - pub fn new(csd: [u32; 4]) -> (Csd, SdVersion) { + pub fn new(csd: [u32; 4]) -> Result<(Csd, SdVersion), SdmmcError> { // Combine the four 32-bit words into a single 128-bit value for easier bit manipulation - let csd_combined = ((csd[0] as u128) << 96) + let csd_combined: u128 = ((csd[0] as u128) << 96) | ((csd[1] as u128) << 64) | ((csd[2] as u128) << 32) | (csd[3] as u128); // Extract the CSD structure version - let csd_structure = ((csd_combined >> 126) & 0x3) as u8; // Bits 126–127 - let sd_version = match csd_structure { + let csd_structure: u8 = ((csd_combined >> 126) & 0x3) as u8; // Bits 126–127 + let sd_version: SdVersion = match csd_structure { 0 => SdVersion::V1_0, // CSD Version 1.0 1 => SdVersion::V2_0, // CSD Version 2.0 // Even if the parsing csd fails, it should not crash the driver completely - _ => panic!("Unsupported CSD structure version"), // CSD structures beyond 2.0 are not supported here - // Actually SDUC card are using CSD 3.0 so maybe add something here later. + _ => return Err(SdmmcError::EUNSUPPORTEDCARD), // CSD structures beyond 2.0 are not supported here + // Actually SDUC card are using CSD 3.0 so maybe add something here later. }; // Parse fields based on CSD version let (card_capacity, erase_sector_size) = match sd_version { SdVersion::V1_0 => { // CSD Version 1.0 capacity calculation - let c_size = ((csd_combined >> 62) & 0xFFF) as u64; // Bits 62–73 - let c_size_mult = ((csd_combined >> 47) & 0x7) as u64; // Bits 47–49 - let read_bl_len = ((csd_combined >> 80) & 0xF) as u64; // Bits 80–83 - let card_capacity = (c_size + 1) * (1 << (c_size_mult + 2)) * (1 << read_bl_len); + let c_size: u64 = ((csd_combined >> 62) & 0xFFF) as u64; // Bits 62–73 + let c_size_mult: u64 = ((csd_combined >> 47) & 0x7) as u64; // Bits 47–49 + let read_bl_len: u64 = ((csd_combined >> 80) & 0xF) as u64; // Bits 80–83 + let card_capacity: u64 = + (c_size + 1) * (1 << (c_size_mult + 2)) * (1 << read_bl_len); // Erase sector size is calculated differently in CSD Version 1.0 - let sector_size = ((csd_combined >> 39) & 0x7F) as u32 + 1; // Bits 39–45 + let sector_size: u32 = ((csd_combined >> 39) & 0x7F) as u32 + 1; // Bits 39–45 (card_capacity, sector_size) } SdVersion::V2_0 => { // CSD Version 2.0 capacity calculation for SDHC/SDXC - let c_size = ((csd_combined >> 48) & 0x3FFFF) as u64; // Bits 48–69 - let card_capacity = (c_size + 1) * 512 * 1024; // Capacity formula for SDHC/SDXC + let c_size: u64 = ((csd_combined >> 48) & 0x3FFFFF) as u64; // Bits 48–69 + let card_capacity: u64 = (c_size + 1) * 512 * 1024; // Capacity formula for SDHC/SDXC // Erase sector size calculation for CSD Version 2.0 - let sector_size = ((csd_combined >> 39) & 0x7F + 1) as u32 * 512; // Bits 39–45 + let sector_size: u32 = ((csd_combined >> 39) & 0x7F + 1) as u32 * 512; // Bits 39–45 (card_capacity, sector_size) } - SdVersion::V3_0 => unreachable!(), - SdVersion::V4_0 => unreachable!(), + SdVersion::V3_0 => return Err(SdmmcError::EINVAL), + SdVersion::V4_0 => return Err(SdmmcError::EINVAL), }; // Block lengths (same for both versions) - let read_bl_len = ((csd_combined >> 80) & 0xF) as u16; // Bits 80–83 - let max_read_block_len = 1 << read_bl_len; + let read_bl_len: u16 = ((csd_combined >> 80) & 0xF) as u16; // Bits 80–83 + let max_read_block_len: u16 = 1 << read_bl_len; - let write_bl_len = ((csd_combined >> 22) & 0xF) as u16; // Bits 22–25 - let max_write_block_len = 1 << write_bl_len; + let write_bl_len: u16 = ((csd_combined >> 22) & 0xF) as u16; // Bits 22–25 + let max_write_block_len: u16 = 1 << write_bl_len; // Partial write support (same for both versions) - let supports_partial_write = ((csd_combined >> 21) & 0x1) != 0; // Bit 21 + let supports_partial_write: bool = ((csd_combined >> 21) & 0x1) != 0; // Bit 21 // Return the constructed CSD struct along with the SD version - ( + Ok(( Csd { csd_structure, card_capacity, @@ -190,10 +274,12 @@ impl Csd { supports_partial_write, }, sd_version, - ) + )) } } +#[allow(dead_code)] +#[derive(Debug)] pub(crate) struct Scr { // Not extracted from Scr parsing yet pub sd_spec: u32, @@ -231,13 +317,13 @@ impl Scr { supported_cmd[i] = (command_support_bits & (1 << i)) != 0; } - debug_log!("Supported cmd: {:?}\n", supported_cmd); + dev_log!("Supported cmd: {:?}\n", supported_cmd); let mut data_stat_after_erase: bool = false; if scr_raw & (0b1 << 55) != 0 { data_stat_after_erase = true; } - debug_log!("Data status after erase: {:?}\n", data_stat_after_erase); + dev_log!("Data status after erase: {:?}\n", data_stat_after_erase); Ok(Scr { sd_spec: 0, @@ -252,84 +338,3 @@ impl Scr { }) } } - -struct ArrayWriter<'a> { - buf: &'a mut [u8], - pos: usize, -} - -impl<'a> ArrayWriter<'a> { - fn new(buf: &'a mut [u8]) -> Self { - Self { buf, pos: 0 } - } -} - -impl<'a> Write for ArrayWriter<'a> { - fn write_str(&mut self, s: &str) -> fmt::Result { - let bytes = s.as_bytes(); - let len = bytes.len(); - - if self.pos + len > self.buf.len() { - return Err(fmt::Error); - } - - self.buf[self.pos..self.pos + len].copy_from_slice(bytes); - - self.pos += len; - Ok(()) - } -} - -/* -/// Parses the R1 response from an SD card command. -/// The function takes a 32-bit unsigned integer representing the R1 response -/// and returns a structured report of the status. -/// -/// # Arguments -/// * `response` - The 32-bit R1 response from the SD card command. -/// -/// # Returns -/// A result with either Ok (no errors) or an error string describing the problem. -fn parse_r1_response(response: u32) -> Result { - // Check each status bit and gather relevant info. - let out_of_range = (response >> 31) & 1 != 0; - let address_error = (response >> 30) & 1 != 0; - let block_len_error = (response >> 29) & 1 != 0; - let erase_seq_error = (response >> 28) & 1 != 0; - let ready_for_data = (response >> 8) & 1 != 0; - let idle_state = (response >> 0) & 1 != 0; - - // Extract card state (bits 9-12). - let card_state = (response >> 9) & 0xF; - - // Collect error messages for any issues found. - let mut errors = Vec::new(); - if out_of_range { errors.push("Out of range error"); } - if address_error { errors.push("Address error"); } - if block_len_error { errors.push("Block length error"); } - if erase_seq_error { errors.push("Erase sequence error"); } - - // Parse and interpret card state - let state_description = match card_state { - 0 => "Idle", - 1 => "Ready", - 2 => "Identification", - 3 => "Standby", - 4 => "Transfer", - 5 => "Sending Data", - 6 => "Receive Data", - 7 => "Programming", - 8 => "Disconnect", - _ => "Unknown", - }; - - // Report ready status or errors. - if !errors.is_empty() { - Err(format!("R1 response contains errors: {}", errors.join(", "))) - } else if !ready_for_data { - Err("Card is not ready for data.".to_string()) - } else { - Ok(format!("R1 response is valid. Card state: {}", state_description)) - } -} -*/ diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs index e8a3d29e0..3eddc8c13 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs @@ -29,8 +29,6 @@ bitflags! { const MMC_CAP_8_BIT_DATA = MMC_CAP_8_BIT_DATA; const MMC_CAP_BUS_WIDTH_TEST = MMC_CAP_BUS_WIDTH_TEST; - const MMC_CAP_VOLTAGE_TUNE = MMC_CAP_VOLTAGE_TUNE; - const MMC_CAP_CMD23 = MMC_CAP_CMD23; const MMC_CAP_AUTO_STOP = MMC_CAP_AUTO_STOP; } } @@ -90,7 +88,6 @@ pub const MMC_CAP_8_BIT_DATA: u128 = 1 << 17; pub const MMC_CAP_BUS_WIDTH_TEST: u128 = 1 << 18; -pub const MMC_CAP_VOLTAGE_TUNE: u128 = 1 << 29; pub const MMC_CAP_CMD23: u128 = 1 << 30; pub const MMC_CAP_AUTO_STOP: u128 = 1 << 31; diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs index 3dee3460c..06cee1794 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs @@ -1,21 +1,114 @@ -#[cfg(feature = "sel4-microkit")] -pub use sel4_microkit_support::process_wait_unreliable; +use crate::sdmmc::MmcPowerMode; +use crate::sdmmc::MmcSignalVoltage; +use crate::sdmmc::SdmmcError; +use core::sync::atomic::AtomicU8; +use core::sync::atomic::Ordering; -#[cfg(feature = "sel4-microkit")] -pub use sel4_microkit_support::debug_log; +pub trait Sleep { + /// For putting the process to sleep for a while, + /// The default spinning implementation is a very unreliable way to put the process to sleep + fn usleep(&mut self, time_us: u32) { + let time_ns: u64 = time_us as u64 * 1000; + for _ in 0..time_ns { + core::hint::spin_loop(); // Use spin loop hint to reduce contention during the wait + } + } +} + +pub trait VoltageOps { + fn card_voltage_switch(&mut self, voltage: MmcSignalVoltage) -> Result<(), SdmmcError> { + core::panic!("Voltage switch not implemented!"); + } + + fn card_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { + core::panic!("Power cycling not implemented!"); + } +} -#[cfg(not(feature = "sel4-microkit"))] -#[inline] pub fn process_wait_unreliable(time_ns: u64) { for _ in 0..time_ns { - hint::spin_loop(); // Use spin loop hint to reduce contention during the wait + core::hint::spin_loop(); // Use spin loop hint to reduce contention during the wait + } +} + +pub trait Log { + fn log(&self, args: core::fmt::Arguments) { + core::panic!("Logging not implemented!"); + } +} + +const UNINITIALIZED: u8 = 0; +const INITIALIZING: u8 = 1; +const INITIALIZED: u8 = 2; +static LOGGER_STATE: AtomicU8 = AtomicU8::new(UNINITIALIZED); +static mut LOGGER: Option<&'static dyn Log> = None; + +/// Why it is unsafe: A trait object of Log should provide a thread safe implementation of Log object +/// The crate treat the log function provided as a THREAD SAFE object! +pub unsafe fn set_logger(logger: &'static dyn Log) -> Result<(), ()> { + // 1. Try to acquire the "write lock". + // Try to change `UNINITIALIZED` to `INITIALIZING`. If it was not `UNINITIALIZED`, another thread is setting it. + if LOGGER_STATE + .compare_exchange( + UNINITIALIZED, + INITIALIZING, + Ordering::Relaxed, + Ordering::Relaxed, + ) + .is_ok() + { + // 2. Write to the non-atomic static mut. + // This is the operation we need to protect. + unsafe { + LOGGER = Some(logger); + } + + // Signal that the data is ready. + // We use Release ordering here. This is the crucial part! + LOGGER_STATE.store(INITIALIZED, Ordering::Release); + return Ok(()); + } + + Err(()) +} + +#[doc(hidden)] +pub fn __logging_helper(args: core::fmt::Arguments) { + // 1. Check if the logger is initialized. + // We use Acquire ordering here. This pairs with the `Release` store above. + unsafe { + if LOGGER_STATE.load(Ordering::Acquire) == INITIALIZED { + if let Some(logger) = LOGGER { + logger.log(args); + } + } + } +} + +#[macro_export] +macro_rules! print_info { + ($($arg:tt)*) => { + $crate::sdmmc_os::__logging_helper(format_args!($($arg)*)); } } -/// Bare metal -#[cfg(not(feature = "sel4-microkit"))] #[macro_export] -// No operation, would be optimized out -macro_rules! debug_log { +macro_rules! info { + ($($arg:tt)*) => { + $crate::print_info!("{}\n", format_args!($($arg)*)); + } +} + +#[cfg(not(feature = "dev-logs"))] +#[macro_export] +macro_rules! dev_log { ($($arg:tt)*) => {}; } + +#[cfg(feature = "dev-logs")] +#[macro_export] +macro_rules! dev_log { + ($($arg:tt)*) => { + $crate::sdmmc_os::__logging_helper(format_args!($($arg)*)); + } +} diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs index 28dfa3a2f..02fe373ba 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs @@ -1,27 +1,25 @@ -use sel4_microkit_support::{debug_log, process_wait_unreliable}; - -use crate::sdmmc::{ - mmc_struct::{MmcBusWidth, MmcTiming, TuningState}, - HostInfo, MmcData, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, SdmmcError, +use crate::{ + dev_log, + sdmmc::{ + HostInfo, MmcData, MmcIos, MmcSignalVoltage, SdmmcCmd, SdmmcError, + mmc_struct::{MmcBusWidth, MmcTiming}, + }, + sdmmc_os::Sleep, }; -const POLLING_INTERVAL_TIME_US: u32 = 1024; +// const POLLING_INTERVAL_TIME_US: u32 = 1024; const DATA_TRANSFER_POLLING_INTERVAL_TIME_US: u32 = 4096; + // The polling chances before time out is deliberately being set to a large value // as the host is supposed to catch thetimeout request and report to us -const POLLING_CHANCE_BEFORE_TIME_OUT: u32 = 1024; +const POLLING_CHANCE_BEFORE_TIME_OUT: u32 = 10240; const DATA_TRANSFER_POLLING_CHANCE_BEFORE_TIME_OUT: u32 = 2048; #[allow(unused_variables)] -/// Program async Rust can be very dangerous if you do not know what is happening understand the hood -/// Power up and power off cannot be properly implemented if I do not have access to control gpio/ regulator and timer +/// Trait to be implemented by the sdcard hal pub trait SdmmcHardware { - fn sdmmc_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { - return Err(SdmmcError::ENOTIMPLEMENTED); - } - fn sdmmc_init(&mut self) -> Result<(MmcIos, HostInfo, u128), SdmmcError> { - return Err(SdmmcError::ENOTIMPLEMENTED); + Err(SdmmcError::ENOTIMPLEMENTED) } /// Change the clock, return the value or do not change it at all @@ -29,15 +27,11 @@ pub trait SdmmcHardware { /// Beware at higher frequency, you may need to play with delay, adjust and clock phase /// to ensure that the clock edges (sampling points) occur just in time for the valid data window. fn sdmmc_config_timing(&mut self, timing: MmcTiming) -> Result { - return Err(SdmmcError::ENOTIMPLEMENTED); + Err(SdmmcError::ENOTIMPLEMENTED) } fn sdmmc_config_bus_width(&mut self, bus_width: MmcBusWidth) -> Result<(), SdmmcError> { - return Err(SdmmcError::ENOTIMPLEMENTED); - } - - fn sdmmc_set_signal_voltage(&mut self, voltage: MmcSignalVoltage) -> Result<(), SdmmcError> { - return Err(SdmmcError::ENOTIMPLEMENTED); + Err(SdmmcError::ENOTIMPLEMENTED) } /// Reads the current state of the SD card data lanes. @@ -53,7 +47,7 @@ pub trait SdmmcHardware { /// Note: /// - This function is not yet implemented and currently returns an `ENOTIMPLEMENTED` error. fn sdmmc_read_datalanes(&self) -> Result { - return Err(SdmmcError::ENOTIMPLEMENTED); + Err(SdmmcError::ENOTIMPLEMENTED) } /// Sends a command to the SD/MMC card, ensuring that busy signal handling is managed appropriately. @@ -87,7 +81,7 @@ pub trait SdmmcHardware { cmd: &SdmmcCmd, data: Option<&MmcData>, ) -> Result<(), SdmmcError> { - return Err(SdmmcError::ENOTIMPLEMENTED); + Err(SdmmcError::ENOTIMPLEMENTED) } fn sdmmc_receive_response( @@ -95,7 +89,7 @@ pub trait SdmmcHardware { cmd: &SdmmcCmd, response: &mut [u32; 4], ) -> Result<(), SdmmcError> { - return Err(SdmmcError::ENOTIMPLEMENTED); + Err(SdmmcError::ENOTIMPLEMENTED) } // Change the function signature to something like sdmmc_config_interrupt(&mut self, enable_irq: bool, enable_sdio_irq: bool); @@ -104,11 +98,11 @@ pub trait SdmmcHardware { enable_irq: bool, enable_sdio_irq: bool, ) -> Result<(), SdmmcError> { - return Err(SdmmcError::ENOTIMPLEMENTED); + Err(SdmmcError::ENOTIMPLEMENTED) } fn sdmmc_ack_interrupt(&mut self) -> Result<(), SdmmcError> { - return Err(SdmmcError::ENOTIMPLEMENTED); + Err(SdmmcError::ENOTIMPLEMENTED) } /// At higher clock frequencies, timing mismatches can occur between the host's sampling point and the valid data window @@ -122,16 +116,30 @@ pub trait SdmmcHardware { /// signals sent from the host to the SD card. This would ensure that the SD card reliably receives data, especially /// at high frequencies. However, output timing tends to be more stable, and a specific function for tuning host-to-card /// data timing is often not implemented or needed, as seen in the Linux driver. - fn sdmmc_execute_tuning(&mut self, memory: *mut [u8; 64]) -> Result<(), SdmmcError> { - return Err(SdmmcError::ENOTIMPLEMENTED); + fn sdmmc_execute_tuning( + &mut self, + memory: *mut [u8; 64], + sleep: &mut dyn Sleep, + ) -> Result<(), SdmmcError> { + Err(SdmmcError::ENOTIMPLEMENTED) } fn sdmmc_host_reset(&mut self) -> Result { - return Err(SdmmcError::ENOTIMPLEMENTED); + Err(SdmmcError::ENOTIMPLEMENTED) + } + + /// This function implements the bare metal version of adjust signaling voltage + /// If in the SdmmcProtocol::new() function, there are different voltage switch + /// methods provided, this sdmmc_voltage_switch function will not be used + /// Power cycling is assumed in host reset function if voltage_switch is implemented here + fn sdmmc_voltage_switch(&mut self, voltage: MmcSignalVoltage) -> Result<(), SdmmcError> { + // Default behavior should be rushing instead of returning one error + panic!("sdmmc: voltage switch is not implemented!") } fn sdmmc_do_request( &mut self, + sleep: &mut dyn Sleep, cmd: &SdmmcCmd, data: Option<&MmcData>, resp: &mut [u32; 4], @@ -147,9 +155,7 @@ pub trait SdmmcHardware { // The flow with data transfer Some(_) => { for _ in 0..DATA_TRANSFER_POLLING_CHANCE_BEFORE_TIME_OUT { - process_wait_unreliable( - DATA_TRANSFER_POLLING_INTERVAL_TIME_US as u64 * 1000, - ); + sleep.usleep(DATA_TRANSFER_POLLING_INTERVAL_TIME_US); res = self.sdmmc_receive_response(cmd, resp); match res { Err(SdmmcError::ETIMEDOUT) => { @@ -168,7 +174,12 @@ pub trait SdmmcHardware { // The flow without data transfer None => { for _ in 0..POLLING_CHANCE_BEFORE_TIME_OUT { - process_wait_unreliable(POLLING_INTERVAL_TIME_US as u64 * 1000); + // There seems to be card that are actually time-sensitive to certain command + // Like if the driver polling the voltage switch command too slow and switch voltage a bit late + // Card will not switch voltage successfully. + // So choosing the fitting polling interval here is very important to correctness + // For correctness reason, right now we don't add any polling interval + // process_wait_unreliable(POLLING_INTERVAL_TIME_US as u64 * 1000); res = self.sdmmc_receive_response(cmd, resp); match res { Err(SdmmcError::ETIMEDOUT) => { @@ -187,7 +198,7 @@ pub trait SdmmcHardware { } break 'command_retry; } - debug_log!("A timeout request not reported by the host, the host might be unreliable\n"); + dev_log!("A timeout request not reported by the host, the host might be unreliable\n"); Err(SdmmcError::EUNDEFINED) } } From e82c55a74e3ad5c013110ab89f8ae1bdc1feebd3 Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 6 Aug 2025 15:10:30 +1000 Subject: [PATCH 34/48] Add license and cargo.lock. Fixes for minor issues. Signed-off-by: Cheng --- .gitignore | 1 - .reuse/dep5 | 4 + drivers/blk/sdmmc/Cargo.lock | 867 ++++++++++++++++++ drivers/blk/sdmmc/Cargo.toml | 3 + drivers/blk/sdmmc/blk_driver.mk | 6 + drivers/blk/sdmmc/build.rs | 3 + drivers/blk/sdmmc/libsddfblk.a | Bin 15714 -> 0 bytes drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml | 3 + drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs | 3 + .../blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 3 + drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml | 3 + drivers/blk/sdmmc/sdmmc_protocol/lib.rs | 3 + drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs | 13 +- .../sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs | 3 + .../blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs | 3 + .../blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs | 3 + .../sdmmc_protocol/sdmmc/sdmmc_capability.rs | 3 + .../sdmmc_protocol/sdmmc/sdmmc_constant.rs | 5 +- drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs | 3 + .../blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs | 3 + drivers/blk/sdmmc/src/main.rs | 14 +- drivers/blk/sdmmc/src/sddf_blk/mod.rs | 3 + drivers/blk/sdmmc/src/sddf_helper.c | 5 + drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs | 3 + .../sdmmc/src/sel4_microkit_os/odroidc4.rs | 3 + include/sddf_rust/timer/Cargo.toml | 13 - include/sddf_rust/timer/lib.rs | 3 - include/sddf_rust/timer/timer.rs | 30 - 28 files changed, 951 insertions(+), 58 deletions(-) create mode 100644 drivers/blk/sdmmc/Cargo.lock delete mode 100644 drivers/blk/sdmmc/libsddfblk.a delete mode 100644 include/sddf_rust/timer/Cargo.toml delete mode 100644 include/sddf_rust/timer/lib.rs delete mode 100644 include/sddf_rust/timer/timer.rs diff --git a/.gitignore b/.gitignore index 7781b50be..0e43bb224 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,6 @@ zig-out/ .zig-cache/ venv/ target/ -Cargo.lock __pycache__ .*.sw* *.dtb diff --git a/.reuse/dep5 b/.reuse/dep5 index 11bd3c99a..7cdf82ec0 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -4,6 +4,8 @@ Source: https://github.com/au-ts/sddf Files: flake.lock + *Cargo.lock + *aarch64-sel4-microkit-minimal.json CHANGES.md build.zig.zon docs/design/* @@ -34,6 +36,8 @@ Files: drivers/timer/goldfish/config.json drivers/timer/cdns/config.json drivers/timer/bcm2835/config.json + drivers/blk/sdmmc/config.json + drivers/blk/sdmmc/README.md Copyright: UNSW License: BSD-2-Clause diff --git a/drivers/blk/sdmmc/Cargo.lock b/drivers/blk/sdmmc/Cargo.lock new file mode 100644 index 000000000..8e26ccc65 --- /dev/null +++ b/drivers/blk/sdmmc/Cargo.lock @@ -0,0 +1,867 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "blk_driver" +version = "0.1.0" +dependencies = [ + "meson_hal", + "sdmmc_protocol", + "sel4-microkit", + "sel4-panicking-env", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dlmalloc" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa3a2dbee57b69fbb5dbe852fa9c0925697fb0c7fbcb1593e90e5ffaedf13d51" +dependencies = [ + "cfg-if", + "libc", + "windows-sys", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "meson_hal" +version = "0.1.0" +dependencies = [ + "sdmmc_protocol", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "one-shot-mutex" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb10614a03e671fcbb7f1421656788f9a761aab44563b83b07140c354fa9334" +dependencies = [ + "lock_api", +] + +[[package]] +name = "pest" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdmmc_protocol" +version = "0.1.0" +dependencies = [ + "bitflags", +] + +[[package]] +name = "sel4" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "cfg-if", + "sel4-config", + "sel4-sys", +] + +[[package]] +name = "sel4-alloca" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "sel4-bitfield-ops" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "rustversion", +] + +[[package]] +name = "sel4-build-env" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" + +[[package]] +name = "sel4-config" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sel4-config-data", + "sel4-config-macros", + "sel4-config-types", + "syn", +] + +[[package]] +name = "sel4-config-data" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "lazy_static", + "sel4-build-env", + "sel4-config-types", + "serde_json", +] + +[[package]] +name = "sel4-config-macros" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "fallible-iterator", + "proc-macro2", + "quote", + "sel4-config-data", + "sel4-config-types", + "syn", +] + +[[package]] +name = "sel4-config-types" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "serde", +] + +[[package]] +name = "sel4-ctors-dtors" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" + +[[package]] +name = "sel4-dlmalloc" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "dlmalloc", + "lock_api", +] + +[[package]] +name = "sel4-elf-header" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" + +[[package]] +name = "sel4-immediate-sync-once-cell" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" + +[[package]] +name = "sel4-immutable-cell" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" + +[[package]] +name = "sel4-initialize-tls" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "cfg-if", + "sel4-alloca", +] + +[[package]] +name = "sel4-microkit" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "cfg-if", + "one-shot-mutex", + "sel4", + "sel4-dlmalloc", + "sel4-immediate-sync-once-cell", + "sel4-microkit-base", + "sel4-microkit-macros", + "sel4-panicking", + "sel4-panicking-env", + "sel4-runtime-common", +] + +[[package]] +name = "sel4-microkit-base" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "sel4", + "sel4-immutable-cell", +] + +[[package]] +name = "sel4-microkit-macros" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sel4-panicking" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "cfg-if", + "sel4-immediate-sync-once-cell", + "sel4-panicking-env", + "unwinding", +] + +[[package]] +name = "sel4-panicking-env" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" + +[[package]] +name = "sel4-runtime-common" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "cfg-if", + "sel4", + "sel4-ctors-dtors", + "sel4-elf-header", + "sel4-initialize-tls", + "sel4-panicking-env", + "sel4-stack", + "unwinding", +] + +[[package]] +name = "sel4-stack" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" + +[[package]] +name = "sel4-sys" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?rev=d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e#d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" +dependencies = [ + "bindgen", + "glob", + "log", + "pest", + "pest_derive", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "sel4-bitfield-ops", + "sel4-build-env", + "sel4-config", + "sel4-config-data", + "syn", + "xmltree", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unwinding" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60612c845ef41699f39dc8c5391f252942c0a88b7d15da672eff0d14101bbd6d" +dependencies = [ + "gimli", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "xml-rs" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" + +[[package]] +name = "xmltree" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b619f8c85654798007fb10afa5125590b43b088c225a25fc2fec100a9fad0fc6" +dependencies = [ + "xml-rs", +] diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/sdmmc/Cargo.toml index 789a0bfe4..3b88742b0 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/sdmmc/Cargo.toml @@ -1,3 +1,6 @@ +# Copyright 2025, UNSW +# SPDX-License-Identifier: BSD-2-Clause + [package] name = "blk_driver" version = "0.1.0" diff --git a/drivers/blk/sdmmc/blk_driver.mk b/drivers/blk/sdmmc/blk_driver.mk index aeebfd92c..0ff092b42 100644 --- a/drivers/blk/sdmmc/blk_driver.mk +++ b/drivers/blk/sdmmc/blk_driver.mk @@ -1,3 +1,9 @@ +# +# Copyright 2024, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# + # Get current dir SDMMC_DRIVER_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) diff --git a/drivers/blk/sdmmc/build.rs b/drivers/blk/sdmmc/build.rs index 6dba70af3..aa8259f65 100644 --- a/drivers/blk/sdmmc/build.rs +++ b/drivers/blk/sdmmc/build.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + fn main() { // Specify the path where the C library is located println!("cargo:rustc-link-search=native=./"); diff --git a/drivers/blk/sdmmc/libsddfblk.a b/drivers/blk/sdmmc/libsddfblk.a deleted file mode 100644 index 935cd539c7233c86463e804bd08b419e1e88a7c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15714 zcmeI34RD;rdB^wt+Pjm^vQDzBldO-kEn!QxC7msd1jL+e$+kW)vMgg82&a?oBpp8K z#NEk3#4#G;;P8>gVQ8R)VlzN$5~pzjEisb@cS_1fDYz|PDRk=4mWI$$6JTl+0)3u+ zpS^pvk}}Ms%}i&!Gw-|m{Pw@Q&+hxRw<|5aG?E$|S(n!sm>!xOrk!@41A)~ot4YzH z#~90JOuP289s`Lz;p-=26R~i7ES?FE#1iAN6dO#A4aJ99HdCacvAyxZSU43MPKWnK zqR|w~l5}z+H5f}fWum%hBoiSm)Ubv>oPW9*A)ZV>G*EAShJ80~2ma`;f zY|DkYbVwvatQ?dc#tzkK>>KkS{vO2c&{S`|Cv$fP4yo0 zu@lR`!Y1o)Ws`wAcCOyiT2C6`SE>WX%kpqTJq*f7{#k4{=-suUy2~{8p3|j=%Vk-t z6U)27FAr)wp%Zm=p}G#u(>0qG^>B+F+5`GH==XqrCFr{~Z`WZhe@}$5z5w(C>+kmb zO}V>*6`eM!>u-M~f6*Pn(Od`egePI_H6U z!eNCfR(PH(k3XEZ$W!9Ey?&k;%M68t-XV_UZ>bOPJHjQ3rl_G`r2*RXd+LKLY1R;QR=jAA$2D@cSQuI7})m=4QpvbT=HmaO{Eu zrgJO`2Ykoam*MyU94Fy;5sp{k_&FRVlr4s12^>KkNJUiRx=;edJdaswFA1=gBfSEdE@Npcc-S<(eJ|vv!-+f zP3gyUZI`Xqwt)i7ZDjV^W-l{^3L3i$)W#Rxhnaa?e+F7HpV6P!?Pj0Xrwjm7#t}mR zlsttJJA=$^U>-5B1(Dlpo6v$Vx7+4+GW%?^kJ&MJd0PKiHzo@YGtH;f))revZK|}e zbWyEuQ4vpIRBD--4BDgkaZLrwP?cCD%OnJ ze9JcYJRwD+1@c@D(_+l6X&rE3J&U3EMUb6`ZRF>#gJgcTO3-;lmHBp+^X9{)Etn5Q z7@LnJ87nlQ9xQ1qe9NJl3Xl~wfGfzBl?4{VMK87>18pp{ph3e9hWs~{Nzr9xjFnhS z_vcg#o0+~OXO>oK!QvfDcjfQS-w9s&LA)dgm6bKH>6PK|Fjk&(prcs0ut;sy7FJZU zU@_JpYp%pGmabN*g_f1s#j3IWoGyUQqC4!@FlrXlVAcpfdst;|YOWhcU?7}9zsjZQ zyIrPXtOIOv{Q+ifg47~yiDhizS_L=12&Puaxa;efsIqvTNK|v4e;<;7+g17rCKkIn zb0371=XN-ObCz4n<8D{}J;?MtO?SIB_faVb!7T~th1x=IiC-(`-ev1yo?vTjfr_~HqU$yQib|}KHufe$MgcdUUTO`AB79hLJ_3P zwQ_Hnrun&FI1X3Fs8|WybmiAy(@-w%fA(I3#wZ$s*G z-sSD~^Ub{aX|VVf#seuG5*=>d^$C*BLRRoR7I_IypW~nNURuaoc%8^=WqhSbTx{}H zd06ZcH*YM!#HBE591<(^zt-zrrrw}y?lQ3F<0WC{Vn|-X8@v|>_;TLVhb8J5zX%7E z*SmR(NUSL2t=Jj95{68)v#Nrb*MN8}haN+Gp~;JULFQYn`PS&ZW`h-c0jPNsoH}`% z7djClSJz8dH<-Nf2;}pPZsz_jB*>#jA-RQL=-uq)LB8%KRIFxvlW2d9N$oeob$A1^ z+Ig*Lzs=<3zRQ>|r1{qCz72+Nqsa=eH$_|dj^Zo$j%ov*_a1&F4sP)_z7xl`xQp+? z-mrrE;k>|TgQS+c+RQJ}db-Sf?D|5p$}E_Ir>FEID8NjwasQb-Yp*{&}cL3EFfVz*rvdk29zasj*j=zfISiFh9hQnCw z=Rd=KS6<2ASOL1qo&5DCh?%~_G7IpU72rjv@?0a@h?sTQMzyuhPLycd#>_gbs6^}a zo8k^EeHR?i%oQF#{{&Kg7l-=%?gBUmJPYxhR|V$^^wWbK<^SY?LD1f7q{?_{te?+{?RWn_VbVa3u18mHy>cUlYe{;Vpf1J2=NvuK)(vGcLn&I z6yW_`l{XIAT5`Y)ijL$rnZa{#O`#(fnL%s_I?`eWv8fVmd#f45#!9q4zZt~lO7Dh~ zZZ>=T-amnqf8O`;T<|!a=Rb+(!l&UJ@V$uVqF3Q8E?*vAasCN(wBReyocT34jk*Hf z#4eYQ5B?`ut|G>xcwt=&LOdoC3)k}@?1Rg{fe(vBc^e-QiA9@uTqG*D@aybEC*NZy zx_KAgR<7zE#~U}q3b4a$EgU#&g289#6Mhnw!eP1>m^DrIL(fli?3me0u<7`Xv0TAKa%`Ak!L`j$(iY;HsaT#>ZBWFMVX**Q{m*$P&$U)PY&)0!@V^i7W3f&k!58} zeaQ{5tj=CH$LkQuOr*($?LFZux^n6E-rn$zt=oIrH%@0ATd!Q--O(2AS>L<Gswu!fowWc5G}Xhv4c`i?$2{%I;S;WevgpcY3n&ixwfaPAS6+cOTD+j6mA8?$EpUUy?+sdywI zZ_yMyOyT5MViLwU5}jmecy8ft7B4sZUKUCFszhVsnGrTPnTU@?@g0CqB5pmwEpP8Y zO?!o4Pn{rlKQWyD;k@DhEPTdK6HLy*g-;=v;ElGg%3Dn?A$Nv0GE|PU9t^)L$Ehm@ zwVoOq&)_(M7*st@NHJUvb3Yv6zlXG@X}>O*hY?J=7#xrglYJD5U})9)e5=a7Adl|6 z?588Q-+5}^H(hHsK5)?vRA}yhKjMAeqxUboKCjVyy84e1>i=jgdhu7M{_>Tp9%)z{q{^(E zb~87=ebddD&kb*<)yF%n;ZAEk?-u#bvrfx*0CKC1M{nx1s=BO-E~~7|(z>kHybmpl z-)yz=yjw(KH6@_ne4b}K$8NUDA-}m3id)=x0$PEbJmZ(_3F}h+gte&C3Upd)Df?cT z{TBO`BiqA`zk?sH#)JFOM~xeeolwg%<53OEU0^)BA2N3c#*H^M@D@5SpZ8ck$0qFYkn6NH=f>)QgEWxb`GllrUo_KEURzD{FTOYP}4GS-iNYotEj^&?&`Mk zH(TZ1R$(_c&VH%W@^a&#-f0zgSe|Z26Mwq@3EhM{X0BP>EHkk=q^xlwooY^PHif~m4?;G3oj^1B8iC-4 zq!N}3KJyn@rQlpq+or)>GkB5E&*UgHW_jbggns6iKo=Z-;m#5a{6h;R{03ibvv8z_ z_-%@*TsSNA^6j1Dn1V&7=>V+vg?=Wt_48-wTZI2J`6|JEGxT;n`1^T!SR(W@IjRg+ zGF=Q-snE~lwjS2kmEaZnnfz;@D{|ad^85DyjDyed%RNN(9ThxxU&-&88#va}LpnL) z5wLwMcVBIxWEJWA6@4pl`F)f1JP10he~R?7-Is|UAznlA8RDnNmfz8z5O0EZFvxan z3syFOSAZe!RN2!Kh0A=@SIWW=OMg-CI-d? z^$@Qlz2wp_9k}ytBcD*@_|!|Ce6o-`pBg!DL|)|3<7yZNS>QaElOVQ90Xtlog}#<0bXvfM|! zTjAxzdlX(zyiehm65plpHsbOOw_$h@yN&F>+|1I&?6#hE#Hx&L3@zV-FOZ;CI?lNrf3xzKr z{vQfoO#F<(ml6L!;f=)4D!i5W#|qy}9DiRClxzF(z&9r2K&-$q=%ccs3c_!dP!KwQo%r2abMa$X^Mns}d5&q3n56#gyZ*C_lL z@qUFLCmvP!pAjEX_zB_(g+EJtT;XpJ&nWy|;x{PVq@RrY6kbkzO5qn0KcMh!#J{TW z1o1l*evtSfg&!tELV6B z*-Z-XC%Z-A<79^vK1KExg&!eXwuAnoKc|TAqFC}K8W-6|e17HJ9;E?APbO=ogsNBl zmwt55p_lp6pMKIs=g^N3m-=zC_syZ7QuNZkYYzQAiv9@M&UuS-e2y#nQ)E9yu{u7_ zD0(^mOB{OmbE#~&K;bLMu2=X*vhgnwFkrh_Ph41xbx|z&!({g>{HJ73DEtMoZ&UcY zWXs=AvVQz4Fbt2`QP!W8#Gh4oJMp)O%j-)fV-3;Rz{GGkGBA*e?PU$ASR&GpiQSN4 z4Y*htgKaHRq{fC5@pL9FZP=9|Ta>Y@NyApNy;BLgfwU0Q{}##rB%~s&u=wv(P9V0w8^)5ESi|P6 zJ66Ho=-?jKfJ@e5#~%Jr07edX{luRyIJc}tm!{PP8)dO+U?>$Cjj`!}D~Ry?)f=^{L>#nXj&3O3_eoPujGDNe!Fm=vdAP{_ooiPhZD(3HKCey7C0 zzr7t+Uz%#OU*rVl*5<)oy`2m7)AKD*u#{shDgdXlL{P4EaBjhqc+?9xueI z|0!Ttr}{YFM@#z`D-VWjU&dEKLZ+dbY8uD6*HU>W!?db>W{&dxRQ@%k0_nfRxK1@&|NE&! zQ`8{#A1{e4FYmv{=V-rWk=?>JdX8m#RGl>059etAbkLRr9PQ&gOKty4z)+%&|E)9` zm+OLdZMpH2e&8Q-8r`2JA%9A0L`?5*$>?^PG5+sBd8Lb@$eAj; SdmmcProtocol { pub fn test_read_one_block(&mut self, start_idx: u64, destination: u64) { let data: MmcData = MmcData { - blocksize: 512, + blocksize: SDCARD_DEFAULT_SECTOR_SIZE, blockcnt: 1, flags: MmcDataFlag::SdmmcDataRead, addr: destination, @@ -1148,7 +1153,7 @@ impl SdmmcProtocol { // TODO: Figure out a way to support cards with 4 KB sector size let data: MmcData = MmcData { - blocksize: 512, + blocksize: SDCARD_DEFAULT_SECTOR_SIZE, blockcnt, flags: MmcDataFlag::SdmmcDataRead, addr: destination, @@ -1253,7 +1258,7 @@ impl SdmmcProtocol { let res: Result<(), SdmmcError>; // TODO: Figure out a way to support cards with 4 KB sector size let data: MmcData = MmcData { - blocksize: 512, + blocksize: SDCARD_DEFAULT_SECTOR_SIZE, blockcnt, flags: MmcDataFlag::SdmmcDataWrite, addr: source, diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs index f8e90e52a..ff0918c5a 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + use super::sdcard::{EMmc, Sdcard}; // Enums for bus_width diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs index bb798628d..c50698003 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + use core::sync::atomic::Ordering; use crate::{sdmmc_os::Sleep, sdmmc_traits::SdmmcHardware}; diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs index 61fb467a0..8ac73d1ff 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + use crate::{dev_log, info, sdmmc::mmc_struct::CardInfo}; use super::{ diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs index 3eddc8c13..c19565642 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + use bitflags::bitflags; // Linux protocol layer use two u32 to represent all capabilities diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs index a7aa60393..7cce6c3f8 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + #![allow(dead_code)] // Allow dead code for the entire module // Define constants for MMC data flags @@ -90,4 +93,4 @@ pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104: u8 = pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50: u8 = 1 << SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_DDR50; -pub const SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE: usize = 16; +pub const SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE: usize = 16; \ No newline at end of file diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs index 06cee1794..5172bdac0 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + use crate::sdmmc::MmcPowerMode; use crate::sdmmc::MmcSignalVoltage; use crate::sdmmc::SdmmcError; diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs index 02fe373ba..6417d0c2b 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs +++ b/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + use crate::{ dev_log, sdmmc::{ diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/sdmmc/src/main.rs index 9f34f8c61..04b4eafe9 100644 --- a/drivers/blk/sdmmc/src/main.rs +++ b/drivers/blk/sdmmc/src/main.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + #![no_std] // Don't link the standard library #![no_main] // Don't use the default entry point #![feature(used_with_arg)] @@ -28,7 +31,7 @@ const TIMER: TimerOps = TimerOps::new(); const SERIAL: SerialOps = SerialOps::new(); use sdmmc_protocol::{ - sdmmc::{mmc_struct::CardInfo, HostInfo}, + sdmmc::{mmc_struct::CardInfo, HostInfo, SDCARD_DEFAULT_SECTOR_SIZE}, sdmmc_traits::SdmmcHardware, }; use sdmmc_protocol::{ @@ -39,9 +42,8 @@ use sel4_microkit::{ debug_print, debug_println, protection_domain, ChannelSet, Handler, Infallible, }; -const SDCARD_SECTOR_SIZE: u32 = 512; const SDDF_TRANSFER_SIZE: u32 = 4096; -const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_SECTOR_SIZE; +const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_DEFAULT_SECTOR_SIZE; // Debug function for printing out content in one block #[allow(dead_code)] @@ -280,7 +282,7 @@ where // Check if the request is valid if (request.count as u64 + request.block_number as u64) - * SDCARD_SECTOR_SIZE as u64 + * SDCARD_DEFAULT_SECTOR_SIZE as u64 > self.card_info.card_capacity { unsafe { @@ -321,7 +323,7 @@ where request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 - * SDCARD_SECTOR_SIZE as u64, + * SDCARD_DEFAULT_SECTOR_SIZE as u64, ))); } else { panic!("SDMMC_DRIVER: The sdmmc should be here since the future should be empty!!!") @@ -337,7 +339,7 @@ where request.block_number as u64 + request.success_count as u64, request.io_or_offset + request.success_count as u64 - * SDCARD_SECTOR_SIZE as u64, + * SDCARD_DEFAULT_SECTOR_SIZE as u64, ))); } else { panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/sdmmc/src/sddf_blk/mod.rs index 5a4ae3fb7..cc1163fe3 100644 --- a/drivers/blk/sdmmc/src/sddf_blk/mod.rs +++ b/drivers/blk/sdmmc/src/sddf_blk/mod.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + unsafe extern "C" { pub unsafe fn blk_queue_init_helper(capacity: u64); diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/sdmmc/src/sddf_helper.c index 89c85775a..51505cd2a 100644 --- a/drivers/blk/sdmmc/src/sddf_helper.c +++ b/drivers/blk/sdmmc/src/sddf_helper.c @@ -1,3 +1,8 @@ +/* + * Copyright 2025, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ + #include #include #include diff --git a/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs b/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs index 35383355c..49d45be4f 100644 --- a/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs +++ b/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + use sdmmc_protocol::sdmmc_os::{Log, Sleep}; use sel4_panicking_env::__debug_print_macro_helper; diff --git a/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs b/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs index ded89424e..17321e2b5 100644 --- a/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs +++ b/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs @@ -1,3 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + use core::ptr; use sdmmc_protocol::{ diff --git a/include/sddf_rust/timer/Cargo.toml b/include/sddf_rust/timer/Cargo.toml deleted file mode 100644 index f2cc7a4ae..000000000 --- a/include/sddf_rust/timer/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "sddf_timer" -version = "0.1.0" -edition = "2024" -authors = ["Cheng Li 李澄 "] - -[lib] -name = "sddf_timer" -path = "lib.rs" - -[dependencies] -sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" } -# sel4-microkit-message = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" } \ No newline at end of file diff --git a/include/sddf_rust/timer/lib.rs b/include/sddf_rust/timer/lib.rs deleted file mode 100644 index 471cc85a6..000000000 --- a/include/sddf_rust/timer/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![no_std] // Don't link the standard library - -pub mod timer; diff --git a/include/sddf_rust/timer/timer.rs b/include/sddf_rust/timer/timer.rs deleted file mode 100644 index cd7f36df7..000000000 --- a/include/sddf_rust/timer/timer.rs +++ /dev/null @@ -1,30 +0,0 @@ -use sel4_microkit::MessageInfo; - -enum SddfTimer { - GetTime = 0, - SetTimeout = 1, -} - -pub struct Timer { - channel: sel4_microkit::Channel, -} - -impl Timer { - pub const fn new(server_channel: sel4_microkit::Channel) -> Self { - Timer { - channel: server_channel, - } - } - - pub fn set_timeout(&self, timeout: u64) { - sel4_microkit::set_mr(0, timeout); - let msg_info: MessageInfo = MessageInfo::new(SddfTimer::SetTimeout as u64, 1); - self.channel.pp_call(msg_info); - } - - pub fn time_now(&self) -> u64 { - let msg_info: MessageInfo = MessageInfo::new(SddfTimer::GetTime as u64, 0); - self.channel.pp_call(msg_info); - sel4_microkit::get_mr(0) - } -} From 2933aface784e3707163e3c7d6eb2494f8531fb2 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Wed, 6 Aug 2025 15:20:14 +1000 Subject: [PATCH 35/48] ci: add odroid-c4 for blk example Signed-off-by: Ivan Velickovic --- ci/matrix.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ci/matrix.py b/ci/matrix.py index e7e25700e..70e9dd2ea 100644 --- a/ci/matrix.py +++ b/ci/matrix.py @@ -29,8 +29,18 @@ "blk": { "configs": ["debug", "release"], "build_systems": ["make", "zig"], - "boards_build": ["maaxboard", "qemu_virt_aarch64", "qemu_virt_riscv64"], - "boards_test": ["maaxboard", "qemu_virt_aarch64", "qemu_virt_riscv64"], + "boards_build": [ + "maaxboard", + "qemu_virt_aarch64", + "qemu_virt_riscv64", + "odroidc4" + ], + "boards_test": [ + "maaxboard", + "qemu_virt_aarch64", + "qemu_virt_riscv64", + "odroidc4" + ], }, "i2c": { "configs": ["debug", "release"], From 042b81b257827c85d2b74b7e7c422cd54f04ac91 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Wed, 6 Aug 2025 15:27:12 +1000 Subject: [PATCH 36/48] ci: matrix.py style fix Signed-off-by: Ivan Velickovic --- ci/matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/matrix.py b/ci/matrix.py index 70e9dd2ea..43ae34726 100644 --- a/ci/matrix.py +++ b/ci/matrix.py @@ -33,13 +33,13 @@ "maaxboard", "qemu_virt_aarch64", "qemu_virt_riscv64", - "odroidc4" + "odroidc4", ], "boards_test": [ "maaxboard", "qemu_virt_aarch64", "qemu_virt_riscv64", - "odroidc4" + "odroidc4", ], }, "i2c": { From f06c8555d2217c44a5e8f3dbfd9acdba30d2f551 Mon Sep 17 00:00:00 2001 From: Cheng Date: Thu, 7 Aug 2025 14:38:58 +1000 Subject: [PATCH 37/48] Disable deadcode warning for a file Signed-off-by: Cheng --- drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs | 2 ++ drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs index dae3b0bc6..4948190b2 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs +++ b/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs @@ -1,6 +1,8 @@ // Copyright 2025, UNSW // SPDX-License-Identifier: BSD-2-Clause +#![allow(dead_code)] + use core::ptr; use sdmmc_protocol::{ diff --git a/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml b/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml index 334f91dfc..f6845ba66 100644 --- a/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml +++ b/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml @@ -16,5 +16,5 @@ bitflags = "2.6.0" [features] default = [] -# A feature specifically for enabling developer-level logging. The driver will always try to log errors +# A feature specifically for enabling developer-level logging. The driver will always try to log errors. dev-logs = [] \ No newline at end of file From 69c8b0de990147880d0382f9be2df5972cfe956c Mon Sep 17 00:00:00 2001 From: Cheng Date: Thu, 7 Aug 2025 15:23:27 +1000 Subject: [PATCH 38/48] Change the sdmmc driver's folder structure and building process Signed-off-by: Cheng --- .reuse/dep5 | 5 ++--- drivers/blk/{sdmmc => mmc/core}/Cargo.lock | 0 drivers/blk/{sdmmc => mmc/core}/Cargo.toml | 4 ++-- drivers/blk/{sdmmc => mmc/core}/README.md | 0 drivers/blk/{sdmmc => mmc/core}/blk_driver.mk | 14 +++++++------- drivers/blk/{sdmmc => mmc/core}/config.json | 0 .../core/protocol}/Cargo.toml | 0 .../sdmmc_protocol => mmc/core/protocol}/lib.rs | 0 .../sdmmc_protocol => mmc/core/protocol}/sdmmc.rs | 0 .../core/protocol}/sdmmc/mmc_struct.rs | 0 .../core/protocol}/sdmmc/sd_ops.rs | 0 .../core/protocol}/sdmmc/sdcard.rs | 0 .../core/protocol}/sdmmc/sdmmc_capability.rs | 0 .../core/protocol}/sdmmc/sdmmc_constant.rs | 0 .../core/protocol}/sdmmc_os.rs | 0 .../core/protocol}/sdmmc_traits.rs | 0 .../blk/{sdmmc => mmc/core}/rust-toolchain.toml | 0 drivers/blk/{sdmmc => mmc/core}/src/main.rs | 0 .../blk/{sdmmc => mmc/core}/src/sddf_blk/mod.rs | 0 drivers/blk/{sdmmc => mmc/core}/src/sddf_helper.c | 0 .../core}/src/sel4_microkit_os/mod.rs | 0 .../core}/src/sel4_microkit_os/odroidc4.rs | 0 .../targets/aarch64-sel4-microkit-minimal.json | 0 .../blk/{sdmmc/sdmmc_hal => mmc}/meson/Cargo.toml | 2 +- drivers/blk/{sdmmc/sdmmc_hal => mmc}/meson/lib.rs | 0 .../{sdmmc/sdmmc_hal => mmc}/meson/meson_gx_mmc.rs | 0 drivers/blk/sdmmc/build.rs | 10 ---------- examples/blk/blk.mk | 2 +- 28 files changed, 13 insertions(+), 24 deletions(-) rename drivers/blk/{sdmmc => mmc/core}/Cargo.lock (100%) rename drivers/blk/{sdmmc => mmc/core}/Cargo.toml (81%) rename drivers/blk/{sdmmc => mmc/core}/README.md (100%) rename drivers/blk/{sdmmc => mmc/core}/blk_driver.mk (70%) rename drivers/blk/{sdmmc => mmc/core}/config.json (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/Cargo.toml (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/lib.rs (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/sdmmc.rs (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/sdmmc/mmc_struct.rs (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/sdmmc/sd_ops.rs (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/sdmmc/sdcard.rs (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/sdmmc/sdmmc_capability.rs (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/sdmmc/sdmmc_constant.rs (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/sdmmc_os.rs (100%) rename drivers/blk/{sdmmc/sdmmc_protocol => mmc/core/protocol}/sdmmc_traits.rs (100%) rename drivers/blk/{sdmmc => mmc/core}/rust-toolchain.toml (100%) rename drivers/blk/{sdmmc => mmc/core}/src/main.rs (100%) rename drivers/blk/{sdmmc => mmc/core}/src/sddf_blk/mod.rs (100%) rename drivers/blk/{sdmmc => mmc/core}/src/sddf_helper.c (100%) rename drivers/blk/{sdmmc => mmc/core}/src/sel4_microkit_os/mod.rs (100%) rename drivers/blk/{sdmmc => mmc/core}/src/sel4_microkit_os/odroidc4.rs (100%) rename drivers/blk/{sdmmc => mmc/core}/support/targets/aarch64-sel4-microkit-minimal.json (100%) rename drivers/blk/{sdmmc/sdmmc_hal => mmc}/meson/Cargo.toml (90%) rename drivers/blk/{sdmmc/sdmmc_hal => mmc}/meson/lib.rs (100%) rename drivers/blk/{sdmmc/sdmmc_hal => mmc}/meson/meson_gx_mmc.rs (100%) delete mode 100644 drivers/blk/sdmmc/build.rs diff --git a/.reuse/dep5 b/.reuse/dep5 index 7cdf82ec0..20b34aef6 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -35,9 +35,8 @@ Files: drivers/timer/meson/config.json drivers/timer/goldfish/config.json drivers/timer/cdns/config.json - drivers/timer/bcm2835/config.json - drivers/blk/sdmmc/config.json - drivers/blk/sdmmc/README.md + drivers/blk/mmc/core/config.json + drivers/blk/mmc/core/README.md Copyright: UNSW License: BSD-2-Clause diff --git a/drivers/blk/sdmmc/Cargo.lock b/drivers/blk/mmc/core/Cargo.lock similarity index 100% rename from drivers/blk/sdmmc/Cargo.lock rename to drivers/blk/mmc/core/Cargo.lock diff --git a/drivers/blk/sdmmc/Cargo.toml b/drivers/blk/mmc/core/Cargo.toml similarity index 81% rename from drivers/blk/sdmmc/Cargo.toml rename to drivers/blk/mmc/core/Cargo.toml index 3b88742b0..fdbbd30a8 100644 --- a/drivers/blk/sdmmc/Cargo.toml +++ b/drivers/blk/mmc/core/Cargo.toml @@ -9,8 +9,8 @@ edition = "2024" [dependencies] sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e", features = ["alloc"] } sel4-panicking-env = { git = "https://github.com/seL4/rust-sel4.git", rev = "d2bc5cf71c5455f85898a4768e9dcbaea39e1a7e" } -meson_hal = { path = "sdmmc_hal/meson", optional = true } -sdmmc_protocol = { path = "sdmmc_protocol" } +meson_hal = { path = "../meson", optional = true } +sdmmc_protocol = { path = "protocol" } [features] meson = ["dep:meson_hal"] diff --git a/drivers/blk/sdmmc/README.md b/drivers/blk/mmc/core/README.md similarity index 100% rename from drivers/blk/sdmmc/README.md rename to drivers/blk/mmc/core/README.md diff --git a/drivers/blk/sdmmc/blk_driver.mk b/drivers/blk/mmc/core/blk_driver.mk similarity index 70% rename from drivers/blk/sdmmc/blk_driver.mk rename to drivers/blk/mmc/core/blk_driver.mk index 0ff092b42..f7053acbe 100644 --- a/drivers/blk/sdmmc/blk_driver.mk +++ b/drivers/blk/mmc/core/blk_driver.mk @@ -16,28 +16,28 @@ $(BUILD_DIR): @echo "Creating build directory $(BUILD_DIR)..." mkdir -p $@ -blk/sdmmc/meson/sddf_helper.o: $(SDMMC_DRIVER_DIR)/src/sddf_helper.c |blk/sdmmc/meson +blk/mmc/meson/sddf_helper.o: $(SDMMC_DRIVER_DIR)/src/sddf_helper.c |blk/mmc/meson $(CC) -c $(CFLAGS) $< -o $@ -blk/sdmmc/meson/libsddfblk.a: blk/sdmmc/meson/sddf_helper.o |blk/sdmmc/meson +blk/mmc/meson/libsddfblk.a: blk/mmc/meson/sddf_helper.o |blk/mmc/meson ${AR} rcs $@ $< -blk/sdmmc/meson: +blk/mmc/meson: mkdir -p $@ # Main build target -blk_driver.elf: $(BUILD_DIR) blk/sdmmc/meson/libsddfblk.a - cp blk/sdmmc/meson/libsddfblk.a $(abspath ${SDMMC_DRIVER_DIR}) +blk_driver.elf: $(BUILD_DIR) blk/mmc/meson/libsddfblk.a @cd $(abspath ${SDMMC_DRIVER_DIR}) && \ echo "Building blk_driver.elf for board $(MICROKIT_BOARD)..." && \ echo "MICROKIT SDK config directory: $(microkit_sdk_config_dir)" && \ echo "SEl4 include directories: $(sel4_include_dirs)" && \ SEL4_INCLUDE_DIRS=$(abspath $(sel4_include_dirs)) \ + RUSTFLAGS="-L $(BUILD_DIR)/blk/mmc/meson/ -l static=sddfblk" \ cargo build \ -Z build-std=core,alloc,compiler_builtins \ -Z build-std-features=compiler-builtins-mem \ - --target-dir $(BUILD_DIR)/blk/sdmmc/meson/ \ + --target-dir $(BUILD_DIR)/blk/mmc/meson/ \ --target support/targets/aarch64-sel4-microkit-minimal.json \ --features "meson" - cp $(BUILD_DIR)/blk/sdmmc/meson/aarch64-sel4-microkit-minimal/debug/blk_driver.elf $(BUILD_DIR) + cp $(BUILD_DIR)/blk/mmc/meson/aarch64-sel4-microkit-minimal/debug/blk_driver.elf $(BUILD_DIR) echo "Build complete: $(TARGET_ELF)" \ No newline at end of file diff --git a/drivers/blk/sdmmc/config.json b/drivers/blk/mmc/core/config.json similarity index 100% rename from drivers/blk/sdmmc/config.json rename to drivers/blk/mmc/core/config.json diff --git a/drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml b/drivers/blk/mmc/core/protocol/Cargo.toml similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/Cargo.toml rename to drivers/blk/mmc/core/protocol/Cargo.toml diff --git a/drivers/blk/sdmmc/sdmmc_protocol/lib.rs b/drivers/blk/mmc/core/protocol/lib.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/lib.rs rename to drivers/blk/mmc/core/protocol/lib.rs diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs b/drivers/blk/mmc/core/protocol/sdmmc.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/sdmmc.rs rename to drivers/blk/mmc/core/protocol/sdmmc.rs diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs b/drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/sdmmc/mmc_struct.rs rename to drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs b/drivers/blk/mmc/core/protocol/sdmmc/sd_ops.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sd_ops.rs rename to drivers/blk/mmc/core/protocol/sdmmc/sd_ops.rs diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs b/drivers/blk/mmc/core/protocol/sdmmc/sdcard.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdcard.rs rename to drivers/blk/mmc/core/protocol/sdmmc/sdcard.rs diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs b/drivers/blk/mmc/core/protocol/sdmmc/sdmmc_capability.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_capability.rs rename to drivers/blk/mmc/core/protocol/sdmmc/sdmmc_capability.rs diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs b/drivers/blk/mmc/core/protocol/sdmmc/sdmmc_constant.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/sdmmc/sdmmc_constant.rs rename to drivers/blk/mmc/core/protocol/sdmmc/sdmmc_constant.rs diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs b/drivers/blk/mmc/core/protocol/sdmmc_os.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/sdmmc_os.rs rename to drivers/blk/mmc/core/protocol/sdmmc_os.rs diff --git a/drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs b/drivers/blk/mmc/core/protocol/sdmmc_traits.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_protocol/sdmmc_traits.rs rename to drivers/blk/mmc/core/protocol/sdmmc_traits.rs diff --git a/drivers/blk/sdmmc/rust-toolchain.toml b/drivers/blk/mmc/core/rust-toolchain.toml similarity index 100% rename from drivers/blk/sdmmc/rust-toolchain.toml rename to drivers/blk/mmc/core/rust-toolchain.toml diff --git a/drivers/blk/sdmmc/src/main.rs b/drivers/blk/mmc/core/src/main.rs similarity index 100% rename from drivers/blk/sdmmc/src/main.rs rename to drivers/blk/mmc/core/src/main.rs diff --git a/drivers/blk/sdmmc/src/sddf_blk/mod.rs b/drivers/blk/mmc/core/src/sddf_blk/mod.rs similarity index 100% rename from drivers/blk/sdmmc/src/sddf_blk/mod.rs rename to drivers/blk/mmc/core/src/sddf_blk/mod.rs diff --git a/drivers/blk/sdmmc/src/sddf_helper.c b/drivers/blk/mmc/core/src/sddf_helper.c similarity index 100% rename from drivers/blk/sdmmc/src/sddf_helper.c rename to drivers/blk/mmc/core/src/sddf_helper.c diff --git a/drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs b/drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs similarity index 100% rename from drivers/blk/sdmmc/src/sel4_microkit_os/mod.rs rename to drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs diff --git a/drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs b/drivers/blk/mmc/core/src/sel4_microkit_os/odroidc4.rs similarity index 100% rename from drivers/blk/sdmmc/src/sel4_microkit_os/odroidc4.rs rename to drivers/blk/mmc/core/src/sel4_microkit_os/odroidc4.rs diff --git a/drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json b/drivers/blk/mmc/core/support/targets/aarch64-sel4-microkit-minimal.json similarity index 100% rename from drivers/blk/sdmmc/support/targets/aarch64-sel4-microkit-minimal.json rename to drivers/blk/mmc/core/support/targets/aarch64-sel4-microkit-minimal.json diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml b/drivers/blk/mmc/meson/Cargo.toml similarity index 90% rename from drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml rename to drivers/blk/mmc/meson/Cargo.toml index 62ec8dec3..b24715e63 100644 --- a/drivers/blk/sdmmc/sdmmc_hal/meson/Cargo.toml +++ b/drivers/blk/mmc/meson/Cargo.toml @@ -20,4 +20,4 @@ path = "lib.rs" # - sdmmc_protocol: Essential for core functionality. [dependencies] -sdmmc_protocol = { path = "../../sdmmc_protocol" } +sdmmc_protocol = { path = "../core/protocol" } diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs b/drivers/blk/mmc/meson/lib.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_hal/meson/lib.rs rename to drivers/blk/mmc/meson/lib.rs diff --git a/drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs b/drivers/blk/mmc/meson/meson_gx_mmc.rs similarity index 100% rename from drivers/blk/sdmmc/sdmmc_hal/meson/meson_gx_mmc.rs rename to drivers/blk/mmc/meson/meson_gx_mmc.rs diff --git a/drivers/blk/sdmmc/build.rs b/drivers/blk/sdmmc/build.rs deleted file mode 100644 index aa8259f65..000000000 --- a/drivers/blk/sdmmc/build.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2025, UNSW -// SPDX-License-Identifier: BSD-2-Clause - -fn main() { - // Specify the path where the C library is located - println!("cargo:rustc-link-search=native=./"); - - // Link the C library (static or dynamic). Adjust "static" or "dylib" as needed. - println!("cargo:rustc-link-lib=static=sddfblk"); -} diff --git a/examples/blk/blk.mk b/examples/blk/blk.mk index 940507cad..e977bbcd9 100644 --- a/examples/blk/blk.mk +++ b/examples/blk/blk.mk @@ -64,7 +64,7 @@ else ifeq ($(strip $(MICROKIT_BOARD)), odroidc4) ARCH := aarch64 CPU := cortex-a55 SERIAL_DRIVER_DIR := meson - BLK_DRIVER_DIR := sdmmc + BLK_DRIVER_DIR := mmc/core else $(error Unsupported MICROKIT_BOARD given) endif From 0ac19655ffa5b2a43cb089ddacd3b2ff2ee05d95 Mon Sep 17 00:00:00 2001 From: Cheng Date: Thu, 7 Aug 2025 22:13:39 +1000 Subject: [PATCH 39/48] Remove the old readme from the core folder to avoid confusion. A new readme on how to add support for a different platform will be added later. Signed-off-by: Cheng --- .reuse/dep5 | 1 - drivers/blk/mmc/core/README.md | 93 ---------------------------------- 2 files changed, 94 deletions(-) delete mode 100644 drivers/blk/mmc/core/README.md diff --git a/.reuse/dep5 b/.reuse/dep5 index 20b34aef6..a620176a4 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -36,7 +36,6 @@ Files: drivers/timer/goldfish/config.json drivers/timer/cdns/config.json drivers/blk/mmc/core/config.json - drivers/blk/mmc/core/README.md Copyright: UNSW License: BSD-2-Clause diff --git a/drivers/blk/mmc/core/README.md b/drivers/blk/mmc/core/README.md deleted file mode 100644 index e51234ea2..000000000 --- a/drivers/blk/mmc/core/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# Generic Rust-Based SDMMC Driver - -This repository contains a modular SDMMC driver written entirely in Rust, focusing on **async support**, **memory safety**, and **functional correctness**. While developed for LionsOS, the driver is designed to be **OS-agnostic** and extensible for other operating systems. - -I’m excited to share this driver as a unique contribution to the open-source community. It combines wide compatibility, high performance, and Rust’s safety benefits, making it a standout in its category. - ---- - -## Features - -- **Modular Design**: Inspired by U-Boot and Linux, separates device-specific logic from protocol implementation, ensuring flexibility and extensibility. -- **Wide Compatibility and High Performance**: - - Supports a range of SD cards, including **SDHC** and **SDXC**, with speed classes up to **UHS-I**. - - While Linux only added support for UHS-II in October 2024, this driver provides robust UHS-I support, ideal for embedded systems and platforms where UHS-II adoption remains limited. -- **Memory Safety**: Utilizes Rust's strict compile-time checks and ownership model, ensuring better safety and readability compared to traditional C-based drivers. -- **OS-Agnostic**: Designed to integrate seamlessly with LionsOS but easily portable to other operating systems. - ---- - -## Supported Hardware Platforms - -- **Odroid C4**: Currently, the driver supports and has been tested on the Odroid C4 platform. -- **Adding Support**: Thanks to the modular design, adding support for additional platforms is straightforward by implementing the hardware abstraction layer for the new platform. - ---- - -## Current Status - -The driver is currently in the **final stages of development for its first iteration**: -- Core functionality has been fully implemented. -- Performance is comparable to Linux's SDMMC subsystem, with support for speed classes up to **UHS-I**. -- Code refinements and documentation updates are ongoing. -- **Expected Milestone**: Public release and merging into LionsOS main branch by February 2025. - ---- - -## Driver Structure - -The driver is organized into the following components: - -1. **`sdmmc_hal` Folder**: - - Contains the hardware abstraction layer for different platforms. - - To add support for a new platform, implement the necessary hardware-specific interfaces here. - -2. **`sdmmc_protocol` Folder**: - - Implements the SD card protocol using the hardware abstraction layer. - - Modify this layer to add new protocol features (e.g., hotplugging or eMMC support). - -3. **`optional_os_support` Folder**: - - Provides OS-specific utilities such as optimized wait/sleep operations(instead of spinning waiting) or printing debug messages to the terminal. - - Modify this layer to provide support for a new OS - ---- - -## Usage and Examples - -Usage instructions and examples will be added soon. Stay tuned! - ---- - -## Why Rust? - -Rust provides unmatched safety and performance benefits for driver development: -- **Memory Safety**: Prevents common bugs like null pointer dereferences and buffer overflows. -- **Async Support**: Enables efficient, non-blocking I/O operations. -- **Readable and Extensible**: Rust’s modern syntax and tooling make the driver easier to understand and maintain compared to traditional C-based implementations. - ---- - -## Future Plans - -- Expand hardware support to additional platforms. -- Add support for legacy SDSC cards and the latest SDUC cards (though they may have limited use cases). -- Add hotplugging and eMMC support. -- Publish detailed usage examples and benchmarks. - ---- - -## Contributing - -Contributions are welcome! Feel free to open issues or submit pull requests to improve functionality, expand platform support, or enhance documentation. - ---- - -## License - -The license for this work will be added upon the first public release. The project is developed as part of my work at Trustworthy Systems, UNSW, so the copyright may reside with the university. Clarifications about licensing are ongoing. - ---- - -## Related Links - -- [LionsOS GitHub Repository](https://github.com/au-ts/lionsos) From 16d8f59f2ef89747a9071012077fb11f18597f9c Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 13 Aug 2025 15:04:14 +1000 Subject: [PATCH 40/48] Refine some comments and move debug prints to be enabled by a feature. Signed-off-by: Cheng --- drivers/blk/mmc/core/Cargo.toml | 1 + drivers/blk/mmc/core/protocol/sdmmc.rs | 12 +-- .../blk/mmc/core/protocol/sdmmc/mmc_struct.rs | 1 - drivers/blk/mmc/core/src/main.rs | 73 +++++++------------ .../blk/mmc/core/src/sel4_microkit_os/mod.rs | 2 +- 5 files changed, 31 insertions(+), 58 deletions(-) diff --git a/drivers/blk/mmc/core/Cargo.toml b/drivers/blk/mmc/core/Cargo.toml index fdbbd30a8..810c05d1a 100644 --- a/drivers/blk/mmc/core/Cargo.toml +++ b/drivers/blk/mmc/core/Cargo.toml @@ -13,6 +13,7 @@ meson_hal = { path = "../meson", optional = true } sdmmc_protocol = { path = "protocol" } [features] +dev_logs = ["sdmmc_protocol/dev-logs"] meson = ["dep:meson_hal"] [build] diff --git a/drivers/blk/mmc/core/protocol/sdmmc.rs b/drivers/blk/mmc/core/protocol/sdmmc.rs index 7d366b616..719b240e7 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc.rs @@ -268,7 +268,6 @@ pub struct HostInfo { pub power_delay_ms: u32, } -/// TODO: Add more variables for SdmmcProtocol to track the state of the sdmmc controller and card correctly pub struct SdmmcProtocol { hardware: T, @@ -424,7 +423,7 @@ impl SdmmcProtocol { && resp[0] & OCR_HCS == OCR_HCS && resp[0] & OCR_S18R == OCR_S18R { - // TODO: If the sdcard fail at this stage, a power circle and reinit should be performed + // If the sdcard fail at this stage, a power circle and reinit will be performed self.tune_sdcard_switch_uhs18v()?; self.mmc_ios.signal_voltage = MmcSignalVoltage::Voltage180; } @@ -453,7 +452,6 @@ impl SdmmcProtocol { // Disable all irqs here self.hardware.sdmmc_config_interrupt(false, false)?; - // TODO: Different sdcard and eMMC support different voltages, figure those out if self.mmc_ios.power_mode != MmcPowerMode::On { return Err(SdmmcError::EINVAL); } @@ -991,7 +989,6 @@ impl SdmmcProtocol { // If any of the cmd above fail, the card should be completely reinit self.mmc_ios.bus_width = MmcBusWidth::Width4; - // TODO: Change sdcard bus width here, or get rid of that field completely } dev_log!("Checking supported speed classes\n"); @@ -1151,7 +1148,6 @@ impl SdmmcProtocol { let mut res: Result<(), SdmmcError>; let mut turing: bool = false; - // TODO: Figure out a way to support cards with 4 KB sector size let data: MmcData = MmcData { blocksize: SDCARD_DEFAULT_SECTOR_SIZE, blockcnt, @@ -1171,7 +1167,6 @@ impl SdmmcProtocol { cmd.cmdarg = start * mmc->read_bl_len; */ // For now we default to assume the card is high_capacity - // TODO: Fix it when we properly implement card boot up // TODO: If we boot the card by ourself or reset the card, remember to send block len cmd loop { if blockcnt == 1 { @@ -1256,7 +1251,7 @@ impl SdmmcProtocol { let cmd: SdmmcCmd; let res: Result<(), SdmmcError>; - // TODO: Figure out a way to support cards with 4 KB sector size + let data: MmcData = MmcData { blocksize: SDCARD_DEFAULT_SECTOR_SIZE, blockcnt, @@ -1399,8 +1394,7 @@ impl SdmmcProtocol { let temp_res: Result<(), SdmmcError> = Self::sdmmc_async_request(hardware, &request_cmd, Some(&data), resp).await; - // Uboot code for determine response type in this case - // cmd.resp_type = (IS_SD(mmc) || write) ? MMC_RSP_R1b : MMC_RSP_R1; + // Dependent on whether the card is mmc or not, the response type is different // TODO: Add mmc checks here let cmd: SdmmcCmd = SdmmcCmd { cmdidx: MMC_CMD_STOP_TRANSMISSION, diff --git a/drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs b/drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs index ff0918c5a..ad9aeabdd 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs @@ -64,7 +64,6 @@ pub(crate) enum MmcDevice { Sdcard(Sdcard), EMmc(EMmc), Unknown, - // TODO, when we decide to support emmc/sdio, modify this struct } /// Represents the different states of an SD or eMMC card. diff --git a/drivers/blk/mmc/core/src/main.rs b/drivers/blk/mmc/core/src/main.rs index 04b4eafe9..20372b365 100644 --- a/drivers/blk/mmc/core/src/main.rs +++ b/drivers/blk/mmc/core/src/main.rs @@ -18,12 +18,12 @@ use core::{ use alloc::boxed::Box; use sddf_blk::{ - blk_dequeue_req_helper, blk_device_init_data_ioaddr, blk_device_init_data_vaddr, - blk_device_regs_vaddr, blk_enqueue_resp_helper, blk_queue_empty_req_helper, - blk_queue_full_resp_helper, blk_queue_init_helper, BlkOp, BlkRequest, BlkStatus, + BlkOp, BlkRequest, BlkStatus, blk_dequeue_req_helper, blk_device_init_data_ioaddr, + blk_device_init_data_vaddr, blk_device_regs_vaddr, blk_enqueue_resp_helper, + blk_queue_empty_req_helper, blk_queue_full_resp_helper, blk_queue_init_helper, }; -use crate::sel4_microkit_os::{platform::VOLTAGE, SerialOps, TimerOps}; +use crate::sel4_microkit_os::{SerialOps, TimerOps, platform::VOLTAGE}; const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(0); const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(1); @@ -31,36 +31,18 @@ const TIMER: TimerOps = TimerOps::new(); const SERIAL: SerialOps = SerialOps::new(); use sdmmc_protocol::{ - sdmmc::{mmc_struct::CardInfo, HostInfo, SDCARD_DEFAULT_SECTOR_SIZE}, + sdmmc::{HostInfo, SDCARD_DEFAULT_SECTOR_SIZE, mmc_struct::CardInfo}, sdmmc_traits::SdmmcHardware, }; use sdmmc_protocol::{ sdmmc::{SdmmcError, SdmmcProtocol}, sdmmc_os::{Sleep, VoltageOps}, }; -use sel4_microkit::{ - debug_print, debug_println, protection_domain, ChannelSet, Handler, Infallible, -}; +use sel4_microkit::{ChannelSet, Handler, Infallible, debug_println, protection_domain}; const SDDF_TRANSFER_SIZE: u32 = 4096; const SDDF_TO_REAL_SECTOR: u32 = SDDF_TRANSFER_SIZE / SDCARD_DEFAULT_SECTOR_SIZE; -// Debug function for printing out content in one block -#[allow(dead_code)] -unsafe fn print_one_block(ptr: *const u8, num: usize) { - unsafe { - // Iterate over the number of bytes and print each one in hexadecimal format - for i in 0..num { - let byte = *ptr.add(i); - if i % 16 == 0 { - debug_print!("\n{:04x}: ", i); - } - debug_print!("{:02x} ", byte); - } - debug_println!(); - } -} - // No-op waker implementations, they do nothing. unsafe fn noop(_data: *const ()) {} unsafe fn noop_clone(_data: *const ()) -> RawWaker { @@ -80,7 +62,7 @@ fn create_dummy_waker() -> Waker { unsafe { Waker::from_raw(raw_waker) } } -#[protection_domain(heap_size = 0x10000)] +#[protection_domain(heap_size = 0x1000)] fn init() -> impl Handler { unsafe { sdmmc_protocol::sdmmc_os::set_logger(&SERIAL).unwrap(); @@ -121,10 +103,7 @@ fn init() -> impl Handler { let _ = sdmmc_host.config_interrupt(false, false); - // Print out one block to check if read works - // sdmmc_host.test_read_one_block(0, 0xf5500000); - - // TODO: Should tuning be possible to fail? + // Should tuning be possible to fail? unsafe { sdmmc_host .tune_performance( @@ -135,10 +114,6 @@ fn init() -> impl Handler { .unwrap_or_else(|error| panic!("SDMMC: Error at tuning performance {:?}", error)); } - // unsafe { - // print_one_block(unsafe_stolen_memory.as_ptr(), 64); - // } - // Should always succeed, at least for odroid C4 let _ = sdmmc_host.config_interrupt(true, false); @@ -192,7 +167,6 @@ where let mut cx = Context::from_waker(&waker); match future.as_mut().poll(&mut cx) { Poll::Ready((result, sdmmc)) => { - // debug_println!("SDMMC_DRIVER: Future completed with result"); self.future = None; // Reset the future once done self.sdmmc = Some(sdmmc); if result.is_ok() { @@ -232,7 +206,6 @@ where } } Poll::Pending => { - // debug_println!("SDMMC_DRIVER: Future is not ready, polling again..."); // Since the future is not ready, no other request can be dequeued, exit the big loop break 'process_notification; } @@ -269,16 +242,18 @@ where &mut request.id as *mut u32, ); } - // TODO: Consider how to add integer overflow check here + // TODO: Consider to add integer overflow check here request.block_number = request.block_number * SDDF_TO_REAL_SECTOR as u64; request.count = (sddf_count as u32) * SDDF_TO_REAL_SECTOR; + // Print the retrieved values - /* - debug_println!("io_or_offset: 0x{:x}", request.io_or_offset);// Simple u64 - debug_println!("block_number: {}", request.block_number); // Simple u32 - debug_println!("count: {}", request.count); // Simple u16 - debug_println!("id: {}", request.id); // Simple u32 - */ + #[cfg(feature = "dev-logs")] + { + debug_println!("io_or_offset: 0x{:x}", request.io_or_offset); // Simple u64 + debug_println!("block_number: {}", request.block_number); // Simple u32 + debug_println!("count: {}", request.count); // Simple u16 + debug_println!("id: {}", request.id); // Simple u32 + } // Check if the request is valid if (request.count as u64 + request.block_number as u64) @@ -314,7 +289,6 @@ where if let None = self.future { match request.request_code { BlkOp::BlkReqRead => { - // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer request.count_to_do = core::cmp::min(request.count, self.host_info.max_block_per_req); if let Some(sdmmc) = self.sdmmc.take() { @@ -326,11 +300,12 @@ where * SDCARD_DEFAULT_SECTOR_SIZE as u64, ))); } else { - panic!("SDMMC_DRIVER: The sdmmc should be here since the future should be empty!!!") + panic!( + "SDMMC_DRIVER: The sdmmc should be here since the future should be empty!!!" + ) } } BlkOp::BlkReqWrite => { - // TODO: The MAX_BLOCK_PER_TRANSFER is got by hackily get the defines in hardware layer which is wrong, check that to get properly from protocol layer request.count_to_do = core::cmp::min(request.count, self.host_info.max_block_per_req); if let Some(sdmmc) = self.sdmmc.take() { @@ -342,7 +317,9 @@ where * SDCARD_DEFAULT_SECTOR_SIZE as u64, ))); } else { - panic!("SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!") + panic!( + "SDMMC_DRIVER: The sdmmc should be here and the future should be empty!!!" + ) } } _ => { @@ -355,7 +332,9 @@ where if let Some(ref mut future) = self.future { match future.as_mut().poll(&mut cx) { Poll::Ready(_) => { - panic!("SDMMC: RECEIVED INVALID REQUEST! Check request validation code!"); + panic!( + "SDMMC: RECEIVED INVALID REQUEST! Check request validation code!" + ); } Poll::Pending => break 'process_notification, } diff --git a/drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs b/drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs index 49d45be4f..1622af92b 100644 --- a/drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs +++ b/drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs @@ -9,7 +9,7 @@ mod odroidc4; #[cfg(feature = "meson")] pub(crate) mod platform { - pub(crate) use crate::sel4_microkit_os::odroidc4::{platform_hal, VOLTAGE}; + pub(crate) use crate::sel4_microkit_os::odroidc4::{VOLTAGE, platform_hal}; } const NS_IN_US: u64 = 1000; From 57cf3ca32b5463386ade3fc5d55df312172c7dd1 Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 13 Aug 2025 16:06:18 +1000 Subject: [PATCH 41/48] Refine the struct of the code Signed-off-by: Cheng --- drivers/blk/mmc/core/Cargo.toml | 2 +- drivers/blk/mmc/core/protocol/sdmmc.rs | 35 ++++--- .../{sdmmc_capability.rs => capability.rs} | 0 .../sdmmc/{sdmmc_constant.rs => constant.rs} | 0 .../blk/mmc/core/protocol/sdmmc/mmc_struct.rs | 2 +- .../core/protocol/sdmmc/{sdcard.rs => sd.rs} | 83 ++++++++++++++++- drivers/blk/mmc/core/protocol/sdmmc/sd_ops.rs | 91 ------------------- drivers/blk/mmc/core/src/sddf_blk/mod.rs | 2 + drivers/blk/mmc/meson/meson_gx_mmc.rs | 4 +- 9 files changed, 104 insertions(+), 115 deletions(-) rename drivers/blk/mmc/core/protocol/sdmmc/{sdmmc_capability.rs => capability.rs} (100%) rename drivers/blk/mmc/core/protocol/sdmmc/{sdmmc_constant.rs => constant.rs} (100%) rename drivers/blk/mmc/core/protocol/sdmmc/{sdcard.rs => sd.rs} (82%) delete mode 100644 drivers/blk/mmc/core/protocol/sdmmc/sd_ops.rs diff --git a/drivers/blk/mmc/core/Cargo.toml b/drivers/blk/mmc/core/Cargo.toml index 810c05d1a..a877869de 100644 --- a/drivers/blk/mmc/core/Cargo.toml +++ b/drivers/blk/mmc/core/Cargo.toml @@ -13,7 +13,7 @@ meson_hal = { path = "../meson", optional = true } sdmmc_protocol = { path = "protocol" } [features] -dev_logs = ["sdmmc_protocol/dev-logs"] +dev-logs = ["sdmmc_protocol/dev-logs"] meson = ["dep:meson_hal"] [build] diff --git a/drivers/blk/mmc/core/protocol/sdmmc.rs b/drivers/blk/mmc/core/protocol/sdmmc.rs index 719b240e7..8dfc80fd4 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc.rs @@ -2,11 +2,10 @@ // SPDX-License-Identifier: BSD-2-Clause pub mod mmc_struct; -pub mod sd_ops; -pub mod sdcard; -pub mod sdmmc_capability; +pub mod sd; +pub mod capability; -mod sdmmc_constant; +mod constant; use core::{ future::Future, @@ -16,13 +15,13 @@ use core::{ }; use mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcDevice, MmcState, MmcTiming}; -use sdcard::{Cid, Csd, Scr, Sdcard}; -use sdmmc_capability::{ +use sd::{Cid, Csd, Scr, Sdcard}; +use capability::{ MMC_CAP_4_BIT_DATA, MMC_EMPTY_CAP, MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS_DDR50, MMC_TIMING_UHS_SDR12, MMC_TIMING_UHS_SDR25, MMC_TIMING_UHS_SDR50, MMC_TIMING_UHS_SDR104, SdcardCapability, SdmmcHostCapability, }; -use sdmmc_constant::{ +use constant::{ MMC_CMD_ALL_SEND_CID, MMC_CMD_APP_CMD, MMC_CMD_ERASE, MMC_CMD_GO_IDLE_STATE, MMC_CMD_READ_MULTIPLE_BLOCK, MMC_CMD_READ_SINGLE_BLOCK, MMC_CMD_SELECT_CARD, MMC_CMD_SEND_CSD, MMC_CMD_SET_BLOCK_COUNT, MMC_CMD_STOP_TRANSMISSION, MMC_CMD_WRITE_MULTIPLE_BLOCK, @@ -389,7 +388,7 @@ impl SdmmcProtocol { if voltage_switch == true && self .host_capability - .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) + .contains(capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) { cmd.cmdarg |= OCR_S18R; // It seems that cards will not respond to commands that have MMC_VDD_165_195 bit set, even if the card supports UHS-I @@ -419,7 +418,7 @@ impl SdmmcProtocol { if voltage_switch == true && self .host_capability - .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) + .contains(capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) && resp[0] & OCR_HCS == OCR_HCS && resp[0] & OCR_S18R == OCR_S18R { @@ -471,7 +470,7 @@ impl SdmmcProtocol { let res: Result<(), SdmmcError> = 'sdcard_init: { let mut voltage_switch_init: bool = self .host_capability - .contains(sdmmc_capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)); + .contains(capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)); let mut init_error: SdmmcError = SdmmcError::EUNSUPPORTEDCARD; for _ in 0..CARD_INIT_RETRY { match self.sdcard_init(voltage_switch_init) { @@ -618,7 +617,7 @@ impl SdmmcProtocol { card_version, relative_card_addr: rca, card_state, - card_cap: sdmmc_capability::SdcardCapability(MMC_EMPTY_CAP), + card_cap: capability::SdcardCapability(MMC_EMPTY_CAP), method: BlockTransmissionMode::StopTransmission, card_config: None, }) @@ -874,7 +873,7 @@ impl SdmmcProtocol { let mut resp: [u32; 4] = [0; 4]; - let mut card_cap: SdcardCapability = sdmmc_capability::SdcardCapability(MMC_EMPTY_CAP); + let mut card_cap: SdcardCapability = capability::SdcardCapability(MMC_EMPTY_CAP); let cmd: SdmmcCmd = SdmmcCmd { cmdidx: SD_CMD_SWITCH_FUNC, resp_type: MMC_RSP_R1, @@ -892,25 +891,25 @@ impl SdmmcProtocol { MmcSignalVoltage::Voltage330 => { let speed_class_byte: u8 = unsafe { (*raw_memory)[SD_SWITCH_FUNCTION_GROUP_ONE] }; if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_SDHS != 0 { - card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_SD_HS)); + card_cap.insert(capability::SdcardCapability(MMC_TIMING_SD_HS)); } } MmcSignalVoltage::Voltage180 => { let speed_class_byte: u8 = unsafe { (*raw_memory)[SD_SWITCH_FUNCTION_GROUP_ONE] }; if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR12 != 0 { - card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_SDR12)); + card_cap.insert(capability::SdcardCapability(MMC_TIMING_UHS_SDR12)); } if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR25 != 0 { - card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_SDR25)); + card_cap.insert(capability::SdcardCapability(MMC_TIMING_UHS_SDR25)); } if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR50 != 0 { - card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_SDR50)); + card_cap.insert(capability::SdcardCapability(MMC_TIMING_UHS_SDR50)); } if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104 != 0 { - card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_SDR104)); + card_cap.insert(capability::SdcardCapability(MMC_TIMING_UHS_SDR104)); } if speed_class_byte & SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50 != 0 { - card_cap.insert(sdmmc_capability::SdcardCapability(MMC_TIMING_UHS_DDR50)); + card_cap.insert(capability::SdcardCapability(MMC_TIMING_UHS_DDR50)); } } // For sdcard, the signal voltage cannot be 1.2V diff --git a/drivers/blk/mmc/core/protocol/sdmmc/sdmmc_capability.rs b/drivers/blk/mmc/core/protocol/sdmmc/capability.rs similarity index 100% rename from drivers/blk/mmc/core/protocol/sdmmc/sdmmc_capability.rs rename to drivers/blk/mmc/core/protocol/sdmmc/capability.rs diff --git a/drivers/blk/mmc/core/protocol/sdmmc/sdmmc_constant.rs b/drivers/blk/mmc/core/protocol/sdmmc/constant.rs similarity index 100% rename from drivers/blk/mmc/core/protocol/sdmmc/sdmmc_constant.rs rename to drivers/blk/mmc/core/protocol/sdmmc/constant.rs diff --git a/drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs b/drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs index ad9aeabdd..c59ec6367 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc/mmc_struct.rs @@ -1,7 +1,7 @@ // Copyright 2025, UNSW // SPDX-License-Identifier: BSD-2-Clause -use super::sdcard::{EMmc, Sdcard}; +use super::sd::{EMmc, Sdcard}; // Enums for bus_width #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/drivers/blk/mmc/core/protocol/sdmmc/sdcard.rs b/drivers/blk/mmc/core/protocol/sdmmc/sd.rs similarity index 82% rename from drivers/blk/mmc/core/protocol/sdmmc/sdcard.rs rename to drivers/blk/mmc/core/protocol/sdmmc/sd.rs index 8ac73d1ff..d84d4f15b 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc/sdcard.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc/sd.rs @@ -1,12 +1,14 @@ // Copyright 2025, UNSW // SPDX-License-Identifier: BSD-2-Clause -use crate::{dev_log, info, sdmmc::mmc_struct::CardInfo}; +use core::sync::atomic::Ordering; + +use crate::{dev_log, info, sdmmc::{constant::{MMC_CMD_APP_CMD, SD_CMD_APP_SEND_SCR, SD_CMD_SWITCH_FUNC}, mmc_struct::CardInfo, MmcData, MmcDataFlag, SdmmcCmd, MMC_RSP_R1}, sdmmc_os::Sleep, sdmmc_traits::SdmmcHardware}; use super::{ SdmmcError, mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcState}, - sdmmc_capability::SdcardCapability, + capability::SdcardCapability, }; #[allow(dead_code)] @@ -23,6 +25,83 @@ pub struct Sdcard { } impl Sdcard { + /// Unsafe because dereference raw pointer + pub(crate) unsafe fn sdcard_get_configuration_register( + hardware: &mut T, + sleep: &mut dyn Sleep, + physical_memory: u64, + raw_memory: *mut [u8; 64], + invalidate_cache_fn: fn(), + rca: u16, + ) -> Result { + let mut resp: [u32; 4] = [0; 4]; + let mut cmd: SdmmcCmd = SdmmcCmd { + cmdidx: MMC_CMD_APP_CMD, + resp_type: MMC_RSP_R1, + cmdarg: (rca as u32) << 16, + }; + hardware.sdmmc_do_request(sleep, &cmd, None, &mut resp, 0)?; + + cmd = SdmmcCmd { + cmdidx: SD_CMD_APP_SEND_SCR, + resp_type: MMC_RSP_R1, + cmdarg: 0, + }; + let data: MmcData = MmcData { + blocksize: 8, + blockcnt: 1, + flags: MmcDataFlag::SdmmcDataRead, + addr: physical_memory, + }; + + hardware.sdmmc_do_request(sleep, &cmd, Some(&data), &mut resp, 0)?; + + core::sync::atomic::fence(Ordering::Acquire); + + invalidate_cache_fn(); + + // print out the content of the SCR register + crate::dev_log!("SCR register content: "); + unsafe { crate::sdmmc::print_one_block(raw_memory as *const u8, 8) }; + + // The sdcard register data is always in big endian format + // Now we construct the last 32 bits of the scr register + let scr_raw: u64 = unsafe { + ((((*raw_memory)[0] as u64) << 24) + + (((*raw_memory)[1] as u64) << 16) + + (((*raw_memory)[2] as u64) << 8) + + ((*raw_memory)[3] as u64)) + << 32 + }; + + let scr: Scr = Scr::new(scr_raw)?; + + Ok(scr) + } + + pub fn sdcard_test_tuning( + hardware: &mut T, + sleep: &mut dyn Sleep, + memory: *mut [u8; 64], + ) -> Result<(), SdmmcError> { + let mut resp: [u32; 4] = [0; 4]; + + let data = MmcData { + blocksize: 64, + blockcnt: 1, + flags: MmcDataFlag::SdmmcDataRead, + addr: memory as u64, + }; + + let cmd = SdmmcCmd { + cmdidx: SD_CMD_SWITCH_FUNC, + resp_type: MMC_RSP_R1, + cmdarg: 0x00FFFFFF, + }; + + hardware.sdmmc_do_request(sleep, &cmd, Some(&data), &mut resp, 1) + } + pub fn print_info(&self) { const LABEL_WIDTH: usize = 20; const DATA_WIDTH: usize = 25; diff --git a/drivers/blk/mmc/core/protocol/sdmmc/sd_ops.rs b/drivers/blk/mmc/core/protocol/sdmmc/sd_ops.rs deleted file mode 100644 index c50698003..000000000 --- a/drivers/blk/mmc/core/protocol/sdmmc/sd_ops.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2025, UNSW -// SPDX-License-Identifier: BSD-2-Clause - -use core::sync::atomic::Ordering; - -use crate::{sdmmc_os::Sleep, sdmmc_traits::SdmmcHardware}; - -use super::{ - MMC_RSP_R1, MmcData, MmcDataFlag, SdmmcCmd, SdmmcError, - sdcard::{Scr, Sdcard}, - sdmmc_constant::{MMC_CMD_APP_CMD, SD_CMD_APP_SEND_SCR, SD_CMD_SWITCH_FUNC}, -}; - -impl Sdcard { - /// Unsafe because dereference raw pointer - pub(crate) unsafe fn sdcard_get_configuration_register( - hardware: &mut T, - sleep: &mut dyn Sleep, - physical_memory: u64, - raw_memory: *mut [u8; 64], - invalidate_cache_fn: fn(), - rca: u16, - ) -> Result { - let mut resp: [u32; 4] = [0; 4]; - let mut cmd: SdmmcCmd = SdmmcCmd { - cmdidx: MMC_CMD_APP_CMD, - resp_type: MMC_RSP_R1, - cmdarg: (rca as u32) << 16, - }; - hardware.sdmmc_do_request(sleep, &cmd, None, &mut resp, 0)?; - - cmd = SdmmcCmd { - cmdidx: SD_CMD_APP_SEND_SCR, - resp_type: MMC_RSP_R1, - cmdarg: 0, - }; - let data: MmcData = MmcData { - blocksize: 8, - blockcnt: 1, - flags: MmcDataFlag::SdmmcDataRead, - addr: physical_memory, - }; - - hardware.sdmmc_do_request(sleep, &cmd, Some(&data), &mut resp, 0)?; - - core::sync::atomic::fence(Ordering::Acquire); - - invalidate_cache_fn(); - - // print out the content of the SCR register - crate::dev_log!("SCR register content: "); - unsafe { crate::sdmmc::print_one_block(raw_memory as *const u8, 8) }; - - // The sdcard register data is always in big endian format - // Now we construct the last 32 bits of the scr register - let scr_raw: u64 = unsafe { - ((((*raw_memory)[0] as u64) << 24) - + (((*raw_memory)[1] as u64) << 16) - + (((*raw_memory)[2] as u64) << 8) - + ((*raw_memory)[3] as u64)) - << 32 - }; - - let scr: Scr = Scr::new(scr_raw)?; - - Ok(scr) - } - - pub fn sdcard_test_tuning( - hardware: &mut T, - sleep: &mut dyn Sleep, - memory: *mut [u8; 64], - ) -> Result<(), SdmmcError> { - let mut resp: [u32; 4] = [0; 4]; - - let data = MmcData { - blocksize: 64, - blockcnt: 1, - flags: MmcDataFlag::SdmmcDataRead, - addr: memory as u64, - }; - - let cmd = SdmmcCmd { - cmdidx: SD_CMD_SWITCH_FUNC, - resp_type: MMC_RSP_R1, - cmdarg: 0x00FFFFFF, - }; - - hardware.sdmmc_do_request(sleep, &cmd, Some(&data), &mut resp, 1) - } -} diff --git a/drivers/blk/mmc/core/src/sddf_blk/mod.rs b/drivers/blk/mmc/core/src/sddf_blk/mod.rs index cc1163fe3..3adf707e1 100644 --- a/drivers/blk/mmc/core/src/sddf_blk/mod.rs +++ b/drivers/blk/mmc/core/src/sddf_blk/mod.rs @@ -1,6 +1,8 @@ // Copyright 2025, UNSW // SPDX-License-Identifier: BSD-2-Clause +#![allow(dead_code)] + unsafe extern "C" { pub unsafe fn blk_queue_init_helper(capacity: u64); diff --git a/drivers/blk/mmc/meson/meson_gx_mmc.rs b/drivers/blk/mmc/meson/meson_gx_mmc.rs index 4948190b2..cc9deabe9 100644 --- a/drivers/blk/mmc/meson/meson_gx_mmc.rs +++ b/drivers/blk/mmc/meson/meson_gx_mmc.rs @@ -11,8 +11,8 @@ use sdmmc_protocol::{ HostInfo, MmcData, MmcDataFlag, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, SdmmcError, mmc_struct::{MmcBusWidth, MmcTiming}, - sdcard::Sdcard, - sdmmc_capability::{ + sd::Sdcard, + capability::{ MMC_CAP_4_BIT_DATA, MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS, MMC_VDD_31_32, MMC_VDD_32_33, MMC_VDD_33_34, }, From 0b62222a644d4062c5dc31b9a4ac133636f5feaf Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 13 Aug 2025 16:26:07 +1000 Subject: [PATCH 42/48] Resolve another set of reviews. Signed-off-by: Cheng --- drivers/blk/mmc/core/src/main.rs | 2 ++ drivers/blk/mmc/core/src/sddf_blk/mod.rs | 1 - drivers/blk/mmc/core/src/sddf_helper.c | 1 + drivers/blk/mmc/meson/meson_gx_mmc.rs | 4 +--- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/blk/mmc/core/src/main.rs b/drivers/blk/mmc/core/src/main.rs index 20372b365..62d5df85f 100644 --- a/drivers/blk/mmc/core/src/main.rs +++ b/drivers/blk/mmc/core/src/main.rs @@ -76,6 +76,8 @@ fn init() -> impl Handler { // This line of code actually is very unsafe! // Considering the memory is stolen from the memory that has sdcard registers mapped in + // A data region for card init is needed because some information is passed to the driver + // by the card transferring data to normal memory instead of reading a register. let init_data_vaddr = unsafe { blk_device_init_data_vaddr() }; let init_data_ioaddr = unsafe { blk_device_init_data_ioaddr() }; diff --git a/drivers/blk/mmc/core/src/sddf_blk/mod.rs b/drivers/blk/mmc/core/src/sddf_blk/mod.rs index 3adf707e1..6a2ce2647 100644 --- a/drivers/blk/mmc/core/src/sddf_blk/mod.rs +++ b/drivers/blk/mmc/core/src/sddf_blk/mod.rs @@ -41,7 +41,6 @@ pub struct BlkRequest { pub io_or_offset: u64, pub block_number: u64, pub count: u32, - // I suggest use u32 here and change the count to use u32 in sddf_blk pub success_count: u32, pub count_to_do: u32, pub id: u32, diff --git a/drivers/blk/mmc/core/src/sddf_helper.c b/drivers/blk/mmc/core/src/sddf_helper.c index 51505cd2a..d1be4a149 100644 --- a/drivers/blk/mmc/core/src/sddf_helper.c +++ b/drivers/blk/mmc/core/src/sddf_helper.c @@ -21,6 +21,7 @@ void blk_queue_init_helper(uint64_t capacity) storage_info->sector_size = 512; storage_info->block_size = 1; storage_info->capacity = capacity; + blk_storage_set_ready(storage_info, true); storage_info->ready = true; } diff --git a/drivers/blk/mmc/meson/meson_gx_mmc.rs b/drivers/blk/mmc/meson/meson_gx_mmc.rs index cc9deabe9..ae055b120 100644 --- a/drivers/blk/mmc/meson/meson_gx_mmc.rs +++ b/drivers/blk/mmc/meson/meson_gx_mmc.rs @@ -21,7 +21,7 @@ use sdmmc_protocol::{ sdmmc_traits::SdmmcHardware, }; -pub const SDIO_BASE: u64 = 0xffe05000; // Base address from DTS +// The driver is targeting the sdmmc host controller at this address: SDIO 0xffe05000 macro_rules! div_round_up { ($n:expr, $d:expr) => { @@ -193,7 +193,6 @@ pub struct SdmmcMesonHardware { frequency: u32, // Irq enabled enabled_irq: u32, - // Put other variables here } impl SdmmcMesonHardware { @@ -201,7 +200,6 @@ impl SdmmcMesonHardware { let register: &'static mut MesonSdmmcRegisters = unsafe { MesonSdmmcRegisters::new(sdmmc_register_base) }; - // TODO: Call reset function here SdmmcMesonHardware { register, delay: None, From 988ccbf569002b51d07d12183222a53f2ee08908 Mon Sep 17 00:00:00 2001 From: Cheng Date: Tue, 7 Oct 2025 19:43:51 +1100 Subject: [PATCH 43/48] Update the driver to the latest change. Signed-off-by: Cheng --- drivers/blk/mmc/core/protocol/sdmmc.rs | 139 +++++------------- .../blk/mmc/core/protocol/sdmmc/constant.rs | 2 +- drivers/blk/mmc/core/protocol/sdmmc/sd.rs | 13 +- drivers/blk/mmc/core/protocol/sdmmc_os.rs | 6 +- drivers/blk/mmc/core/protocol/sdmmc_traits.rs | 15 +- drivers/blk/mmc/core/src/main.rs | 10 +- .../blk/mmc/core/src/sel4_microkit_os/mod.rs | 2 +- .../mmc/core/src/sel4_microkit_os/odroidc4.rs | 63 ++++---- drivers/blk/mmc/meson/meson_gx_mmc.rs | 73 +++++---- 9 files changed, 142 insertions(+), 181 deletions(-) diff --git a/drivers/blk/mmc/core/protocol/sdmmc.rs b/drivers/blk/mmc/core/protocol/sdmmc.rs index 8dfc80fd4..b976da197 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc.rs @@ -1,9 +1,9 @@ // Copyright 2025, UNSW // SPDX-License-Identifier: BSD-2-Clause +pub mod capability; pub mod mmc_struct; pub mod sd; -pub mod capability; mod constant; @@ -14,12 +14,10 @@ use core::{ task::{Context, Poll, Waker}, }; -use mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcDevice, MmcState, MmcTiming}; -use sd::{Cid, Csd, Scr, Sdcard}; use capability::{ MMC_CAP_4_BIT_DATA, MMC_EMPTY_CAP, MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS_DDR50, MMC_TIMING_UHS_SDR12, MMC_TIMING_UHS_SDR25, MMC_TIMING_UHS_SDR50, MMC_TIMING_UHS_SDR104, - SdcardCapability, SdmmcHostCapability, + SdcardCapability, }; use constant::{ MMC_CMD_ALL_SEND_CID, MMC_CMD_APP_CMD, MMC_CMD_ERASE, MMC_CMD_GO_IDLE_STATE, @@ -37,14 +35,16 @@ use constant::{ SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR50, SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_SDR104, SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE, }; +use mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcDevice, MmcState, MmcTiming}; +use sd::{Cid, Csd, Scr, Sdcard}; pub const SDCARD_DEFAULT_SECTOR_SIZE: u32 = 512; use crate::{ dev_log, - sdmmc::{mmc_struct::CardInfo}, + sdmmc::mmc_struct::CardInfo, sdmmc_os::{Sleep, VoltageOps}, - sdmmc_traits::SdmmcHardware, + sdmmc_traits::{SdmmcHardware, SdmmcOps}, }; pub struct SdmmcCmd { @@ -107,14 +107,6 @@ pub const MMC_RSP_R5: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; pub const MMC_RSP_R6: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; pub const MMC_RSP_R7: u32 = MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE; -// Enums for power_mode -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum MmcPowerMode { - Off = 0, - On = 1, - Undefined = 2, -} - // Signal voltage #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum MmcSignalVoltage { @@ -198,17 +190,6 @@ pub struct MmcIos { /// data transfer occurs at higher rates. pub clock: u64, - /// The current power supply mode for the SD/MMC card. - /// - /// - This field indicates whether the card is powered on, powered off, or - /// being powered up. The power mode can affect the card's internal state - /// and availability for communication. - /// - Possible values: - /// - `PowerMode::Off`: The card is completely powered off. - /// - `PowerMode::Up`: The card is in the process of powering up. - /// - `PowerMode::On`: The card is fully powered and ready for communication. - pub power_mode: MmcPowerMode, - /// The width of the data bus used for communication between the host and the card. /// /// - This field specifies whether the bus operates in 1-bit, 4-bit, or 8-bit mode. @@ -257,14 +238,17 @@ pub struct HostInfo { /// - Cards often negotiate their operating voltage during initialization. pub vdd: u32, - /// The power delay (in milliseconds) used after powering the card to ensure - /// stable operation. - /// - /// - After powering up the card, the host controller typically waits for a - /// certain period before initiating communication to ensure that the card's - /// power supply is stable. - /// - This delay ensures the card is ready to respond to commands. - pub power_delay_ms: u32, + /// The capability of the host, like the features the host supports + pub host_capability: u128, +} + +impl HostInfo { + /// Zero cost function to determine if the host has a set of specific capabilities or not + #[inline] + pub const fn has_capability(&self, capability: u128) -> bool { + capability::SdmmcHostCapability(self.host_capability) + .contains(capability::SdmmcHostCapability(capability)) + } } pub struct SdmmcProtocol { @@ -276,10 +260,6 @@ pub struct SdmmcProtocol { mmc_ios: MmcIos, - host_info: HostInfo, - - host_capability: SdmmcHostCapability, - /// This mmc device is optional because there may not always be a card in the slot! mmc_device: Option, @@ -296,15 +276,13 @@ where impl SdmmcProtocol { pub fn new(mut hardware: T, sleep: S, voltage_ops: Option) -> Result { - let (ios, info, cap) = hardware.sdmmc_init()?; + let ios = hardware.sdmmc_init()?; Ok(SdmmcProtocol { hardware, sleep, voltage_ops, mmc_ios: ios, - host_info: info, - host_capability: SdmmcHostCapability(cap), mmc_device: None, private_memory: None, }) @@ -383,13 +361,9 @@ impl SdmmcProtocol { // Right now we deliberately not set XPC bit for maximum compatibility // Change this when we decide to support spi or SDSC as well - cmd.cmdarg |= self.host_info.vdd & 0xff8000; + cmd.cmdarg |= T::HOST_INFO.vdd & 0xff8000; - if voltage_switch == true - && self - .host_capability - .contains(capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) - { + if voltage_switch == true && T::HOST_INFO.has_capability(MMC_TIMING_UHS_SDR12) { cmd.cmdarg |= OCR_S18R; // It seems that cards will not respond to commands that have MMC_VDD_165_195 bit set, even if the card supports UHS-I // cmd.cmdarg |= MMC_VDD_165_195; @@ -416,9 +390,7 @@ impl SdmmcProtocol { // Checking if the host and card is eligible for voltage switch if voltage_switch == true - && self - .host_capability - .contains(capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)) + && T::HOST_INFO.has_capability(MMC_TIMING_UHS_SDR12) && resp[0] & OCR_HCS == OCR_HCS && resp[0] & OCR_S18R == OCR_S18R { @@ -430,31 +402,11 @@ impl SdmmcProtocol { Ok(()) } - fn sdmmc_power_cycle( - sleep: &mut S, - voltage_ops: &mut V, - power_delay_ms: u32, - ) -> Result<(), SdmmcError> { - voltage_ops.card_set_power(MmcPowerMode::Off)?; - - sleep.usleep(power_delay_ms * 1_000); - - voltage_ops.card_set_power(MmcPowerMode::On)?; - - sleep.usleep(power_delay_ms * 1_000); - - Ok(()) - } - // Function that is not completed pub fn setup_card(&mut self) -> Result<(), SdmmcError> { // Disable all irqs here self.hardware.sdmmc_config_interrupt(false, false)?; - if self.mmc_ios.power_mode != MmcPowerMode::On { - return Err(SdmmcError::EINVAL); - } - let clock = self.hardware.sdmmc_config_timing(MmcTiming::CardSetup)?; self.mmc_ios.clock = clock; @@ -468,9 +420,7 @@ impl SdmmcProtocol { // For card initialization, we retry 2 times for each card // There could be more complex retry logic implemented in the future let res: Result<(), SdmmcError> = 'sdcard_init: { - let mut voltage_switch_init: bool = self - .host_capability - .contains(capability::SdmmcHostCapability(MMC_TIMING_UHS_SDR12)); + let mut voltage_switch_init: bool = T::HOST_INFO.has_capability(MMC_TIMING_UHS_SDR12); let mut init_error: SdmmcError = SdmmcError::EUNSUPPORTEDCARD; for _ in 0..CARD_INIT_RETRY { match self.sdcard_init(voltage_switch_init) { @@ -484,11 +434,8 @@ impl SdmmcProtocol { // Reset the signaling voltage back to 3.3V voltage_ops.card_voltage_switch(MmcSignalVoltage::Voltage330)?; self.sleep.usleep(1_000); - Self::sdmmc_power_cycle( - &mut self.sleep, - voltage_ops, - self.host_info.power_delay_ms, - )?; + + voltage_ops.card_power_cycling()?; } // One bug that does not break anything here is // sdmmc_host_reset will reset the clock to CardSetup timing and turn off the irq @@ -954,9 +901,7 @@ impl SdmmcProtocol { } if self.mmc_ios.bus_width == MmcBusWidth::Width1 - && self - .host_capability - .contains(SdmmcHostCapability(MMC_CAP_4_BIT_DATA)) + && T::HOST_INFO.has_capability(MMC_CAP_4_BIT_DATA) { // Switch data bits per transfer let relative_card_address: u16; @@ -1042,43 +987,33 @@ impl SdmmcProtocol { match self.mmc_ios.signal_voltage { MmcSignalVoltage::Voltage330 => { if sdcard_cap.contains(SdcardCapability(MMC_TIMING_SD_HS)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_SD_HS)) + && T::HOST_INFO.has_capability(MMC_TIMING_SD_HS) { target_timing = MmcTiming::SdHs; } } MmcSignalVoltage::Voltage180 => { if sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR104)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR104)) + && T::HOST_INFO.has_capability(MMC_TIMING_UHS_SDR104) { target_timing = MmcTiming::UhsSdr104; // If the switch speed succeed, terminate the block break 'tune_speed; } if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_DDR50)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_UHS_DDR50)) + && T::HOST_INFO.has_capability(MMC_TIMING_UHS_DDR50) { target_timing = MmcTiming::UhsDdr50; break 'tune_speed; } if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR50)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR50)) + && T::HOST_INFO.has_capability(MMC_TIMING_UHS_SDR50) { target_timing = MmcTiming::UhsSdr50; break 'tune_speed; } if !sdcard_cap.contains(SdcardCapability(MMC_TIMING_UHS_SDR25)) - && self - .host_capability - .contains(SdmmcHostCapability(MMC_TIMING_UHS_SDR25)) + && T::HOST_INFO.has_capability(MMC_TIMING_UHS_SDR25) { target_timing = MmcTiming::UhsSdr25; break 'tune_speed; @@ -1413,10 +1348,6 @@ impl SdmmcProtocol { } } - pub fn get_host_info(&mut self) -> HostInfo { - self.host_info.clone() - } - pub fn print_card_info(&self) { if let Some(ref device) = self.mmc_device { match device { @@ -1435,6 +1366,10 @@ impl SdmmcProtocol { } } + pub const fn host_info() -> HostInfo { + T::HOST_INFO + } + pub fn card_info(&self) -> Result { let res: Result; if let Some(ref device) = self.mmc_device { @@ -1464,7 +1399,7 @@ enum CmdState { } pub struct SdmmcCmdFuture<'a, 'b, 'c> { - hardware: &'a mut dyn SdmmcHardware, + hardware: &'a mut dyn SdmmcOps, cmd: &'b SdmmcCmd, waker: Option, state: CmdState, @@ -1473,7 +1408,7 @@ pub struct SdmmcCmdFuture<'a, 'b, 'c> { impl<'a, 'b, 'c> SdmmcCmdFuture<'a, 'b, 'c> { pub fn new( - hardware: &'a mut dyn SdmmcHardware, + hardware: &'a mut dyn SdmmcOps, cmd: &'b SdmmcCmd, response: &'c mut [u32; 4], ) -> SdmmcCmdFuture<'a, 'b, 'c> { @@ -1509,7 +1444,7 @@ impl<'a, 'b, 'c> Future for SdmmcCmdFuture<'a, 'b, 'c> { let this: &mut SdmmcCmdFuture<'a, 'b, 'c> = self.as_mut().get_mut(); let cmd: &SdmmcCmd = this.cmd; let response: &mut [u32; 4] = this.response; - let hardware: &dyn SdmmcHardware = this.hardware; + let hardware: &dyn SdmmcOps = this.hardware; res = hardware.sdmmc_receive_response(cmd, response); } if let Err(SdmmcError::EBUSY) = res { diff --git a/drivers/blk/mmc/core/protocol/sdmmc/constant.rs b/drivers/blk/mmc/core/protocol/sdmmc/constant.rs index 7cce6c3f8..f621af67b 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc/constant.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc/constant.rs @@ -93,4 +93,4 @@ pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_SDR104: u8 = pub const SD_SWITCH_FUNCTION_GROUP_ONE_CHECK_UHS_DDR50: u8 = 1 << SD_SWITCH_FUNCTION_GROUP_ONE_SET_UHS_DDR50; -pub const SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE: usize = 16; \ No newline at end of file +pub const SD_SWITCH_FUNCTION_SELECTION_GROUP_ONE: usize = 16; diff --git a/drivers/blk/mmc/core/protocol/sdmmc/sd.rs b/drivers/blk/mmc/core/protocol/sdmmc/sd.rs index d84d4f15b..9e4bafa2d 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc/sd.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc/sd.rs @@ -3,12 +3,21 @@ use core::sync::atomic::Ordering; -use crate::{dev_log, info, sdmmc::{constant::{MMC_CMD_APP_CMD, SD_CMD_APP_SEND_SCR, SD_CMD_SWITCH_FUNC}, mmc_struct::CardInfo, MmcData, MmcDataFlag, SdmmcCmd, MMC_RSP_R1}, sdmmc_os::Sleep, sdmmc_traits::SdmmcHardware}; +use crate::{ + dev_log, info, + sdmmc::{ + MMC_RSP_R1, MmcData, MmcDataFlag, SdmmcCmd, + constant::{MMC_CMD_APP_CMD, SD_CMD_APP_SEND_SCR, SD_CMD_SWITCH_FUNC}, + mmc_struct::CardInfo, + }, + sdmmc_os::Sleep, + sdmmc_traits::SdmmcHardware, +}; use super::{ SdmmcError, - mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcState}, capability::SdcardCapability, + mmc_struct::{BlockTransmissionMode, MmcBusWidth, MmcState}, }; #[allow(dead_code)] diff --git a/drivers/blk/mmc/core/protocol/sdmmc_os.rs b/drivers/blk/mmc/core/protocol/sdmmc_os.rs index 5172bdac0..9f27d503c 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc_os.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc_os.rs @@ -1,12 +1,12 @@ // Copyright 2025, UNSW // SPDX-License-Identifier: BSD-2-Clause -use crate::sdmmc::MmcPowerMode; use crate::sdmmc::MmcSignalVoltage; use crate::sdmmc::SdmmcError; use core::sync::atomic::AtomicU8; use core::sync::atomic::Ordering; +#[allow(unused_variables)] pub trait Sleep { /// For putting the process to sleep for a while, /// The default spinning implementation is a very unreliable way to put the process to sleep @@ -18,12 +18,13 @@ pub trait Sleep { } } +#[allow(unused_variables)] pub trait VoltageOps { fn card_voltage_switch(&mut self, voltage: MmcSignalVoltage) -> Result<(), SdmmcError> { core::panic!("Voltage switch not implemented!"); } - fn card_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { + fn card_power_cycling(&mut self) -> Result<(), SdmmcError> { core::panic!("Power cycling not implemented!"); } } @@ -34,6 +35,7 @@ pub fn process_wait_unreliable(time_ns: u64) { } } +#[allow(unused_variables)] pub trait Log { fn log(&self, args: core::fmt::Arguments) { core::panic!("Logging not implemented!"); diff --git a/drivers/blk/mmc/core/protocol/sdmmc_traits.rs b/drivers/blk/mmc/core/protocol/sdmmc_traits.rs index 6417d0c2b..b08fe6be6 100644 --- a/drivers/blk/mmc/core/protocol/sdmmc_traits.rs +++ b/drivers/blk/mmc/core/protocol/sdmmc_traits.rs @@ -19,9 +19,8 @@ const POLLING_CHANCE_BEFORE_TIME_OUT: u32 = 10240; const DATA_TRANSFER_POLLING_CHANCE_BEFORE_TIME_OUT: u32 = 2048; #[allow(unused_variables)] -/// Trait to be implemented by the sdcard hal -pub trait SdmmcHardware { - fn sdmmc_init(&mut self) -> Result<(MmcIos, HostInfo, u128), SdmmcError> { +pub trait SdmmcOps { + fn sdmmc_init(&mut self) -> Result { Err(SdmmcError::ENOTIMPLEMENTED) } @@ -205,3 +204,13 @@ pub trait SdmmcHardware { Err(SdmmcError::EUNDEFINED) } } + +#[allow(unused_variables)] +/// Trait to be implemented by the sdcard hal +pub trait SdmmcHardware: SdmmcOps { + const HOST_INFO: HostInfo; + + /// This function is NOT meant for initialization of the host + /// It should be marked as const once the const traits feature in Rust is stable + unsafe fn new(sdmmc_register_base: u64) -> Self; +} diff --git a/drivers/blk/mmc/core/src/main.rs b/drivers/blk/mmc/core/src/main.rs index 62d5df85f..aed1ccc33 100644 --- a/drivers/blk/mmc/core/src/main.rs +++ b/drivers/blk/mmc/core/src/main.rs @@ -29,9 +29,10 @@ const INTERRUPT: sel4_microkit::Channel = sel4_microkit::Channel::new(0); const BLK_VIRTUALIZER: sel4_microkit::Channel = sel4_microkit::Channel::new(1); const TIMER: TimerOps = TimerOps::new(); const SERIAL: SerialOps = SerialOps::new(); +const HOST_INFO: HostInfo = crate::sel4_microkit_os::platform::host_info(); use sdmmc_protocol::{ - sdmmc::{HostInfo, SDCARD_DEFAULT_SECTOR_SIZE, mmc_struct::CardInfo}, + sdmmc::{mmc_struct::CardInfo, HostInfo, SDCARD_DEFAULT_SECTOR_SIZE}, sdmmc_traits::SdmmcHardware, }; use sdmmc_protocol::{ @@ -100,7 +101,6 @@ fn init() -> impl Handler { sdmmc_host.print_card_info(); - let host_info: HostInfo = sdmmc_host.get_host_info(); let card_info: CardInfo = sdmmc_host.card_info().unwrap(); let _ = sdmmc_host.config_interrupt(false, false); @@ -127,7 +127,6 @@ fn init() -> impl Handler { future: None, sdmmc: Some(sdmmc_host), request: None, - host_info, card_info, } } @@ -136,7 +135,6 @@ struct HandlerImpl { future: Option, SdmmcProtocol)>>>>, sdmmc: Option>, request: Option, - host_info: HostInfo, card_info: CardInfo, } @@ -292,7 +290,7 @@ where match request.request_code { BlkOp::BlkReqRead => { request.count_to_do = - core::cmp::min(request.count, self.host_info.max_block_per_req); + core::cmp::min(request.count, HOST_INFO.max_block_per_req); if let Some(sdmmc) = self.sdmmc.take() { self.future = Some(Box::pin(sdmmc.read_block( request.count_to_do, @@ -309,7 +307,7 @@ where } BlkOp::BlkReqWrite => { request.count_to_do = - core::cmp::min(request.count, self.host_info.max_block_per_req); + core::cmp::min(request.count, HOST_INFO.max_block_per_req); if let Some(sdmmc) = self.sdmmc.take() { self.future = Some(Box::pin(sdmmc.write_block( request.count_to_do, diff --git a/drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs b/drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs index 1622af92b..9ea53b558 100644 --- a/drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs +++ b/drivers/blk/mmc/core/src/sel4_microkit_os/mod.rs @@ -9,7 +9,7 @@ mod odroidc4; #[cfg(feature = "meson")] pub(crate) mod platform { - pub(crate) use crate::sel4_microkit_os::odroidc4::{VOLTAGE, platform_hal}; + pub(crate) use crate::sel4_microkit_os::odroidc4::{VOLTAGE, host_info, platform_hal}; } const NS_IN_US: u64 = 1000; diff --git a/drivers/blk/mmc/core/src/sel4_microkit_os/odroidc4.rs b/drivers/blk/mmc/core/src/sel4_microkit_os/odroidc4.rs index 17321e2b5..c9547cba6 100644 --- a/drivers/blk/mmc/core/src/sel4_microkit_os/odroidc4.rs +++ b/drivers/blk/mmc/core/src/sel4_microkit_os/odroidc4.rs @@ -4,12 +4,15 @@ use core::ptr; use sdmmc_protocol::{ - sdmmc::{MmcPowerMode, MmcSignalVoltage, SdmmcError}, - sdmmc_os::VoltageOps, + sdmmc::{HostInfo, MmcSignalVoltage, SdmmcError}, + sdmmc_os::{Sleep, VoltageOps}, + sdmmc_traits::SdmmcHardware, }; use meson_hal::meson_gx_mmc::SdmmcMesonHardware; +use crate::sel4_microkit_os::TimerOps; + pub struct Odroidc4VoltageSwitch; pub(crate) const VOLTAGE: Odroidc4VoltageSwitch = Odroidc4VoltageSwitch::new(); @@ -86,7 +89,7 @@ impl VoltageOps for Odroidc4VoltageSwitch { Ok(()) } - fn card_set_power(&mut self, power_mode: MmcPowerMode) -> Result<(), SdmmcError> { + fn card_power_cycling(&mut self) -> Result<(), SdmmcError> { let mut value: u32; unsafe { value = ptr::read_volatile(AO_RTI_OUTPUT_ENABLE_REG as *const u32); @@ -98,32 +101,36 @@ impl VoltageOps for Odroidc4VoltageSwitch { ptr::write_volatile(AO_RTI_OUTPUT_ENABLE_REG as *mut u32, value); } } - match power_mode { - MmcPowerMode::On => { - unsafe { - value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); - } - if value & GPIO_AO_3 == 0 { - value |= GPIO_AO_3; - unsafe { - ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); - } - } - self.card_voltage_switch(MmcSignalVoltage::Voltage330)?; + + // Turning the power off + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + if value & GPIO_AO_3 != 0 { + value &= !GPIO_AO_3; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); } - MmcPowerMode::Off => { - unsafe { - value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); - } - if value & GPIO_AO_3 != 0 { - value &= !GPIO_AO_3; - unsafe { - ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); - } - } + } + + // Sleep for 5ms + TimerOps::new().usleep(5000); + + // Turning the power on + unsafe { + value = ptr::read_volatile(AO_RTI_OUTPUT_LEVEL_REG as *const u32); + } + if value & GPIO_AO_3 == 0 { + value |= GPIO_AO_3; + unsafe { + ptr::write_volatile(AO_RTI_OUTPUT_LEVEL_REG as *mut u32, value); } - _ => return Err(SdmmcError::EINVAL), } + self.card_voltage_switch(MmcSignalVoltage::Voltage330)?; + + // Sleep for another 5ms + TimerOps::new().usleep(5000); + Ok(()) } } @@ -131,3 +138,7 @@ impl VoltageOps for Odroidc4VoltageSwitch { pub unsafe fn platform_hal(regs_base: u64) -> SdmmcMesonHardware { unsafe { SdmmcMesonHardware::new(regs_base) } } + +pub const fn host_info() -> HostInfo { + SdmmcMesonHardware::HOST_INFO +} diff --git a/drivers/blk/mmc/meson/meson_gx_mmc.rs b/drivers/blk/mmc/meson/meson_gx_mmc.rs index ae055b120..2e4688b1b 100644 --- a/drivers/blk/mmc/meson/meson_gx_mmc.rs +++ b/drivers/blk/mmc/meson/meson_gx_mmc.rs @@ -8,17 +8,16 @@ use core::ptr; use sdmmc_protocol::{ dev_log, info, sdmmc::{ - HostInfo, MmcData, MmcDataFlag, MmcIos, MmcPowerMode, MmcSignalVoltage, SdmmcCmd, - SdmmcError, - mmc_struct::{MmcBusWidth, MmcTiming}, - sd::Sdcard, + HostInfo, MmcData, MmcDataFlag, MmcIos, MmcSignalVoltage, SdmmcCmd, SdmmcError, capability::{ MMC_CAP_4_BIT_DATA, MMC_TIMING_LEGACY, MMC_TIMING_SD_HS, MMC_TIMING_UHS, MMC_VDD_31_32, MMC_VDD_32_33, MMC_VDD_33_34, }, + mmc_struct::{MmcBusWidth, MmcTiming}, + sd::Sdcard, }, sdmmc_os::{Sleep, process_wait_unreliable}, - sdmmc_traits::SdmmcHardware, + sdmmc_traits::{SdmmcHardware, SdmmcOps}, }; // The driver is targeting the sdmmc host controller at this address: SDIO 0xffe05000 @@ -174,7 +173,7 @@ struct MesonSdmmcRegisters { impl MesonSdmmcRegisters { /// This function is only safe to use if the sdmmc_register_base is the correct memory addr /// of the sdmmc register base and accessible for the driver - unsafe fn new(sdmmc_register_base: u64) -> &'static mut MesonSdmmcRegisters { + const unsafe fn new(sdmmc_register_base: u64) -> &'static mut MesonSdmmcRegisters { unsafe { &mut *(sdmmc_register_base as *mut MesonSdmmcRegisters) } } } @@ -196,21 +195,6 @@ pub struct SdmmcMesonHardware { } impl SdmmcMesonHardware { - pub unsafe fn new(sdmmc_register_base: u64) -> Self { - let register: &'static mut MesonSdmmcRegisters = - unsafe { MesonSdmmcRegisters::new(sdmmc_register_base) }; - - SdmmcMesonHardware { - register, - delay: None, - // Default uboot speed class - timing: MmcTiming::SdHs, - // Wrong value but should not have much impact - frequency: MESON_MIN_FREQUENCY, - enabled_irq: 0, - } - } - /// The meson_reset function reset the host register state /// However, this function does not try to reset the power state like operating voltage and signal voltage fn meson_reset(&mut self) { @@ -412,16 +396,13 @@ impl SdmmcMesonHardware { } } -impl SdmmcHardware for SdmmcMesonHardware { - fn sdmmc_init(&mut self) -> Result<(MmcIos, HostInfo, u128), SdmmcError> { - let cap: u128 = MMC_TIMING_LEGACY | MMC_TIMING_SD_HS | MMC_TIMING_UHS | MMC_CAP_4_BIT_DATA; - +impl SdmmcOps for SdmmcMesonHardware { + fn sdmmc_init(&mut self) -> Result { // Reset host state self.meson_reset(); let ios: MmcIos = MmcIos { clock: MESON_MIN_FREQUENCY as u64, - power_mode: MmcPowerMode::On, bus_width: MmcBusWidth::Width1, signal_voltage: MmcSignalVoltage::Voltage330, enabled_irq: false, @@ -429,17 +410,7 @@ impl SdmmcHardware for SdmmcMesonHardware { spi: None, }; - let info: HostInfo = HostInfo { - max_frequency: MESON_MAX_FREQUENCY as u64, - min_frequency: MESON_MIN_FREQUENCY as u64, - max_block_per_req: MAX_BLOCK_PER_TRANSFER, - // On odroid c4, the operating voltage is default to 3.3V - vdd: (MMC_VDD_33_34 | MMC_VDD_32_33 | MMC_VDD_31_32), - // TODO, figure out the correct value when we can power the card on and off - power_delay_ms: 5, - }; - - return Ok((ios, info, cap)); + return Ok(ios); } fn sdmmc_execute_tuning( @@ -790,7 +761,6 @@ impl SdmmcHardware for SdmmcMesonHardware { let ios: MmcIos = MmcIos { clock: MESON_MIN_FREQUENCY as u64, - power_mode: MmcPowerMode::On, bus_width: MmcBusWidth::Width1, signal_voltage: MmcSignalVoltage::Voltage330, enabled_irq: false, @@ -801,3 +771,30 @@ impl SdmmcHardware for SdmmcMesonHardware { Ok(ios) } } + +impl SdmmcHardware for SdmmcMesonHardware { + const HOST_INFO: HostInfo = HostInfo { + max_frequency: MESON_MAX_FREQUENCY as u64, + min_frequency: MESON_MIN_FREQUENCY as u64, + max_block_per_req: MAX_BLOCK_PER_TRANSFER, + // On odroid c4, the operating voltage is default to 3.3V + vdd: (MMC_VDD_33_34 | MMC_VDD_32_33 | MMC_VDD_31_32), + host_capability: MMC_TIMING_LEGACY | MMC_TIMING_SD_HS | MMC_TIMING_UHS | MMC_CAP_4_BIT_DATA, + }; + + /// This function should be CONST!!! It is just Rust does not support it yet + unsafe fn new(sdmmc_register_base: u64) -> Self { + let register: &'static mut MesonSdmmcRegisters = + unsafe { MesonSdmmcRegisters::new(sdmmc_register_base) }; + + SdmmcMesonHardware { + register, + delay: None, + // Default uboot speed class + timing: MmcTiming::SdHs, + // Wrong value but should not have much impact + frequency: MESON_MIN_FREQUENCY, + enabled_irq: 0, + } + } +} From da665c720472f9786388a58e90afa1844d918de1 Mon Sep 17 00:00:00 2001 From: Cheng Date: Tue, 7 Oct 2025 19:53:10 +1100 Subject: [PATCH 44/48] Add a simple readme. Signed-off-by: Cheng --- drivers/blk/mmc/core/README.md | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 drivers/blk/mmc/core/README.md diff --git a/drivers/blk/mmc/core/README.md b/drivers/blk/mmc/core/README.md new file mode 100644 index 000000000..124fd1dc6 --- /dev/null +++ b/drivers/blk/mmc/core/README.md @@ -0,0 +1,43 @@ + +**Note:** This driver is in the early stages of development and does not yet implement the full feature set found in mature stacks like the Linux MMC subsystem. + +--- + +## Features + +### Supported +- **Card Types:** SDHC / SDXC +- **Bus Speeds:** High Speed (SDHS) / UHS-I +- **Core Operations:** Asynchronous Read, Write, and Erase + +### Not Yet Implemented or Tested +- **Card Types:** SDSC / SDUC, eMMC +- **Interface Modes:** SPI mode +- **Bus Speeds:** Speed classes higher than UHS-I (e.g., UHS-II, UHS-III) +- A comprehensive set of features found in mature MMC stacks. + +--- + +## Hardware Platform Support + +| Platform | Status | +| :------------- | :-------------- | +| Odroid C4 | ✅ Supported | +| sdhci-zynqmp | 🚧 In Progress | + +--- + +## Adding Support for a New Hardware Platform + +Porting the driver to a new hardware platform involves implementing the hardware-specific logic. Follow these steps: + +1. **Familiarize Yourself:** Study the driver's architecture and review the reference implementation for the Odroid C4 to understand the core components. +2. **Implement the HAL:** Create a new hardware abstraction layer (HAL) for your platform by implementing the `SdmmcHardware` trait. This trait defines the interface for all hardware-specific operations. +3. **Build with Logging:** Compile your HAL and the core protocol crate with the `dev-logs` feature enabled to get detailed diagnostic output. +4. **Test and Verify:** Run the driver on your hardware. Analyze the logs to verify that the initialization sequence and command responses are correct. + +--- +*This README was authored by the project maintainer and refined for clarity with assistance from Google's Gemini. All technical information was written and verified by the author.* \ No newline at end of file From 1116f629afe515861441a9c50d5e8f51cf8f65a6 Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 8 Oct 2025 13:17:42 +1100 Subject: [PATCH 45/48] Update the meta.py file Signed-off-by: Cheng --- examples/blk/meta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/blk/meta.py b/examples/blk/meta.py index 4ca4a6537..b04996b9b 100644 --- a/examples/blk/meta.py +++ b/examples/blk/meta.py @@ -107,7 +107,7 @@ def generate(sdf_file: str, output_dir: str, dtb: DeviceTree): blk_system.add_client(client, partition=partition) if board.name == "odroidc4": - gpio_mr = MemoryRegion("gpio", 0x1000, paddr=0xFF800000) + gpio_mr = MemoryRegion(sdf, name="gpio", size=0x1000, paddr=0xFF800000) blk_driver.add_map(Map(gpio_mr, 0xFF800000, "rw", cached=False)) sdf.add_mr(gpio_mr) From 8e1f06c9a9f134ed1838fafd18917fc6fc6fdd41 Mon Sep 17 00:00:00 2001 From: Cheng Date: Wed, 8 Oct 2025 13:24:01 +1100 Subject: [PATCH 46/48] Add back the bcm2835/config.json I removed from deps accidently in the process of rebase. Signed-off-by: Cheng --- .reuse/dep5 | 1 + 1 file changed, 1 insertion(+) diff --git a/.reuse/dep5 b/.reuse/dep5 index a620176a4..24c9262fa 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -35,6 +35,7 @@ Files: drivers/timer/meson/config.json drivers/timer/goldfish/config.json drivers/timer/cdns/config.json + drivers/timer/bcm2835/config.json drivers/blk/mmc/core/config.json Copyright: UNSW License: BSD-2-Clause From 007fad91a55028e2531c7bc5db4eeabeebad5c47 Mon Sep 17 00:00:00 2001 From: Cheng Date: Mon, 13 Oct 2025 11:22:55 +1100 Subject: [PATCH 47/48] Readme modification Signed-off-by: Cheng --- drivers/blk/mmc/core/README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/blk/mmc/core/README.md b/drivers/blk/mmc/core/README.md index 124fd1dc6..ecc6be385 100644 --- a/drivers/blk/mmc/core/README.md +++ b/drivers/blk/mmc/core/README.md @@ -37,7 +37,4 @@ Porting the driver to a new hardware platform involves implementing the hardware 1. **Familiarize Yourself:** Study the driver's architecture and review the reference implementation for the Odroid C4 to understand the core components. 2. **Implement the HAL:** Create a new hardware abstraction layer (HAL) for your platform by implementing the `SdmmcHardware` trait. This trait defines the interface for all hardware-specific operations. 3. **Build with Logging:** Compile your HAL and the core protocol crate with the `dev-logs` feature enabled to get detailed diagnostic output. -4. **Test and Verify:** Run the driver on your hardware. Analyze the logs to verify that the initialization sequence and command responses are correct. - ---- -*This README was authored by the project maintainer and refined for clarity with assistance from Google's Gemini. All technical information was written and verified by the author.* \ No newline at end of file +4. **Test and Verify:** Run the driver on your hardware. Analyze the logs to verify that the initialization sequence and command responses are correct. \ No newline at end of file From 6b456f7088a40e115c85b2893288ca9498f0cd0b Mon Sep 17 00:00:00 2001 From: Cheng Li Date: Mon, 17 Nov 2025 14:18:07 +1100 Subject: [PATCH 48/48] Revert one changes for the heap size in the example. Signed-off-by: Cheng Li --- drivers/blk/mmc/core/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/blk/mmc/core/src/main.rs b/drivers/blk/mmc/core/src/main.rs index aed1ccc33..1c753c737 100644 --- a/drivers/blk/mmc/core/src/main.rs +++ b/drivers/blk/mmc/core/src/main.rs @@ -63,7 +63,7 @@ fn create_dummy_waker() -> Waker { unsafe { Waker::from_raw(raw_waker) } } -#[protection_domain(heap_size = 0x1000)] +#[protection_domain(heap_size = 0x10000)] fn init() -> impl Handler { unsafe { sdmmc_protocol::sdmmc_os::set_logger(&SERIAL).unwrap();