diff options
Diffstat (limited to 'embassy-stm32/src/sdmmc/sd.rs')
| -rw-r--r-- | embassy-stm32/src/sdmmc/sd.rs | 699 |
1 files changed, 699 insertions, 0 deletions
diff --git a/embassy-stm32/src/sdmmc/sd.rs b/embassy-stm32/src/sdmmc/sd.rs new file mode 100644 index 000000000..20318bbfa --- /dev/null +++ b/embassy-stm32/src/sdmmc/sd.rs | |||
| @@ -0,0 +1,699 @@ | |||
| 1 | use core::default::Default; | ||
| 2 | use core::ops::{Deref, DerefMut}; | ||
| 3 | |||
| 4 | use sdio_host::emmc::{EMMC, ExtCSD}; | ||
| 5 | use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; | ||
| 6 | use sdio_host::{common_cmd, emmc_cmd, sd_cmd}; | ||
| 7 | |||
| 8 | use crate::sdmmc::{ | ||
| 9 | BlockSize, DatapathMode, Error, Sdmmc, Signalling, aligned_mut, aligned_ref, block_size, bus_width_vals, | ||
| 10 | slice8_mut, slice8_ref, | ||
| 11 | }; | ||
| 12 | use crate::time::{Hertz, mhz}; | ||
| 13 | |||
| 14 | /// Aligned data block for SDMMC transfers. | ||
| 15 | /// | ||
| 16 | /// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements. | ||
| 17 | #[repr(align(4))] | ||
| 18 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 19 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 20 | pub struct DataBlock(pub [u32; 128]); | ||
| 21 | |||
| 22 | impl DataBlock { | ||
| 23 | /// Create a new DataBlock | ||
| 24 | pub const fn new() -> Self { | ||
| 25 | DataBlock([0u32; 128]) | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | impl Deref for DataBlock { | ||
| 30 | type Target = [u8; 512]; | ||
| 31 | |||
| 32 | fn deref(&self) -> &Self::Target { | ||
| 33 | unwrap!(slice8_ref(&self.0[..]).try_into()) | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | impl DerefMut for DataBlock { | ||
| 38 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 39 | unwrap!(slice8_mut(&mut self.0[..]).try_into()) | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Command Block buffer for SDMMC command transfers. | ||
| 44 | /// | ||
| 45 | /// This is a 16-word array, exposed so that DMA commpatible memory can be used if required. | ||
| 46 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 47 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 48 | pub struct CmdBlock(pub [u32; 16]); | ||
| 49 | |||
| 50 | impl CmdBlock { | ||
| 51 | /// Creates a new instance of CmdBlock | ||
| 52 | pub const fn new() -> Self { | ||
| 53 | Self([0u32; 16]) | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | impl Deref for CmdBlock { | ||
| 58 | type Target = [u32; 16]; | ||
| 59 | |||
| 60 | fn deref(&self) -> &Self::Target { | ||
| 61 | &self.0 | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | impl DerefMut for CmdBlock { | ||
| 66 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 67 | &mut self.0 | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Represents either an SD or EMMC card | ||
| 72 | pub trait Addressable: Sized + Clone { | ||
| 73 | /// Associated type | ||
| 74 | type Ext; | ||
| 75 | |||
| 76 | /// Get this peripheral's address on the SDMMC bus | ||
| 77 | fn get_address(&self) -> u16; | ||
| 78 | |||
| 79 | /// Is this a standard or high capacity peripheral? | ||
| 80 | fn get_capacity(&self) -> CardCapacity; | ||
| 81 | |||
| 82 | /// Size in bytes | ||
| 83 | fn size(&self) -> u64; | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Storage Device | ||
| 87 | pub struct StorageDevice<'a, 'b, T: Addressable> { | ||
| 88 | info: T, | ||
| 89 | /// Inner member | ||
| 90 | pub sdmmc: &'a mut Sdmmc<'b>, | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Card Storage Device | ||
| 94 | impl<'a, 'b> StorageDevice<'a, 'b, Card> { | ||
| 95 | /// Create a new SD card | ||
| 96 | pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 97 | let mut s = Self { | ||
| 98 | info: Card::default(), | ||
| 99 | sdmmc, | ||
| 100 | }; | ||
| 101 | |||
| 102 | s.acquire(cmd_block, freq).await?; | ||
| 103 | |||
| 104 | Ok(s) | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Initializes the card into a known state (or at least tries to). | ||
| 108 | async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 109 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 110 | let regs = self.sdmmc.info.regs; | ||
| 111 | |||
| 112 | let _bus_width = match self.sdmmc.bus_width() { | ||
| 113 | BusWidth::Eight => return Err(Error::BusWidth), | ||
| 114 | bus_width => bus_width, | ||
| 115 | }; | ||
| 116 | |||
| 117 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 118 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 119 | self.sdmmc.init_idle()?; | ||
| 120 | |||
| 121 | // Check if cards supports CMD8 (with pattern) | ||
| 122 | self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | ||
| 123 | let cic = CIC::from(regs.respr(0).read().cardstatus()); | ||
| 124 | |||
| 125 | if cic.pattern() != 0xAA { | ||
| 126 | return Err(Error::UnsupportedCardVersion); | ||
| 127 | } | ||
| 128 | |||
| 129 | if cic.voltage_accepted() & 1 == 0 { | ||
| 130 | return Err(Error::UnsupportedVoltage); | ||
| 131 | } | ||
| 132 | |||
| 133 | let ocr = loop { | ||
| 134 | // Signal that next command is a app command | ||
| 135 | self.sdmmc.cmd(common_cmd::app_cmd(0), false)?; // CMD55 | ||
| 136 | |||
| 137 | // 3.2-3.3V | ||
| 138 | let voltage_window = 1 << 5; | ||
| 139 | // Initialize card | ||
| 140 | match self | ||
| 141 | .sdmmc | ||
| 142 | .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) | ||
| 143 | { | ||
| 144 | // ACMD41 | ||
| 145 | Ok(_) => (), | ||
| 146 | Err(Error::Crc) => (), | ||
| 147 | Err(err) => return Err(err), | ||
| 148 | } | ||
| 149 | |||
| 150 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | ||
| 151 | if !ocr.is_busy() { | ||
| 152 | // Power up done | ||
| 153 | break ocr; | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | |||
| 157 | if ocr.high_capacity() { | ||
| 158 | // Card is SDHC or SDXC or SDUC | ||
| 159 | self.info.card_type = CardCapacity::HighCapacity; | ||
| 160 | } else { | ||
| 161 | self.info.card_type = CardCapacity::StandardCapacity; | ||
| 162 | } | ||
| 163 | self.info.ocr = ocr; | ||
| 164 | |||
| 165 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 166 | |||
| 167 | self.sdmmc.cmd(sd_cmd::send_relative_address(), false)?; | ||
| 168 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); | ||
| 169 | self.info.rca = rca.address(); | ||
| 170 | |||
| 171 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 172 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 173 | |||
| 174 | self.info.scr = self.get_scr(cmd_block).await?; | ||
| 175 | |||
| 176 | let (bus_width, acmd_arg) = if !self.info.scr.bus_width_four() { | ||
| 177 | (BusWidth::One, 0) | ||
| 178 | } else { | ||
| 179 | (BusWidth::Four, 2) | ||
| 180 | }; | ||
| 181 | |||
| 182 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 183 | self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), false)?; | ||
| 184 | |||
| 185 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 186 | |||
| 187 | // Read status | ||
| 188 | self.info.status = self.read_sd_status(cmd_block).await?; | ||
| 189 | |||
| 190 | if freq > mhz(25) { | ||
| 191 | // Switch to SDR25 | ||
| 192 | self.sdmmc.signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?; | ||
| 193 | |||
| 194 | if self.sdmmc.signalling == Signalling::SDR25 { | ||
| 195 | // Set final clock frequency | ||
| 196 | self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?; | ||
| 197 | |||
| 198 | if self.sdmmc.read_status(&self.info)?.state() != CurrentState::Transfer { | ||
| 199 | return Err(Error::SignalingSwitchFailed); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | // Read status after signalling change | ||
| 204 | self.read_sd_status(cmd_block).await?; | ||
| 205 | } | ||
| 206 | |||
| 207 | Ok(()) | ||
| 208 | } | ||
| 209 | |||
| 210 | /// Switch mode using CMD6. | ||
| 211 | /// | ||
| 212 | /// Attempt to set a new signalling mode. The selected | ||
| 213 | /// signalling mode is returned. Expects the current clock | ||
| 214 | /// frequency to be > 12.5MHz. | ||
| 215 | /// | ||
| 216 | /// SD only. | ||
| 217 | async fn switch_signalling_mode( | ||
| 218 | &self, | ||
| 219 | cmd_block: &mut CmdBlock, | ||
| 220 | signalling: Signalling, | ||
| 221 | ) -> Result<Signalling, Error> { | ||
| 222 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 223 | // necessary" | ||
| 224 | |||
| 225 | let set_function = 0x8000_0000 | ||
| 226 | | match signalling { | ||
| 227 | // See PLSS v7_10 Table 4-11 | ||
| 228 | Signalling::DDR50 => 0xFF_FF04, | ||
| 229 | Signalling::SDR104 => 0xFF_1F03, | ||
| 230 | Signalling::SDR50 => 0xFF_1F02, | ||
| 231 | Signalling::SDR25 => 0xFF_FF01, | ||
| 232 | Signalling::SDR12 => 0xFF_FF00, | ||
| 233 | }; | ||
| 234 | |||
| 235 | let buffer = &mut cmd_block.0[..64 / 4]; | ||
| 236 | let mode = DatapathMode::Block(block_size(size_of_val(buffer))); | ||
| 237 | let transfer = self.sdmmc.prepare_datapath_read(aligned_mut(buffer), mode); | ||
| 238 | |||
| 239 | self.sdmmc.cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | ||
| 240 | |||
| 241 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 242 | |||
| 243 | // Host is allowed to use the new functions at least 8 | ||
| 244 | // clocks after the end of the switch command | ||
| 245 | // transaction. We know the current clock period is < 80ns, | ||
| 246 | // so a total delay of 640ns is required here | ||
| 247 | for _ in 0..300 { | ||
| 248 | cortex_m::asm::nop(); | ||
| 249 | } | ||
| 250 | |||
| 251 | // Function Selection of Function Group 1 | ||
| 252 | let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF; | ||
| 253 | |||
| 254 | match selection { | ||
| 255 | 0 => Ok(Signalling::SDR12), | ||
| 256 | 1 => Ok(Signalling::SDR25), | ||
| 257 | 2 => Ok(Signalling::SDR50), | ||
| 258 | 3 => Ok(Signalling::SDR104), | ||
| 259 | 4 => Ok(Signalling::DDR50), | ||
| 260 | _ => Err(Error::UnsupportedCardType), | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Reads the SCR register. | ||
| 265 | /// | ||
| 266 | /// SD only. | ||
| 267 | async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result<SCR, Error> { | ||
| 268 | // Read the 64-bit SCR register | ||
| 269 | self.sdmmc.cmd(common_cmd::set_block_length(8), false)?; // CMD16 | ||
| 270 | self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), false)?; | ||
| 271 | |||
| 272 | let scr = &mut cmd_block.0[..2]; | ||
| 273 | |||
| 274 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 275 | |||
| 276 | let transfer = self | ||
| 277 | .sdmmc | ||
| 278 | .prepare_datapath_read(aligned_mut(scr), DatapathMode::Block(BlockSize::Size8)); | ||
| 279 | self.sdmmc.cmd(sd_cmd::send_scr(), true)?; | ||
| 280 | |||
| 281 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 282 | |||
| 283 | Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into())))) | ||
| 284 | } | ||
| 285 | |||
| 286 | /// Reads the SD Status (ACMD13) | ||
| 287 | /// | ||
| 288 | /// SD only. | ||
| 289 | async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result<SDStatus, Error> { | ||
| 290 | let rca = self.info.rca; | ||
| 291 | |||
| 292 | self.sdmmc.cmd(common_cmd::set_block_length(64), false)?; // CMD16 | ||
| 293 | self.sdmmc.cmd(common_cmd::app_cmd(rca), false)?; // APP | ||
| 294 | |||
| 295 | let buffer = &mut cmd_block.as_mut()[..64 / 4]; | ||
| 296 | let mode = DatapathMode::Block(block_size(size_of_val(buffer))); | ||
| 297 | |||
| 298 | let transfer = self.sdmmc.prepare_datapath_read(aligned_mut(buffer), mode); | ||
| 299 | self.sdmmc.cmd(sd_cmd::sd_status(), true)?; | ||
| 300 | |||
| 301 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 302 | |||
| 303 | for byte in cmd_block.iter_mut() { | ||
| 304 | *byte = u32::from_be(*byte); | ||
| 305 | } | ||
| 306 | |||
| 307 | Ok(cmd_block.0.into()) | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | /// Emmc storage device | ||
| 312 | impl<'a, 'b> StorageDevice<'a, 'b, Emmc> { | ||
| 313 | /// Create a new EMMC card | ||
| 314 | pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> { | ||
| 315 | let mut s = Self { | ||
| 316 | info: Emmc::default(), | ||
| 317 | sdmmc, | ||
| 318 | }; | ||
| 319 | |||
| 320 | s.acquire(cmd_block, freq).await?; | ||
| 321 | |||
| 322 | Ok(s) | ||
| 323 | } | ||
| 324 | |||
| 325 | async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> { | ||
| 326 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 327 | let regs = self.sdmmc.info.regs; | ||
| 328 | |||
| 329 | let bus_width = self.sdmmc.bus_width(); | ||
| 330 | |||
| 331 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 332 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 333 | self.sdmmc.init_idle()?; | ||
| 334 | |||
| 335 | let ocr = loop { | ||
| 336 | let high_voltage = 0b0 << 7; | ||
| 337 | let access_mode = 0b10 << 29; | ||
| 338 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 339 | // Initialize card | ||
| 340 | match self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 341 | Ok(_) => (), | ||
| 342 | Err(Error::Crc) => (), | ||
| 343 | Err(err) => return Err(err), | ||
| 344 | } | ||
| 345 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 346 | if !ocr.is_busy() { | ||
| 347 | // Power up done | ||
| 348 | break ocr; | ||
| 349 | } | ||
| 350 | }; | ||
| 351 | |||
| 352 | self.info.capacity = if ocr.access_mode() == 0b10 { | ||
| 353 | // Card is SDHC or SDXC or SDUC | ||
| 354 | CardCapacity::HighCapacity | ||
| 355 | } else { | ||
| 356 | CardCapacity::StandardCapacity | ||
| 357 | }; | ||
| 358 | self.info.ocr = ocr; | ||
| 359 | |||
| 360 | self.info.cid = self.sdmmc.get_cid()?.into(); | ||
| 361 | |||
| 362 | self.info.rca = 1u16.into(); | ||
| 363 | self.sdmmc | ||
| 364 | .cmd(emmc_cmd::assign_relative_address(self.info.rca), false)?; | ||
| 365 | |||
| 366 | self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into(); | ||
| 367 | self.sdmmc.select_card(Some(self.info.get_address()))?; | ||
| 368 | |||
| 369 | let (widbus, _) = bus_width_vals(bus_width); | ||
| 370 | |||
| 371 | // Write bus width to ExtCSD byte 183 | ||
| 372 | self.sdmmc.cmd( | ||
| 373 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 374 | false, | ||
| 375 | )?; | ||
| 376 | |||
| 377 | // Wait for ready after R1b response | ||
| 378 | loop { | ||
| 379 | let status = self.sdmmc.read_status(&self.info)?; | ||
| 380 | |||
| 381 | if status.ready_for_data() { | ||
| 382 | break; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?; | ||
| 387 | self.info.ext_csd = self.read_ext_csd().await?; | ||
| 388 | |||
| 389 | Ok(()) | ||
| 390 | } | ||
| 391 | |||
| 392 | /// Gets the EXT_CSD register. | ||
| 393 | /// | ||
| 394 | /// eMMC only. | ||
| 395 | async fn read_ext_csd(&self) -> Result<ExtCSD, Error> { | ||
| 396 | // Note: cmd_block can't be used because ExtCSD is too long to fit. | ||
| 397 | let mut data_block = DataBlock::new(); | ||
| 398 | |||
| 399 | self.sdmmc | ||
| 400 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false) | ||
| 401 | .unwrap(); // CMD16 | ||
| 402 | |||
| 403 | let transfer = self.sdmmc.prepare_datapath_read( | ||
| 404 | aligned_mut(&mut data_block.0), | ||
| 405 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 406 | ); | ||
| 407 | self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true)?; | ||
| 408 | |||
| 409 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 410 | |||
| 411 | Ok(data_block.0.into()) | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | /// Card or Emmc storage device | ||
| 416 | impl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> { | ||
| 417 | /// Write a block | ||
| 418 | pub fn card(&self) -> A { | ||
| 419 | self.info.clone() | ||
| 420 | } | ||
| 421 | |||
| 422 | /// Read a data block. | ||
| 423 | #[inline] | ||
| 424 | pub async fn read_block(&mut self, block_idx: u32, data_block: &mut DataBlock) -> Result<(), Error> { | ||
| 425 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 426 | let card_capacity = self.info.get_capacity(); | ||
| 427 | |||
| 428 | // Always read 1 block of 512 bytes | ||
| 429 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 430 | let address = match card_capacity { | ||
| 431 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 432 | _ => block_idx, | ||
| 433 | }; | ||
| 434 | self.sdmmc | ||
| 435 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 436 | |||
| 437 | let transfer = self.sdmmc.prepare_datapath_read( | ||
| 438 | aligned_mut(&mut data_block.0), | ||
| 439 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 440 | ); | ||
| 441 | self.sdmmc.cmd(common_cmd::read_single_block(address), true)?; | ||
| 442 | |||
| 443 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 444 | |||
| 445 | Ok(()) | ||
| 446 | } | ||
| 447 | |||
| 448 | /// Read multiple data blocks. | ||
| 449 | #[inline] | ||
| 450 | pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { | ||
| 451 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 452 | let card_capacity = self.info.get_capacity(); | ||
| 453 | |||
| 454 | // NOTE(unsafe) reinterpret buffer as &mut [u32] | ||
| 455 | let buffer = unsafe { | ||
| 456 | core::slice::from_raw_parts_mut( | ||
| 457 | blocks.as_mut_ptr() as *mut u32, | ||
| 458 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 459 | ) | ||
| 460 | }; | ||
| 461 | |||
| 462 | // Always read 1 block of 512 bytes | ||
| 463 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 464 | let address = match card_capacity { | ||
| 465 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 466 | _ => block_idx, | ||
| 467 | }; | ||
| 468 | self.sdmmc | ||
| 469 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 470 | |||
| 471 | let transfer = self.sdmmc.prepare_datapath_read( | ||
| 472 | aligned_mut(buffer), | ||
| 473 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 474 | ); | ||
| 475 | self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true)?; | ||
| 476 | |||
| 477 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 478 | |||
| 479 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 480 | self.sdmmc.clear_interrupt_flags(); | ||
| 481 | |||
| 482 | Ok(()) | ||
| 483 | } | ||
| 484 | |||
| 485 | /// Write a data block. | ||
| 486 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> | ||
| 487 | where | ||
| 488 | CardStatus<A::Ext>: From<u32>, | ||
| 489 | { | ||
| 490 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 491 | |||
| 492 | // Always read 1 block of 512 bytes | ||
| 493 | // cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 494 | let address = match self.info.get_capacity() { | ||
| 495 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 496 | _ => block_idx, | ||
| 497 | }; | ||
| 498 | self.sdmmc | ||
| 499 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 500 | |||
| 501 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | ||
| 502 | #[cfg(sdmmc_v1)] | ||
| 503 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 504 | |||
| 505 | let transfer = self.sdmmc.prepare_datapath_write( | ||
| 506 | aligned_ref(&buffer.0), | ||
| 507 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 508 | ); | ||
| 509 | |||
| 510 | #[cfg(sdmmc_v2)] | ||
| 511 | self.sdmmc.cmd(common_cmd::write_single_block(address), true)?; | ||
| 512 | |||
| 513 | self.sdmmc.complete_datapath_transfer(transfer, true).await?; | ||
| 514 | |||
| 515 | // TODO: Make this configurable | ||
| 516 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 517 | |||
| 518 | while timeout > 0 { | ||
| 519 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 520 | if ready_for_data { | ||
| 521 | return Ok(()); | ||
| 522 | } | ||
| 523 | timeout -= 1; | ||
| 524 | } | ||
| 525 | |||
| 526 | Err(Error::SoftwareTimeout) | ||
| 527 | } | ||
| 528 | |||
| 529 | /// Write multiple data blocks. | ||
| 530 | pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> | ||
| 531 | where | ||
| 532 | CardStatus<A::Ext>: From<u32>, | ||
| 533 | { | ||
| 534 | let _scoped_block_stop = self.sdmmc.info.rcc.block_stop(); | ||
| 535 | |||
| 536 | // NOTE(unsafe) reinterpret buffer as &[u32] | ||
| 537 | let buffer = unsafe { | ||
| 538 | core::slice::from_raw_parts( | ||
| 539 | blocks.as_ptr() as *const u32, | ||
| 540 | blocks.len() * size_of::<DataBlock>() / size_of::<u32>(), | ||
| 541 | ) | ||
| 542 | }; | ||
| 543 | // Always read 1 block of 512 bytes | ||
| 544 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 545 | let address = match self.info.get_capacity() { | ||
| 546 | CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32, | ||
| 547 | _ => block_idx, | ||
| 548 | }; | ||
| 549 | |||
| 550 | self.sdmmc | ||
| 551 | .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), false)?; // CMD16 | ||
| 552 | |||
| 553 | #[cfg(sdmmc_v1)] | ||
| 554 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 555 | |||
| 556 | // Setup write command | ||
| 557 | let transfer = self.sdmmc.prepare_datapath_write( | ||
| 558 | aligned_ref(buffer), | ||
| 559 | DatapathMode::Block(block_size(size_of::<DataBlock>())), | ||
| 560 | ); | ||
| 561 | #[cfg(sdmmc_v2)] | ||
| 562 | self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 563 | |||
| 564 | self.sdmmc.complete_datapath_transfer(transfer, false).await?; | ||
| 565 | |||
| 566 | self.sdmmc.cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 567 | self.sdmmc.clear_interrupt_flags(); | ||
| 568 | |||
| 569 | // TODO: Make this configurable | ||
| 570 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 571 | |||
| 572 | while timeout > 0 { | ||
| 573 | let ready_for_data = self.sdmmc.read_status(&self.info)?.ready_for_data(); | ||
| 574 | |||
| 575 | if ready_for_data { | ||
| 576 | return Ok(()); | ||
| 577 | } | ||
| 578 | timeout -= 1; | ||
| 579 | } | ||
| 580 | Err(Error::SoftwareTimeout) | ||
| 581 | } | ||
| 582 | } | ||
| 583 | |||
| 584 | #[derive(Clone, Copy, Debug, Default)] | ||
| 585 | /// SD Card | ||
| 586 | pub struct Card { | ||
| 587 | /// The type of this card | ||
| 588 | pub card_type: CardCapacity, | ||
| 589 | /// Operation Conditions Register | ||
| 590 | pub ocr: OCR<SD>, | ||
| 591 | /// Relative Card Address | ||
| 592 | pub rca: u16, | ||
| 593 | /// Card ID | ||
| 594 | pub cid: CID<SD>, | ||
| 595 | /// Card Specific Data | ||
| 596 | pub csd: CSD<SD>, | ||
| 597 | /// SD CARD Configuration Register | ||
| 598 | pub scr: SCR, | ||
| 599 | /// SD Status | ||
| 600 | pub status: SDStatus, | ||
| 601 | } | ||
| 602 | |||
| 603 | impl Addressable for Card { | ||
| 604 | type Ext = SD; | ||
| 605 | |||
| 606 | /// Get this peripheral's address on the SDMMC bus | ||
| 607 | fn get_address(&self) -> u16 { | ||
| 608 | self.rca | ||
| 609 | } | ||
| 610 | |||
| 611 | /// Is this a standard or high capacity peripheral? | ||
| 612 | fn get_capacity(&self) -> CardCapacity { | ||
| 613 | self.card_type | ||
| 614 | } | ||
| 615 | |||
| 616 | /// Size in bytes | ||
| 617 | fn size(&self) -> u64 { | ||
| 618 | u64::from(self.csd.block_count()) * 512 | ||
| 619 | } | ||
| 620 | } | ||
| 621 | |||
| 622 | #[derive(Clone, Copy, Debug, Default)] | ||
| 623 | /// eMMC storage | ||
| 624 | pub struct Emmc { | ||
| 625 | /// The capacity of this card | ||
| 626 | pub capacity: CardCapacity, | ||
| 627 | /// Operation Conditions Register | ||
| 628 | pub ocr: OCR<EMMC>, | ||
| 629 | /// Relative Card Address | ||
| 630 | pub rca: u16, | ||
| 631 | /// Card ID | ||
| 632 | pub cid: CID<EMMC>, | ||
| 633 | /// Card Specific Data | ||
| 634 | pub csd: CSD<EMMC>, | ||
| 635 | /// Extended Card Specific Data | ||
| 636 | pub ext_csd: ExtCSD, | ||
| 637 | } | ||
| 638 | |||
| 639 | impl Addressable for Emmc { | ||
| 640 | type Ext = EMMC; | ||
| 641 | |||
| 642 | /// Get this peripheral's address on the SDMMC bus | ||
| 643 | fn get_address(&self) -> u16 { | ||
| 644 | self.rca | ||
| 645 | } | ||
| 646 | |||
| 647 | /// Is this a standard or high capacity peripheral? | ||
| 648 | fn get_capacity(&self) -> CardCapacity { | ||
| 649 | self.capacity | ||
| 650 | } | ||
| 651 | |||
| 652 | /// Size in bytes | ||
| 653 | fn size(&self) -> u64 { | ||
| 654 | u64::from(self.ext_csd.sector_count()) * 512 | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | impl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> { | ||
| 659 | type Error = Error; | ||
| 660 | type Align = aligned::A4; | ||
| 661 | |||
| 662 | async fn read( | ||
| 663 | &mut self, | ||
| 664 | block_address: u32, | ||
| 665 | buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 666 | ) -> Result<(), Self::Error> { | ||
| 667 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 668 | if buf.len() == 1 { | ||
| 669 | let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut DataBlock) }; | ||
| 670 | self.read_block(block_address, block).await?; | ||
| 671 | } else { | ||
| 672 | let blocks: &mut [DataBlock] = | ||
| 673 | unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; | ||
| 674 | self.read_blocks(block_address, blocks).await?; | ||
| 675 | } | ||
| 676 | Ok(()) | ||
| 677 | } | ||
| 678 | |||
| 679 | async fn write( | ||
| 680 | &mut self, | ||
| 681 | block_address: u32, | ||
| 682 | buf: &[aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 683 | ) -> Result<(), Self::Error> { | ||
| 684 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 685 | if buf.len() == 1 { | ||
| 686 | let block = unsafe { &*(&buf[0] as *const _ as *const DataBlock) }; | ||
| 687 | self.write_block(block_address, block).await?; | ||
| 688 | } else { | ||
| 689 | let blocks: &[DataBlock] = | ||
| 690 | unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; | ||
| 691 | self.write_blocks(block_address, blocks).await?; | ||
| 692 | } | ||
| 693 | Ok(()) | ||
| 694 | } | ||
| 695 | |||
| 696 | async fn size(&mut self) -> Result<u64, Self::Error> { | ||
| 697 | Ok(self.info.size()) | ||
| 698 | } | ||
| 699 | } | ||
