diff --git a/bouffalo-hal/Cargo.toml b/bouffalo-hal/Cargo.toml index b898804..b1928c3 100644 --- a/bouffalo-hal/Cargo.toml +++ b/bouffalo-hal/Cargo.toml @@ -22,6 +22,7 @@ nb = "1.1.0" embedded-hal-027 = { package = "embedded-hal", version = "0.2.7" } embedded-io-async = "0.6.1" atomic-waker = "1.1.2" +embedded-sdmmc = "0.8.1" [dev-dependencies] memoffset = "0.9.0" diff --git a/bouffalo-hal/src/glb/v2.rs b/bouffalo-hal/src/glb/v2.rs index 44679cf..8d906c4 100644 --- a/bouffalo-hal/src/glb/v2.rs +++ b/bouffalo-hal/src/glb/v2.rs @@ -19,22 +19,25 @@ pub struct RegisterBlock { _reserved3: [u8; 0x1c], /// Pulse Width Modulation configuration register. pub pwm_config: RW, - _reserved4: [u8; 0x33b], + _reserved4: [u8; 0x25c], + /// SDH configuration register. + pub sdh_config: RW, + _reserved5: [u8; 0xdd], pub param_config: RW, - _reserved5: [u8; 0x70], + _reserved6: [u8; 0x70], // TODO: clock_config_0, clock_config_2, clock_config_3 registers /// Clock generation configuration 1. pub clock_config_1: RW, - _reserved6: [u8; 0x148], + _reserved7: [u8; 0x148], /// LDO12UHS config. pub ldo12uhs_config: RW, - _reserved7: [u8; 0x1f0], + _reserved8: [u8; 0x1f0], /// Generic Purpose Input/Output config. pub gpio_config: [RW; 46], - _reserved8: [u8; 0x148], + _reserved9: [u8; 0x148], /// Read value from Generic Purpose Input/Output pads. pub gpio_input: [RO; 2], - _reserved9: [u8; 0x18], + _reserved10: [u8; 0x18], /// Write value to Generic Purpose Input/Output pads. pub gpio_output: [RW; 2], /// Set pin output value to high. @@ -380,6 +383,53 @@ impl ParamConfig { } } +/// SDH configuration register. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[repr(transparent)] +pub struct SdhConfig(u16); + +impl SdhConfig { + const SDH_CLK_EN: u16 = 0x1 << 13; + const SDH_CLK_SEL: u16 = 0x1 << 12; + const SDH_CLK_DIV_LEN: u16 = 0x7 << 9; + + /// Enable SDH clock. + #[inline] + pub const fn enable_sdh_clk(self) -> Self { + Self((self.0 & !Self::SDH_CLK_EN) | (Self::SDH_CLK_EN & (1 << 13))) + } + /// Disable SDH clock. + #[inline] + pub const fn disable_sdh_clk(self) -> Self { + Self((self.0 & !Self::SDH_CLK_EN) | (Self::SDH_CLK_EN & (0 << 13))) + } + // If SDH clock is enabled. + #[inline] + pub const fn is_sdh_clk_enabled(self) -> bool { + (self.0 & Self::SDH_CLK_EN) >> 13 == 1 + } + /// Set SDH clock select. + #[inline] + pub const fn set_sdh_clk_sel(self, val: u8) -> Self { + Self((self.0 & !Self::SDH_CLK_SEL) | (Self::SDH_CLK_SEL & ((val as u16) << 12))) + } + /// Get SDH clock select. + #[inline] + pub const fn sdh_clk_sel(self) -> u8 { + ((self.0 & Self::SDH_CLK_SEL) >> 12) as u8 + } + /// Set SDH clock divider length. + #[inline] + pub const fn set_sdh_clk_div_len(self, val: u8) -> Self { + Self((self.0 & !Self::SDH_CLK_DIV_LEN) | (Self::SDH_CLK_DIV_LEN & ((val as u16) << 9))) + } + /// Get SDH clock divider length. + #[inline] + pub const fn sdh_clk_div_len(self) -> u8 { + ((self.0 & Self::SDH_CLK_DIV_LEN) >> 9) as u8 + } +} + /// Mode for Serial Peripheral Interface Bus. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] @@ -798,8 +848,8 @@ mod tests { use super::{ Drive, Function, GpioConfig, I2cClockSource, I2cConfig, InterruptMode, Mode, Pull, - PwmConfig, PwmSignal0, PwmSignal1, RegisterBlock, SpiConfig, UartConfig, UartMuxGroup, - UartSignal, + PwmConfig, PwmSignal0, PwmSignal1, RegisterBlock, SdhConfig, SpiConfig, UartConfig, + UartMuxGroup, UartSignal, }; use memoffset::offset_of; @@ -810,6 +860,7 @@ mod tests { assert_eq!(offset_of!(RegisterBlock, i2c_config), 0x180); assert_eq!(offset_of!(RegisterBlock, spi_config), 0x1b0); assert_eq!(offset_of!(RegisterBlock, pwm_config), 0x1d0); + assert_eq!(offset_of!(RegisterBlock, sdh_config), 0x430); assert_eq!(offset_of!(RegisterBlock, param_config), 0x510); assert_eq!(offset_of!(RegisterBlock, clock_config_1), 0x584); assert_eq!(offset_of!(RegisterBlock, ldo12uhs_config), 0x6d0); @@ -1070,4 +1121,24 @@ mod tests { assert_eq!(config.0, 0x00000002); assert_eq!(config.signal_1(), PwmSignal1::BrushlessDcMotor); } + + #[test] + fn struct_sdh_config_functions() { + let mut val = SdhConfig(0x0); + val = val.enable_sdh_clk(); + assert!(val.is_sdh_clk_enabled()); + assert_eq!(val.0, 0x2000); + val = val.disable_sdh_clk(); + assert!(!val.is_sdh_clk_enabled()); + assert_eq!(val.0, 0x0000); + + val = val.set_sdh_clk_sel(1); + assert_eq!(val.sdh_clk_sel(), 1); + assert_eq!(val.0, 0x1000); + + val = SdhConfig(0x0); + val = val.set_sdh_clk_div_len(0x7); + assert_eq!(val.sdh_clk_div_len(), 0x7); + assert_eq!(val.0, 0x0E00); + } } diff --git a/bouffalo-hal/src/sdio.rs b/bouffalo-hal/src/sdio.rs index 714d4db..5ac5dd9 100644 --- a/bouffalo-hal/src/sdio.rs +++ b/bouffalo-hal/src/sdio.rs @@ -1,5 +1,11 @@ //! Secure Digital Input/Output peripheral. +use crate::glb; +use crate::gpio::{self, Alternate}; +use core::arch::asm; +use core::ops::Deref; +use embedded_io::Write; +use embedded_sdmmc::{Block, BlockDevice, BlockIdx}; use volatile_register::RW; /// Secure Digital Input/Output peripheral registers. @@ -26,7 +32,7 @@ pub struct RegisterBlock { /// Host Control 1 Register. pub host_control_1: RW, /// Power Control Register. - pub powercontrol: RW, + pub power_control: RW, /// Block Gap Control Register. pub block_gap: RW, /// Register which is mandatory for the Host Controller. @@ -234,6 +240,8 @@ pub enum AutoCMDMode { CMD12 = 1, /// Auto CMD23 Enable. CMD23 = 2, + /// Set this bit to zero. + None = 0, } impl TransferMode { @@ -279,7 +287,8 @@ impl TransferMode { pub const fn auto_cmd_mode(self) -> AutoCMDMode { match (self.0 & Self::AUTO_CMD) >> 2 { 1 => AutoCMDMode::CMD12, - _ => AutoCMDMode::CMD23, + 2 => AutoCMDMode::CMD23, + _ => AutoCMDMode::None, } } /// Enable Block Count register. @@ -322,21 +331,22 @@ pub struct Command(u16); /// CMD Type. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum CmdType { - Other, + Normal, Suspend, Resume, Abort, + Empty, } -/// Response Type +/// Response Type. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum ResponseType { /// No Response. NoResponse, - /// Response Length 136 + /// Response Length 136. ResponseLen136, - /// Response Length 48 + /// Response Length 48. ResponseLen48, - /// Response Length 48 check Busy after response + /// Response Length 48 check Busy after response. ResponseLen48Check, } @@ -350,12 +360,12 @@ impl Command { /// These bits shall be set to the command number (CMD0-63, ACMD0-63) that is specified in bits 45-40 of the Command-Format in the Physical Layer Specification and SDIO Card Specification. #[inline] - pub const fn set_cmd_num(self, val: u16) -> Self { - Self((self.0 & Self::CMD_INDEX) | (Self::CMD_INDEX & (val << 8))) + pub const fn set_cmd_idx(self, val: u16) -> Self { + Self((self.0 & !Self::CMD_INDEX) | (Self::CMD_INDEX & (val << 8))) } /// Get command number. #[inline] - pub const fn cmd_num(self) -> u16 { + pub const fn cmd_idx(self) -> u16 { (self.0 & Self::CMD_INDEX) >> 8 } /// Set command type. @@ -367,10 +377,11 @@ impl Command { #[inline] pub const fn cmd_type(self) -> CmdType { match (self.0 & Self::CMD_TYPE) >> 6 { + 0 => CmdType::Normal, 1 => CmdType::Suspend, 2 => CmdType::Resume, 3 => CmdType::Abort, - _ => CmdType::Other, + _ => CmdType::Empty, } } /// Set this bit to 1 to indicate that data is present and shall be transferred using the DAT line. @@ -589,7 +600,7 @@ pub enum CardSignal { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum BusWidthMode { /// 8-bit Bus Width. - SelectByDataTrabsferWidth, + SelectByDataTransferWidth, /// Bus Width is Selected by Data Transfer Width. EightBitWidth, } @@ -675,7 +686,7 @@ impl HostControl1 { pub const fn bus_width(self) -> BusWidthMode { match (self.0 & Self::EXTEND_DATA) >> 5 { 1 => BusWidthMode::EightBitWidth, - _ => BusWidthMode::SelectByDataTrabsferWidth, + _ => BusWidthMode::SelectByDataTransferWidth, } } /// Set DMA mode. @@ -2250,7 +2261,7 @@ impl HostControl2 { pub const fn enable_preset_val(self) -> Self { Self((self.0 & !Self::PRESET_VAL_EN) | (Self::PRESET_VAL_EN & (1 << 15))) } - // Disable Preset Value. + /// Disable Preset Value. #[inline] pub const fn disable_preset_val(self) -> Self { Self((self.0 & !Self::PRESET_VAL_EN) | (Self::PRESET_VAL_EN & (0 << 15))) @@ -2265,7 +2276,7 @@ impl HostControl2 { pub const fn enable_async_int(self) -> Self { Self((self.0 & !Self::ASYNCHRONOUS_INT_EN) | (Self::ASYNCHRONOUS_INT_EN & (1 << 14))) } - // Disable Asynchronous Interrupt. + /// Disable Asynchronous Interrupt. #[inline] pub const fn disable_async_int(self) -> Self { Self((self.0 & !Self::ASYNCHRONOUS_INT_EN) | (Self::ASYNCHRONOUS_INT_EN & (0 << 14))) @@ -2382,7 +2393,7 @@ impl Capabilities { const BASE_CLK_FREQ: u64 = 0xFF << 40; const TIMEOUT_CLK_UNIT: u64 = 0x1 << 39; const TIMEOUT_CLK_FREQ: u64 = 0x3F << 32; - // Capabilities_2 + // Capabilities_2. const CLK_MULTIPLIER: u64 = 0xFF << 16; const RE_TUNING_MODES: u64 = 0x3 << 14; const USE_TUNING: u64 = 0x1 << 13; @@ -3008,113 +3019,114 @@ impl PresetValue { ((self.0 & Self::DDR50_SDCLK_FREQ_SEL_VAL) >> 112) as u16 } - // Get Driver Strength Value For SDR104. + /// Get Driver Strength Value For SDR104. #[inline] pub const fn sdr104_drv_strength_val(self) -> u16 { ((self.0 & Self::SDR104_DRV_STRENGTH_VAL) >> 110) as u16 } - // Get Clock Generator Frequency Select Value For SDR104. + /// Get Clock Generator Frequency Select Value For SDR104. #[inline] pub const fn sdr104_clkgen_sel_val(self) -> u16 { ((self.0 & Self::SDR104_CLKGEN_SEL_VAL) >> 106) as u16 } - // Get SD Clock Generator Frequency Select Value For SDR104. + /// Get SD Clock Generator Frequency Select Value For SDR104. #[inline] pub const fn sdr104_sdclk_freq_clk_val(self) -> u16 { ((self.0 & Self::SDR104_SDCLK_FREQ_SEL_VAL) >> 96) as u16 } - // Get Driver Strength Value For SDR50. + /// Get Driver Strength Value For SDR50. #[inline] pub const fn sdr50_drv_strength_val(self) -> u16 { ((self.0 & Self::SDR50_DRV_STRENGTH_VAL) >> 94) as u16 } - // Get Clock Generator Frequency Select Value For SDR50. + /// Get Clock Generator Frequency Select Value For SDR50. #[inline] pub const fn sdr50_clkgen_sel_val(self) -> u16 { ((self.0 & Self::SDR50_CLKGEN_SEL_VAL) >> 90) as u16 } - // Get SD Clock Generator Frequency Select Value For SDR50. + /// Get SD Clock Generator Frequency Select Value For SDR50. #[inline] pub const fn sdr50_sdclk_freq_clk_val(self) -> u16 { ((self.0 & Self::SDR50_SDCLK_FREQ_SEL_VAL) >> 80) as u16 } - // Get Driver Strength Value For SDR25. + /// Get Driver Strength Value For SDR25. #[inline] pub const fn sdr25_drv_strength_val(self) -> u16 { ((self.0 & Self::SDR25_DRV_STRENGTH_VAL) >> 78) as u16 } - // Get Clock Generator Frequency Select Value For SDR25. + + /// Get Clock Generator Frequency Select Value For SDR25. #[inline] pub const fn sdr25_clkgen_sel_val(self) -> u16 { ((self.0 & Self::SDR25_CLKGEN_SEL_VAL) >> 74) as u16 } - // Get SD Clock Generator Frequency Select Value For SDR25. + /// Get SD Clock Generator Frequency Select Value For SDR25. #[inline] pub const fn sdr25_sdclk_freq_clk_val(self) -> u16 { ((self.0 & Self::SDR25_SDCLK_FREQ_SEL_VAL) >> 64) as u16 } - // Get Driver Strength Value For SDR12. + /// Get Driver Strength Value For SDR12. #[inline] pub const fn sdr12_drv_strength_val(self) -> u16 { ((self.0 & Self::SDR12_DRV_STRENGTH_VAL) >> 62) as u16 } - // Get Clock Generator Frequency Select Value For SDR12. + /// Get Clock Generator Frequency Select Value For SDR12. #[inline] pub const fn sdr12_clkgen_sel_val(self) -> u16 { ((self.0 & Self::SDR12_CLKGEN_SEL_VAL) >> 58) as u16 } - // Get SD Clock Generator Frequency Select Value For SDR12. + /// Get SD Clock Generator Frequency Select Value For SDR12. #[inline] pub const fn sdr12_sdclk_freq_clk_val(self) -> u16 { ((self.0 & Self::SDR12_SDCLK_FREQ_SEL_VAL) >> 48) as u16 } - // Get Driver Strength Value For High Speed. + /// Get Driver Strength Value For High Speed. #[inline] pub const fn hs_drv_strength_val(self) -> u16 { ((self.0 & Self::HS_DRV_STRENGTH_VAL) >> 46) as u16 } - // Get Clock Generator Frequency Select Value For High Speed. + /// Get Clock Generator Frequency Select Value For High Speed. #[inline] pub const fn hs_clkgen_sel_val(self) -> u16 { ((self.0 & Self::HS_CLKGEN_SEL_VAL) >> 42) as u16 } - // Get SD Clock Generator Frequency Select Value For High Speed. + /// Get SD Clock Generator Frequency Select Value For High Speed. #[inline] pub const fn hs_sdclk_freq_clk_val(self) -> u16 { ((self.0 & Self::HS_SDCLK_FREQ_SEL_VAL) >> 32) as u16 } - // Get Driver Strength Value For Default Speed. + /// Get Driver Strength Value For Default Speed. #[inline] pub const fn default_drv_strength_val(self) -> u16 { ((self.0 & Self::DEFAULT_DRV_STRENGTH_VAL) >> 30) as u16 } - // Get Clock Generator Frequency Select Value For Default Speed. + /// Get Clock Generator Frequency Select Value For Default Speed. #[inline] pub const fn default_clkgen_sel_val(self) -> u16 { ((self.0 & Self::DEFAULT_CLKGEN_SEL_VAL) >> 26) as u16 } - // Get SD Clock Generator Frequency Select Value For Default Speed. + /// Get SD Clock Generator Frequency Select Value For Default Speed. #[inline] pub const fn default_sdclk_freq_clk_val(self) -> u16 { ((self.0 & Self::DEFAULT_SDCLK_FREQ_SEL_VAL) >> 16) as u16 } - // Get Driver Strength Value For Initialization. + /// Get Driver Strength Value For Initialization. #[inline] pub const fn init_drv_strength_val(self) -> u16 { ((self.0 & Self::INIT_DRV_STRENGTH_VAL) >> 14) as u16 } - // Get Clock Generator Frequency Select Value For Initialization. + /// Get Clock Generator Frequency Select Value For Initialization. #[inline] pub const fn init_clkgen_sel_val(self) -> u16 { ((self.0 & Self::INIT_CLKGEN_SEL_VAL) >> 10) as u16 } - // Get SD Clock Generator Frequency Select Value For Initialization. + /// Get SD Clock Generator Frequency Select Value For Initialization. #[inline] pub const fn init_sdclk_freq_clk_val(self) -> u16 { (self.0 & Self::INIT_SDCLK_FREQ_SEL_VAL) as u16 @@ -3228,6 +3240,7 @@ impl SlotInterruptStatus { #[repr(transparent)] pub struct HostControllerVersion(u16); +/// SD Host Specification Version. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum SpecificVersion { /// SD Host Specification Version 1.00. @@ -3489,10 +3502,20 @@ pub struct TXConfiguration(u32); impl TXConfiguration { const _TX_MUX_SEL: u32 = 0x1 << 31; - const _TX_INT_CLK_SEL: u32 = 0x1 << 30; + const TX_INT_CLK_SEL: u32 = 0x1 << 30; const _TX_HOLD_DELAY1: u32 = 0x3FF << 16; const _TX_HOLD_DELAY0: u32 = 0x3FF; + /// Set tx interrupt clock select. + #[inline] + pub const fn set_tx_int_clk_sel(self, val: u8) -> Self { + Self((self.0 & !Self::TX_INT_CLK_SEL) | (Self::TX_INT_CLK_SEL & ((val as u32) << 30))) + } + /// Get tx interrupt clock select. + #[inline] + pub const fn tx_int_clk_sel(self) -> u8 { + ((self.0 & Self::TX_INT_CLK_SEL) >> 30) as u8 + } // TODO } @@ -3510,6 +3533,431 @@ impl TUNINGConfiguration { // TODO } +/// SDH transfer flag. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum SDHTransFlag { + None = 0x00000000, + EnDma = 0x00000001, // Enable DMA. + EnBlkCount = 0x00000002, // Enable block count. + EnAutoCmd12 = 0x00000004, // Enable auto CMD12. + EnAutoCmd23 = 0x00000008, // Enable auto CMD23. + ReadData = 0x00000010, // Enable read data. + MultiBlk = 0x00000020, // Enable multi-block data operation. + Resp136Bits = 0x00010000, // Response is 136 bits length. + Resp48Bits = 0x00020000, // Response is 48 bits length. + Resp48BitsWithBusy = 0x00030000, // Response is 48 bits length with busy status. + EnCrcCheck = 0x00080000, // Enable CRC check. + EnIndexCheck = 0x00100000, // Enable index check. + DataPresent = 0x00200000, // Data present. + Suspend = 0x00400000, // Suspend command. + Resume = 0x00800000, // Resume command. + Abort = 0x00C00000, // Abort command. +} + +/// SDH response type. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum SDHResp { + None, + R1, + R5, + R6, + R7, + R1B, + R5B, + R2, + R3, + R4, +} + +/// Sleep for n milliseconds. +pub fn sleep_ms(n: u32) { + for _ in 0..n * 125 { + unsafe { asm!("nop") } + } +} + +/// Managed Secure Digital Host Controller peripheral. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Sdh { + sdh: SDH, + pads: PADS, + block_count: u32, +} + +impl, PADS, const I: usize> Sdh { + /// Create a new instance of the SDH peripheral. + #[inline] + pub fn new(sdh: SDH, pads: PADS) -> Self + where + PADS: Pads, + { + let block_count = 0; + Self { + sdh, + pads, + block_count, + } + } + + /// Initialize the SDH peripheral (enable debug to print card info). + #[inline] + pub fn init(&mut self, w: &mut W, glb: &GLB, debug: bool) + where + GLB: Deref, + { + // SDH_RESET. + unsafe { + self.sdh.software_reset.modify(|val| val.reset_all()); + } + while !self.sdh.software_reset.read().is_reset_all_finished() { + // Wait for software reset finished. + core::hint::spin_loop() + } + + unsafe { + // GLB_Set_SDH_CLK. + glb.sdh_config.modify(|val| { + val.set_sdh_clk_sel(0) // GLB_REG_SDH_CLK_SEL. + .set_sdh_clk_div_len(7) // GLB_REG_SDH_CLK_DIV. + .enable_sdh_clk() // GLB_REG_SDH_CLK_EN. + }); + + // SDH_Ctrl_Init. + self.sdh.clock_control.modify(|val| { + val.set_sd_clk_freq(0) // SDH_SD_FREQ_SEL_LO. + .set_sd_clk_freq_upper(0) // SDH_SD_FREQ_SEL_HI. + .set_clk_gen_mode(ClkGenMode::DividedClk) // SDH_CLK_GEN_SEL. + .enable_internal_clk() // SDH_INT_CLK_EN. + .enable_sd_clk() // SDH_SD_CLK_EN. + }); + } + while !self.sdh.clock_control.read().is_sd_clk_enabled() { + // Wait for sd clock enabled. + core::hint::spin_loop() + } + unsafe { + // SDH_DMA_EN. + self.sdh.transfer_mode.modify(|val| val.disable_dma()); + + self.sdh.host_control_1.modify(|val| { + val.set_bus_width(BusWidthMode::SelectByDataTransferWidth) // SDH_EX_DATA_WIDTH. + .set_transfer_width(TransferWidth::OneBitMode) // SDH_DATA_WIDTH. + .set_speed_mode(SpeedMode::HighSpeed) // SDH_HI_SPEED_EN. + }); + + // SDH_SD_BUS_VLT. + self.sdh + .power_control + .modify(|val| val.set_bus_voltage(BusVoltage::V3_3)); + + // SDH_TX_INT_CLK_SEL. + self.sdh + .tx_configuration + .modify(|val| val.set_tx_int_clk_sel(1)); + + // SDH enable interrupt. + self.sdh + .normal_interrupt_status_enable + .modify(|val| val.enable_buffer_read_ready()); + + // SDH_Set_Timeout. + self.sdh + .timeout_control + .modify(|val| val.set_timeout_val(0x0e)); + + // SDH_Powon. + self.sdh.power_control.modify(|val| val.enable_bus_power()); + } + + // Sdcard idle. + self.send_command(SDHResp::None, CmdType::Normal, 0, 0, false); + sleep_ms(100); + + // Send CMD8. + self.send_command(SDHResp::R7, CmdType::Normal, 8, 0x1AA, false); + sleep_ms(100); + let data = self.get_resp(); + if data != 0x1AA { + writeln!( + *w, + "unexpected response to CMD8: {:#010X}, expected 0x1AA", + data + ) + .ok(); + loop {} + } + + loop { + const OCR_NBUSY: u32 = 0x80000000; + const OCR_VOLTAGE_MASK: u32 = 0x007FFF80; + const OCR_HCS: u32 = 0x40000000; + self.send_command(SDHResp::R1, CmdType::Normal, 55, 0, false); + sleep_ms(100); + self.send_command( + SDHResp::R3, + CmdType::Normal, + 41, + OCR_VOLTAGE_MASK & 0x00ff8000 | OCR_HCS, + false, + ); + sleep_ms(100); + let ocr = self.get_resp(); + if (ocr as u32 & OCR_NBUSY) == OCR_NBUSY { + break; + } + sleep_ms(100); + } + + // Send CMD2 to get CID. + self.send_command(SDHResp::R2, CmdType::Normal, 2, 0, false); + sleep_ms(100); + let cid = self.get_resp(); + if debug { + writeln!(*w, "cid: {:#034X}", cid).ok(); + } + + // Send CMD3 to get RCA. + self.send_command(SDHResp::R6, CmdType::Normal, 3, 0, false); + sleep_ms(100); + let rca = self.get_resp() as u32 >> 16; + if debug { + writeln!(*w, "rca: {:#010X}", rca).ok(); + } + + // Send CMD9 to get CSD. + self.send_command(SDHResp::R2, CmdType::Normal, 9, rca << 16, false); + sleep_ms(100); + let csd_raw = self.get_resp(); + let (csd_structure, c_size) = parse_csd_v2(csd_raw); + if csd_structure != 1 { + writeln!(*w, "unexpected CSD: {:#034X}", csd_raw).ok(); + loop {} + } + if debug { + writeln!(*w, "csd: {:#034X}, c_size: {}", csd_raw, c_size).ok(); + } + + let block_size = 512; + self.block_count = (c_size + 1) * 1024; + + // Send CMD7 to select card. + self.send_command(SDHResp::R1B, CmdType::Normal, 7, rca << 16, false); + sleep_ms(100); + + // Set 1 data len, CMD55 -> ACMD6. + self.send_command(SDHResp::R1, CmdType::Normal, 55, rca << 16, false); + sleep_ms(100); + self.send_command(SDHResp::R1, CmdType::Normal, 6, 0x0, false); + sleep_ms(100); + + let kb_size = (self.block_count as f64) * (block_size as f64) / 1024.0; + let mb_size = kb_size / 1024.0; + let gb_size = mb_size / 1024.0; + + if debug { + if kb_size < 1024.0 { + writeln!(*w, "sdcard init done, size: {:.2} KB", kb_size).ok(); + } else if mb_size < 1024.0 { + writeln!(*w, "sdcard init done, size: {:.2} MB", mb_size).ok(); + } else { + writeln!(*w, "sdcard init done, size: {:.2} GB", gb_size).ok(); + } + } + } + + /// Send command to sdcard. + #[inline] + pub fn send_command( + &self, + resp_type: SDHResp, + cmd_type: CmdType, + cmd_idx: u32, + argument: u32, + has_data: bool, + ) { + let mut flag = SDHTransFlag::None as u32; + if has_data { + flag |= SDHTransFlag::DataPresent as u32; + } + match resp_type { + SDHResp::None => {} + SDHResp::R1 | SDHResp::R5 | SDHResp::R6 | SDHResp::R7 => { + flag |= SDHTransFlag::Resp48Bits as u32 + | SDHTransFlag::EnCrcCheck as u32 + | SDHTransFlag::EnIndexCheck as u32; + } + SDHResp::R1B | SDHResp::R5B => { + flag |= SDHTransFlag::Resp48BitsWithBusy as u32 + | SDHTransFlag::EnCrcCheck as u32 + | SDHTransFlag::EnIndexCheck as u32; + } + SDHResp::R2 => { + flag |= SDHTransFlag::Resp136Bits as u32 | SDHTransFlag::EnCrcCheck as u32; + } + SDHResp::R3 | SDHResp::R4 => { + flag |= SDHTransFlag::Resp48Bits as u32; + } + } + + unsafe { + self.sdh.argument.write(Argument(argument)); + self.sdh.command.write( + Command((flag >> 16) as u16) + .set_cmd_type(cmd_type) + .set_cmd_idx(cmd_idx as u16), + ) + } + } + + /// Get response from sdcard. + #[inline] + pub fn get_resp(&self) -> u128 { + self.sdh.response.read().response() + } + + /// Read block from sdcard. + #[inline] + pub fn read_block(&self, block: &mut Block, block_idx: u32) { + unsafe { + // SDH_SD_TRANSFER_MODE. + self.sdh.transfer_mode.modify(|val| { + val.set_data_transfer_mode(DataTransferMode::MISO) // SDH_TO_HOST_DIR. + .set_auto_cmd_mode(AutoCMDMode::None) // SDH_AUTO_CMD_EN. + }); + + // Block_size. + self.sdh + .block_size + .modify(|val| val.set_transfer_block(512)); + + // Block_count. + self.sdh.block_count.modify(|val| val.set_blocks_count(1)); + + // SDH_ClearIntStatus(SDH_INT_BUFFER_READ_READY). + self.sdh + .normal_interrupt_status + .write(NormalInterruptStatus(0x00000020)); + } + self.send_command(SDHResp::R1, CmdType::Normal, 17, block_idx, true); + while !self + .sdh + .normal_interrupt_status + .read() + .is_buffer_read_ready() + { + // SDH_INT_BUFFER_READ_READY. + // Wait for buffer read ready. + core::hint::spin_loop() + } + for j in 0..Block::LEN / 4 { + let val = self.sdh.buffer_data_port.read().buffer_data(); + block[j * 4 + 0] = (val >> 0) as u8; + block[j * 4 + 1] = (val >> 8) as u8; + block[j * 4 + 2] = (val >> 16) as u8; + block[j * 4 + 3] = (val >> 24) as u8; + } + } + + /// Release the SDH instance and return the pads. + #[inline] + pub fn free(self) -> (SDH, PADS) { + (self.sdh, self.pads) + } +} + +impl, PADS, const I: usize> BlockDevice for Sdh { + type Error = core::convert::Infallible; + fn read( + &self, + blocks: &mut [Block], + start_block_idx: BlockIdx, + _reason: &str, + ) -> Result<(), Self::Error> { + for (i, block) in blocks.iter_mut().enumerate() { + self.read_block(block, start_block_idx.0 + i as u32); + } + Ok(()) + } + fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> { + unimplemented!(); + } + fn num_blocks(&self) -> Result { + Ok(embedded_sdmmc::BlockCount(self.block_count)) + } +} + +/// Parse CSD version 2.0. +pub fn parse_csd_v2(csd: u128) -> (u32, u32) { + let csd_structure = (((csd >> (32 * 3)) & 0xC00000) >> 22) as u32; + let c_size = (((csd >> 32) & 0x3FFFFF00) >> 8) as u32; + (csd_structure, c_size) +} + +/// Valid SDH pads. +pub trait Pads {} + +impl< + 'a, + 'b, + 'c, + 'd, + 'e, + 'f, + const N1: usize, + const N2: usize, + const N3: usize, + const N4: usize, + const N5: usize, + const N6: usize, + > Pads<1> + for ( + Alternate<'a, N1, gpio::Sdh>, + Alternate<'b, N2, gpio::Sdh>, + Alternate<'c, N3, gpio::Sdh>, + Alternate<'d, N4, gpio::Sdh>, + Alternate<'e, N5, gpio::Sdh>, + Alternate<'f, N6, gpio::Sdh>, + ) +where + Alternate<'a, N1, gpio::Sdh>: HasClkSignal, + Alternate<'b, N2, gpio::Sdh>: HasCmdSignal, + Alternate<'c, N3, gpio::Sdh>: HasDat0Signal, + Alternate<'d, N4, gpio::Sdh>: HasDat1Signal, + Alternate<'e, N5, gpio::Sdh>: HasDat2Signal, + Alternate<'f, N6, gpio::Sdh>: HasDat3Signal, +{ +} + +/// Check if target gpio `Pin` is internally connected to SDH clock signal. +pub trait HasClkSignal {} + +impl<'a> HasClkSignal for Alternate<'a, 0, gpio::Sdh> {} + +/// Check if target gpio `Pin` is internally connected to SDH command signal. +pub trait HasCmdSignal {} + +impl<'a> HasCmdSignal for Alternate<'a, 1, gpio::Sdh> {} + +/// Check if target gpio `Pin` is internally connected to SDH data 0 signal. +pub trait HasDat0Signal {} + +impl<'a> HasDat0Signal for Alternate<'a, 2, gpio::Sdh> {} + +/// Check if target gpio `Pin` is internally connected to SDH data 1 signal. +pub trait HasDat1Signal {} + +impl<'a> HasDat1Signal for Alternate<'a, 3, gpio::Sdh> {} + +/// Check if target gpio `Pin` is internally connected to SDH data 2 signal. +pub trait HasDat2Signal {} + +impl<'a> HasDat2Signal for Alternate<'a, 4, gpio::Sdh> {} + +/// Check if target gpio `Pin` is internally connected to SDH data 3 signal. +pub trait HasDat3Signal {} + +impl<'a> HasDat3Signal for Alternate<'a, 5, gpio::Sdh> {} + #[cfg(test)] mod tests { use super::RegisterBlock; @@ -3523,7 +3971,7 @@ mod tests { NormalInterruptStatus, NormalInterruptStatusEnable, PowerControl, PresentState, PresetValue, Response, ResponseType, SDExtraParameters, SPIMode, SharedBusControl, SlotInterruptStatus, SlotType, SoftwareReset, SpecificVersion, SpeedMode, SystemAddress, - TimeoutControl, TransferMode, TransferWidth, WakeupControl, + TXConfiguration, TimeoutControl, TransferMode, TransferWidth, WakeupControl, }; use memoffset::offset_of; @@ -3539,7 +3987,7 @@ mod tests { assert_eq!(offset_of!(RegisterBlock, buffer_data_port), 0x20); assert_eq!(offset_of!(RegisterBlock, present_state), 0x24); assert_eq!(offset_of!(RegisterBlock, host_control_1), 0x28); - assert_eq!(offset_of!(RegisterBlock, powercontrol), 0x29); + assert_eq!(offset_of!(RegisterBlock, power_control), 0x29); assert_eq!(offset_of!(RegisterBlock, block_gap), 0x2a); assert_eq!(offset_of!(RegisterBlock, wakeup_control), 0x2b); assert_eq!(offset_of!(RegisterBlock, clock_control), 0x2c); @@ -3682,8 +4130,8 @@ mod tests { fn struct_command_functions() { let mut val = Command(0x0); - val = val.set_cmd_num(0x3F); - assert_eq!(val.cmd_num(), 0x3F); + val = val.set_cmd_idx(0x3F); + assert_eq!(val.cmd_idx(), 0x3F); assert_eq!(val.0, 0x3F00); val = Command(0x0); @@ -3696,8 +4144,8 @@ mod tests { val = val.set_cmd_type(CmdType::Suspend); assert_eq!(val.cmd_type(), CmdType::Suspend); assert_eq!(val.0, 0x0040); - val = val.set_cmd_type(CmdType::Other); - assert_eq!(val.cmd_type(), CmdType::Other); + val = val.set_cmd_type(CmdType::Normal); + assert_eq!(val.cmd_type(), CmdType::Normal); assert_eq!(val.0, 0x0000); val = val.set_data_present(); @@ -3818,7 +4266,7 @@ mod tests { val = val.set_bus_width(BusWidthMode::EightBitWidth); assert_eq!(val.bus_width(), BusWidthMode::EightBitWidth); assert_eq!(val.0, 0x20); - val = val.set_bus_width(BusWidthMode::SelectByDataTrabsferWidth); + val = val.set_bus_width(BusWidthMode::SelectByDataTransferWidth); assert_eq!(val.0, 0x00); val = val.set_dma_mode(DMAMode::ADMA2); @@ -3851,7 +4299,7 @@ mod tests { } #[test] - fn struct_powercontrol_functions() { + fn struct_power_control_functions() { let mut val = PowerControl(0x0); val = val.set_bus_voltage(BusVoltage::V1_8); @@ -5061,6 +5509,10 @@ mod tests { #[test] fn struct_tx_configuration_functions() { + let mut val = TXConfiguration(0x0); + val = val.set_tx_int_clk_sel(0x1); + assert_eq!(val.tx_int_clk_sel(), 0x1); + assert_eq!(val.0, 0x4000_0000); // TODO } diff --git a/bouffalo-rt/src/soc/bl808.rs b/bouffalo-rt/src/soc/bl808.rs index 8e7b505..1c0f45d 100644 --- a/bouffalo-rt/src/soc/bl808.rs +++ b/bouffalo-rt/src/soc/bl808.rs @@ -835,6 +835,8 @@ pub struct Peripherals<'a> { pub mmglb: MMGLB, /// Pseudo Static Random Access Memory controller. pub psram: PSRAM, + /// Secure Digital High Capacity peripheral. + pub sdh: SDH, } soc! { @@ -858,6 +860,8 @@ soc! { pub struct LZ4D => 0x2000AD00, bouffalo_hal::lz4d::RegisterBlock; /// Hibernation control peripheral. pub struct HBN => 0x2000F000, bouffalo_hal::hbn::RegisterBlock; + /// Secure Digital High Capacity peripheral. + pub struct SDH => 0x20060000, bouffalo_hal::sdio::RegisterBlock; /// Ethernet Media Access Control peripheral. pub struct EMAC => 0x20070000, bouffalo_hal::emac::RegisterBlock; /// Universal Asynchronous Receiver/Transmitter 3 with fixed base address. @@ -910,6 +914,7 @@ pub fn __rom_init_params(xtal_hz: u32) -> (Peripherals<'static>, Clocks) { plic: PLIC { _private: () }, mmglb: MMGLB { _private: () }, psram: PSRAM { _private: () }, + sdh: SDH { _private: () }, }; let clocks = Clocks { xtal: Hertz(xtal_hz), diff --git a/examples/peripherals/sdh-demo/Cargo.toml b/examples/peripherals/sdh-demo/Cargo.toml index 4b049df..9ddc968 100644 --- a/examples/peripherals/sdh-demo/Cargo.toml +++ b/examples/peripherals/sdh-demo/Cargo.toml @@ -13,6 +13,7 @@ panic-halt = "1.0.0" embedded-time = "0.12.1" embedded-io = "0.6.1" embedded-sdmmc = "0.8.1" +embedded-hal = "1.0.0" [[bin]] name = "sdh-demo" diff --git a/examples/peripherals/sdh-demo/src/main.rs b/examples/peripherals/sdh-demo/src/main.rs index 4f886a0..18ad621 100644 --- a/examples/peripherals/sdh-demo/src/main.rs +++ b/examples/peripherals/sdh-demo/src/main.rs @@ -1,16 +1,13 @@ #![no_std] #![no_main] -use core::{arch::asm, ptr}; +use core::arch::asm; -use bouffalo_hal::{prelude::*, uart::Config}; +use bouffalo_hal::{prelude::*, sdio::Sdh, uart::Config}; use bouffalo_rt::{entry, Clocks, Peripherals}; -use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx, VolumeManager}; +use embedded_sdmmc::VolumeManager; use embedded_time::rate::*; use panic_halt as _; -use sdh::*; - -mod sdh; struct MyTimeSource {} @@ -23,32 +20,36 @@ impl embedded_sdmmc::TimeSource for MyTimeSource { #[entry] fn main(p: Peripherals, c: Clocks) -> ! { - // light up led + // Light up led. let mut led = p.gpio.io8.into_floating_output(); let mut led_state = PinState::Low; led.set_state(led_state).ok(); - // init serial + // Init serial. let tx = p.gpio.io14.into_uart(); let rx = p.gpio.io15.into_uart(); let sig2 = p.uart_muxes.sig2.into_transmit::<0>(); let sig3 = p.uart_muxes.sig3.into_receive::<0>(); let config = Config::default().set_baudrate(2000000.Bd()); - let mut serial = p.uart0.freerun(config, ((tx, sig2), (rx, sig3)), &c); + let mut serial = p + .uart0 + .freerun(config, ((tx, sig2), (rx, sig3)), &c) + .unwrap(); writeln!(serial, "Welcome to sdh-demo!").ok(); - // sdh gpio init - p.gpio.io0.into_sdh(); - p.gpio.io1.into_sdh(); - p.gpio.io2.into_sdh(); - p.gpio.io3.into_sdh(); - p.gpio.io4.into_sdh(); - p.gpio.io5.into_sdh(); - - // sdh init - let sdcard = sdh_init(&mut serial); + // Sdh gpio init. + let sdh_clk = p.gpio.io0.into_sdh(); + let sdh_cmd = p.gpio.io1.into_sdh(); + let sdh_d0 = p.gpio.io2.into_sdh(); + let sdh_d1 = p.gpio.io3.into_sdh(); + let sdh_d2 = p.gpio.io4.into_sdh(); + let sdh_d3 = p.gpio.io5.into_sdh(); + + // Sdh init. + let mut sdcard = Sdh::new(p.sdh, (sdh_clk, sdh_cmd, sdh_d0, sdh_d1, sdh_d2, sdh_d3)); + sdcard.init(&mut serial, &p.glb, true); let time_source = MyTimeSource {}; let mut volume_mgr = VolumeManager::new(sdcard, time_source); let volume_res = volume_mgr.open_raw_volume(embedded_sdmmc::VolumeIdx(0)); @@ -74,19 +75,7 @@ fn main(p: Peripherals, c: Clocks) -> ! { } } -#[inline] -pub(crate) fn set_bits(val: u32, pos: u32, len: u32, val_in: u32) -> u32 { - let mask = ((1 << len) - 1) << pos; - (val & !mask) | ((val_in << pos) & mask) -} - -#[inline] -pub(crate) fn is_bit_set(val: u32, pos: u32) -> bool { - (val & (1 << pos)) != 0 -} - -#[inline] -pub(crate) fn sleep_ms(n: u32) { +pub fn sleep_ms(n: u32) { for _ in 0..n * 125 { unsafe { asm!("nop") } } diff --git a/examples/peripherals/sdh-demo/src/sdh.rs b/examples/peripherals/sdh-demo/src/sdh.rs deleted file mode 100644 index e7188b7..0000000 --- a/examples/peripherals/sdh-demo/src/sdh.rs +++ /dev/null @@ -1,314 +0,0 @@ -//! sdh - -use crate::*; -use embedded_io::Write; - -const GLB_BASE: u32 = 0x20000000; -const SDH_BASE: u32 = 0x20060000; - -pub struct MySdCard { - block_count: u32, -} - -impl MySdCard { - fn read_block(&self, block: &mut Block, block_idx: u32) { - let mut tmp_val = read_memory16(SDH_BASE + 0x0C); // SDH_SD_TRANSFER_MODE - tmp_val = set_bits(tmp_val, 4, 1, 1); // SDH_TO_HOST_DIR - tmp_val = set_bits(tmp_val, 2, 2, 0); // SDH_AUTO_CMD_EN - tmp_val = set_bits(tmp_val, 5, 1, 0); // SDH_MULTI_BLK_SEL - write_memory16(SDH_BASE + 0x0C, tmp_val); - write_memory16(SDH_BASE + 0x04, 512); // block_size - write_memory16(SDH_BASE + 0x06, 1); // block_count - write_memory(SDH_BASE + 0x30, 0x00000020); // SDH_ClearIntStatus(SDH_INT_BUFFER_READ_READY) - sdh_send_command(SDHResp::R1, SDHCmdType::Normal, 17, block_idx, true); - tmp_val = read_memory(SDH_BASE + 0x30); - while !is_bit_set(tmp_val, 5) { - tmp_val = read_memory(SDH_BASE + 0x30); - } - for j in 0..Block::LEN / 4 { - let val = read_memory(SDH_BASE + 0x20); - block[j * 4 + 0] = (val >> 0) as u8; - block[j * 4 + 1] = (val >> 8) as u8; - block[j * 4 + 2] = (val >> 16) as u8; - block[j * 4 + 3] = (val >> 24) as u8; - } - } -} - -impl BlockDevice for MySdCard { - type Error = core::convert::Infallible; - fn read( - &self, - blocks: &mut [Block], - start_block_idx: BlockIdx, - _reason: &str, - ) -> Result<(), Self::Error> { - for (i, block) in blocks.iter_mut().enumerate() { - self.read_block(block, start_block_idx.0 + i as u32); - } - Ok(()) - } - fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> { - unimplemented!(); - } - fn num_blocks(&self) -> Result { - Ok(embedded_sdmmc::BlockCount(self.block_count)) - } -} - -pub fn sdh_init(w: &mut W) -> MySdCard { - let mut tmp_val; - - // SDH_RESET - tmp_val = read_memory16(SDH_BASE + 0x2e); - tmp_val = set_bits(tmp_val, 8, 1, 1); - write_memory16(SDH_BASE + 0x2e, tmp_val); - while is_bit_set(tmp_val, 8) { - tmp_val = read_memory16(SDH_BASE + 0x2e); - } - - // GLB_Set_SDH_CLK - tmp_val = read_memory(GLB_BASE + 0x430); - tmp_val = set_bits(tmp_val, 13, 1, 0); // GLB_REG_SDH_CLK_EN - write_memory(GLB_BASE + 0x430, tmp_val); - tmp_val = read_memory(GLB_BASE + 0x430); - tmp_val = set_bits(tmp_val, 12, 1, 0); // GLB_REG_SDH_CLK_SEL - tmp_val = set_bits(tmp_val, 9, 3, 7); // GLB_REG_SDH_CLK_DIV - write_memory(GLB_BASE + 0x430, tmp_val); - let mut tmp_val = read_memory(GLB_BASE + 0x430); - tmp_val = set_bits(tmp_val, 13, 1, 1); // GLB_REG_SDH_CLK_EN - write_memory(GLB_BASE + 0x430, tmp_val); - - // SDH_Ctrl_Init - tmp_val = read_memory16(SDH_BASE + 0x2c); - tmp_val = set_bits(tmp_val, 8, 8, 0); // SDH_SD_FREQ_SEL_LO - tmp_val = set_bits(tmp_val, 6, 2, 0); // SDH_SD_FREQ_SEL_HI - tmp_val = set_bits(tmp_val, 5, 1, 0); // SDH_CLK_GEN_SEL - tmp_val = set_bits(tmp_val, 0, 1, 1); // SDH_INT_CLK_EN - tmp_val = set_bits(tmp_val, 2, 1, 1); // SDH_SD_CLK_EN - write_memory16(SDH_BASE + 0x2c, tmp_val); - tmp_val = read_memory16(SDH_BASE + 0x2c); - while !is_bit_set(tmp_val, 1) { - tmp_val = read_memory16(SDH_BASE + 0x2c); - } - tmp_val = read_memory16(SDH_BASE + 0xc); - tmp_val = set_bits(tmp_val, 0, 1, 0); // SDH_DMA_EN - write_memory16(SDH_BASE + 0xc, tmp_val); - tmp_val = read_memory16(SDH_BASE + 0x28); - tmp_val = set_bits(tmp_val, 5, 1, 0); // SDH_EX_DATA_WIDTH - tmp_val = set_bits(tmp_val, 1, 1, 0); // SDH_DATA_WIDTH - tmp_val = set_bits(tmp_val, 2, 1, 1); // SDH_HI_SPEED_EN - tmp_val = set_bits(tmp_val, 9, 3, 7); // SDH_SD_BUS_VLT - write_memory16(SDH_BASE + 0x28, tmp_val); - tmp_val = read_memory(SDH_BASE + 0x118); - tmp_val = set_bits(tmp_val, 30, 1, 1); // SDH_TX_INT_CLK_SEL - write_memory(SDH_BASE + 0x118, tmp_val); - - // SDH Enable Interrupt - tmp_val = 0; - tmp_val = set_bits(tmp_val, 5, 1, 1); // SDH_INT_BUFFER_READ_READY - write_memory(SDH_BASE + 0x34, tmp_val); - - // SDH_Set_Timeout - tmp_val = read_memory16(SDH_BASE + 0x2e); - tmp_val = set_bits(tmp_val, 0, 4, 0x0e); // SDH_TIMEOUT_VALUE - write_memory16(SDH_BASE + 0x2e, tmp_val); - - // SDH_Powon - tmp_val = read_memory16(SDH_BASE + 0x28); - tmp_val = set_bits(tmp_val, 8, 1, 1); // SDH_SD_BUS_POWER - write_memory16(SDH_BASE + 0x28, tmp_val); - - // sdcard idle - sdh_send_command(SDHResp::None, SDHCmdType::Normal, 0, 0, false); - sleep_ms(100); - - // send CMD8 - sdh_send_command(SDHResp::R7, SDHCmdType::Normal, 8, 0x1AA, false); - sleep_ms(100); - let data: u128 = sdh_get_resp(); - if data != 0x1AA { - writeln!( - *w, - "unexpected response to CMD8: {:#010X}, expected 0x1AA", - data - ) - .ok(); - loop {} - } - - loop { - const OCR_NBUSY: u32 = 0x80000000; - const OCR_VOLTAGE_MASK: u32 = 0x007FFF80; - const OCR_HCS: u32 = 0x40000000; - sdh_send_command(SDHResp::R1, SDHCmdType::Normal, 55, 0, false); - sleep_ms(100); - sdh_send_command( - SDHResp::R3, - SDHCmdType::Normal, - 41, - OCR_VOLTAGE_MASK & 0x00ff8000 | OCR_HCS, - false, - ); - sleep_ms(100); - let ocr = sdh_get_resp(); - if (ocr as u32 & OCR_NBUSY) == OCR_NBUSY { - break; - } - sleep_ms(100); - } - - // send CMD2 to get CID - sdh_send_command(SDHResp::R2, SDHCmdType::Normal, 2, 0, false); - sleep_ms(100); - let cid = sdh_get_resp(); - writeln!(*w, "cid: {:#034X}", cid).ok(); - - // send CMD3 to get RCA - sdh_send_command(SDHResp::R6, SDHCmdType::Normal, 3, 0, false); - sleep_ms(100); - let rca = sdh_get_resp() as u32 >> 16; - writeln!(*w, "rca: {:#010X}", rca).ok(); - - // send CMD9 to get CSD - sdh_send_command(SDHResp::R2, SDHCmdType::Normal, 9, rca << 16, false); - sleep_ms(100); - let csd_raw = sdh_get_resp(); - let (csd_structure, c_size) = parse_csd_v2(csd_raw); - if csd_structure != 1 { - writeln!(*w, "unexpected CSD: {:#034X}", csd_raw).ok(); - loop {} - } - - writeln!(*w, "csd: {:#034X}, c_size: {}", csd_raw, c_size).ok(); - - let block_size = 512; - let block_count = (c_size + 1) * 1024; - - // send CMD7 to select card - sdh_send_command(SDHResp::R1B, SDHCmdType::Normal, 7, rca << 16, false); - sleep_ms(100); - - // set 1 data len, CMD55 -> ACMD6 - sdh_send_command(SDHResp::R1, SDHCmdType::Normal, 55, rca << 16, false); - sleep_ms(100); - sdh_send_command(SDHResp::R1, SDHCmdType::Normal, 6, 0x0, false); - sleep_ms(100); - - writeln!( - *w, - "sdcard init done, size: {} MB", - block_count as u128 * block_size / 1024 / 1024 - ) - .ok(); - MySdCard { block_count } -} - -fn sdh_send_command( - resp_type: SDHResp, - cmd_type: SDHCmdType, - cmd_idx: u32, - argument: u32, - has_data: bool, -) { - let mut tmp_val; - let mut flag = SDHTransFlag::None as u32; - if has_data { - flag |= SDHTransFlag::DataPresent as u32; - } - match resp_type { - SDHResp::None => {} - SDHResp::R1 | SDHResp::R5 | SDHResp::R6 | SDHResp::R7 => { - flag |= SDHTransFlag::Resp48Bits as u32 - | SDHTransFlag::EnCrcCheck as u32 - | SDHTransFlag::EnIndexCheck as u32; - } - SDHResp::R1B | SDHResp::R5B => { - flag |= SDHTransFlag::Resp48BitsWithBusy as u32 - | SDHTransFlag::EnCrcCheck as u32 - | SDHTransFlag::EnIndexCheck as u32; - } - SDHResp::R2 => { - flag |= SDHTransFlag::Resp136Bits as u32 | SDHTransFlag::EnCrcCheck as u32; - } - SDHResp::R3 | SDHResp::R4 => { - flag |= SDHTransFlag::Resp48Bits as u32; - } - } - tmp_val = flag >> 16; - - tmp_val = set_bits(tmp_val, 6, 2, cmd_type as u32); - tmp_val = set_bits(tmp_val, 8, 6, cmd_idx); - write_memory(SDH_BASE + 0x08, argument); - write_memory16(SDH_BASE + 0x0E, tmp_val); -} - -fn sdh_get_resp() -> u128 { - let a = read_memory(SDH_BASE + 0x10); - let b = read_memory(SDH_BASE + 0x14); - let c = read_memory(SDH_BASE + 0x18); - let d = read_memory(SDH_BASE + 0x1C); - (a as u128) | ((b as u128) << 32) | ((c as u128) << 64) | ((d as u128) << 96) -} - -enum SDHCmdType { - Normal, - Suspend, - Resume, - Abort, - Empty, -} - -enum SDHTransFlag { - None = 0x00000000, - EnDma = 0x00000001, // Enable DMA - EnBlkCount = 0x00000002, // Enable block count - EnAutoCmd12 = 0x00000004, // Enable auto CMD12 - EnAutoCmd23 = 0x00000008, // Enable auto CMD23 - ReadData = 0x00000010, // Enable read data - MultiBlk = 0x00000020, // Enable multi-block data operation - Resp136Bits = 0x00010000, // Response is 136 bits length - Resp48Bits = 0x00020000, // Response is 48 bits length - Resp48BitsWithBusy = 0x00030000, // Response is 48 bits length with busy status - EnCrcCheck = 0x00080000, // Enable CRC check - EnIndexCheck = 0x00100000, // Enable index check - DataPresent = 0x00200000, // Data present - Suspend = 0x00400000, // Suspend command - Resume = 0x00800000, // Resume command - Abort = 0x00C00000, // Abort command -} - -enum SDHResp { - None, - R1, - R5, - R6, - R7, - R1B, - R5B, - R2, - R3, - R4, -} - -fn write_memory16(addr: u32, val: u32) { - unsafe { ptr::write_volatile(addr as *mut u16, val as u16) } -} - -fn read_memory16(addr: u32) -> u32 { - unsafe { ptr::read_volatile(addr as *const u16) as u32 } -} - -pub fn read_memory(addr: u32) -> u32 { - unsafe { ptr::read_volatile(addr as *const u32) } -} - -pub fn write_memory(addr: u32, val: u32) { - unsafe { ptr::write_volatile(addr as *mut u32, val) } -} - -fn parse_csd_v2(csd: u128) -> (u32, u32) { - let csd_structure = (((csd >> (32 * 3)) & 0xC00000) >> 22) as u32; - let c_size = (((csd >> 32) & 0x3FFFFF00) >> 8) as u32; - (csd_structure, c_size) -}