diff options
| author | chemicstry <[email protected]> | 2022-03-15 19:58:19 +0200 |
|---|---|---|
| committer | chemicstry <[email protected]> | 2022-03-15 19:58:19 +0200 |
| commit | 2d224cf6a072bf9f12c56147c9a0339302ef47ce (patch) | |
| tree | 699aa80ee446827ba6addfd99f63b3be4347ef0e | |
| parent | da9c0efaad594b48cfcdf641d353133d14cf98fe (diff) | |
Update
| -rw-r--r-- | .vscode/settings.json | 6 | ||||
| -rw-r--r-- | embassy-stm32/src/sdmmc/mod.rs | 1450 | ||||
| -rw-r--r-- | embassy-stm32/src/sdmmc/v1.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/sdmmc/v2.rs | 1374 | ||||
| m--------- | stm32-data | 0 |
5 files changed, 1449 insertions, 1382 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index 79433a7c9..b4047399c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json | |||
| @@ -16,16 +16,16 @@ | |||
| 16 | //"embassy-net/medium-ethernet", | 16 | //"embassy-net/medium-ethernet", |
| 17 | //"embassy-net/tcp", | 17 | //"embassy-net/tcp", |
| 18 | //"embassy-net/pool-16", | 18 | //"embassy-net/pool-16", |
| 19 | "nightly", | 19 | //"nightly", |
| 20 | ], | 20 | ], |
| 21 | "rust-analyzer.linkedProjects": [ | 21 | "rust-analyzer.linkedProjects": [ |
| 22 | // Declare for the target you wish to develop | 22 | // Declare for the target you wish to develop |
| 23 | "examples/nrf/Cargo.toml", | 23 | // "examples/nrf/Cargo.toml", |
| 24 | // "examples/rp/Cargo.toml", | 24 | // "examples/rp/Cargo.toml", |
| 25 | // "examples/std/Cargo.toml", | 25 | // "examples/std/Cargo.toml", |
| 26 | // "examples/stm32f0/Cargo.toml", | 26 | // "examples/stm32f0/Cargo.toml", |
| 27 | // "examples/stm32f1/Cargo.toml", | 27 | // "examples/stm32f1/Cargo.toml", |
| 28 | // "examples/stm32f4/Cargo.toml", | 28 | "examples/stm32f4/Cargo.toml", |
| 29 | // "examples/stm32f7/Cargo.toml", | 29 | // "examples/stm32f7/Cargo.toml", |
| 30 | // "examples/stm32g0/Cargo.toml", | 30 | // "examples/stm32g0/Cargo.toml", |
| 31 | // "examples/stm32g4/Cargo.toml", | 31 | // "examples/stm32g4/Cargo.toml", |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 087cb4c40..7bd809810 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -1,7 +1,1449 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | #[cfg_attr(sdmmc_v1, path = "v1.rs")] | 3 | use core::default::Default; |
| 4 | #[cfg_attr(sdmmc_v2, path = "v2.rs")] | 4 | use core::marker::PhantomData; |
| 5 | mod _version; | 5 | use core::task::Poll; |
| 6 | 6 | ||
| 7 | pub use _version::*; | 7 | use embassy::interrupt::InterruptExt; |
| 8 | use embassy::util::Unborrow; | ||
| 9 | use embassy::waitqueue::AtomicWaker; | ||
| 10 | use embassy_hal_common::drop::OnDrop; | ||
| 11 | use embassy_hal_common::unborrow; | ||
| 12 | use futures::future::poll_fn; | ||
| 13 | use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; | ||
| 14 | |||
| 15 | use crate::dma::NoDma; | ||
| 16 | use crate::gpio::sealed::AFType; | ||
| 17 | use crate::gpio::{Pull, Speed}; | ||
| 18 | use crate::interrupt::Interrupt; | ||
| 19 | use crate::pac::sdmmc::Sdmmc as RegBlock; | ||
| 20 | use crate::peripherals; | ||
| 21 | use crate::time::Hertz; | ||
| 22 | |||
| 23 | /// The signalling scheme used on the SDMMC bus | ||
| 24 | #[non_exhaustive] | ||
| 25 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 27 | pub enum Signalling { | ||
| 28 | SDR12, | ||
| 29 | SDR25, | ||
| 30 | SDR50, | ||
| 31 | SDR104, | ||
| 32 | DDR50, | ||
| 33 | } | ||
| 34 | |||
| 35 | impl Default for Signalling { | ||
| 36 | fn default() -> Self { | ||
| 37 | Signalling::SDR12 | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | #[repr(align(4))] | ||
| 42 | pub struct DataBlock([u8; 512]); | ||
| 43 | |||
| 44 | /// Errors | ||
| 45 | #[non_exhaustive] | ||
| 46 | #[derive(Debug, Copy, Clone)] | ||
| 47 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 48 | pub enum Error { | ||
| 49 | Timeout, | ||
| 50 | SoftwareTimeout, | ||
| 51 | UnsupportedCardVersion, | ||
| 52 | UnsupportedCardType, | ||
| 53 | Crc, | ||
| 54 | DataCrcFail, | ||
| 55 | RxOverFlow, | ||
| 56 | NoCard, | ||
| 57 | BadClock, | ||
| 58 | SignalingSwitchFailed, | ||
| 59 | PeripheralBusy, | ||
| 60 | } | ||
| 61 | |||
| 62 | /// A SD command | ||
| 63 | struct Cmd { | ||
| 64 | cmd: u8, | ||
| 65 | arg: u32, | ||
| 66 | resp: Response, | ||
| 67 | } | ||
| 68 | |||
| 69 | #[derive(Clone, Copy, Debug, Default)] | ||
| 70 | /// SD Card | ||
| 71 | pub struct Card { | ||
| 72 | /// The type of this card | ||
| 73 | pub card_type: CardCapacity, | ||
| 74 | /// Operation Conditions Register | ||
| 75 | pub ocr: OCR, | ||
| 76 | /// Relative Card Address | ||
| 77 | pub rca: u32, | ||
| 78 | /// Card ID | ||
| 79 | pub cid: CID, | ||
| 80 | /// Card Specific Data | ||
| 81 | pub csd: CSD, | ||
| 82 | /// SD CARD Configuration Register | ||
| 83 | pub scr: SCR, | ||
| 84 | /// SD Status | ||
| 85 | pub status: SDStatus, | ||
| 86 | } | ||
| 87 | impl Card { | ||
| 88 | /// Size in bytes | ||
| 89 | pub fn size(&self) -> u64 { | ||
| 90 | // SDHC / SDXC / SDUC | ||
| 91 | u64::from(self.csd.block_count()) * 512 | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Indicates transfer direction | ||
| 96 | enum Dir { | ||
| 97 | CardToHost, | ||
| 98 | HostToCard, | ||
| 99 | } | ||
| 100 | |||
| 101 | #[repr(u8)] | ||
| 102 | enum PowerCtrl { | ||
| 103 | Off = 0b00, | ||
| 104 | On = 0b11, | ||
| 105 | } | ||
| 106 | |||
| 107 | #[repr(u32)] | ||
| 108 | #[allow(dead_code)] | ||
| 109 | #[allow(non_camel_case_types)] | ||
| 110 | enum CmdAppOper { | ||
| 111 | VOLTAGE_WINDOW_SD = 0x8010_0000, | ||
| 112 | HIGH_CAPACITY = 0x4000_0000, | ||
| 113 | SDMMC_STD_CAPACITY = 0x0000_0000, | ||
| 114 | SDMMC_CHECK_PATTERN = 0x0000_01AA, | ||
| 115 | SD_SWITCH_1_8V_CAPACITY = 0x0100_0000, | ||
| 116 | } | ||
| 117 | |||
| 118 | #[derive(Eq, PartialEq, Copy, Clone)] | ||
| 119 | enum Response { | ||
| 120 | None = 0, | ||
| 121 | Short = 1, | ||
| 122 | Long = 3, | ||
| 123 | } | ||
| 124 | |||
| 125 | cfg_if::cfg_if! { | ||
| 126 | if #[cfg(sdmmc_v1)] { | ||
| 127 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | ||
| 128 | /// `sdmmc_ck` in Hertz. | ||
| 129 | /// | ||
| 130 | /// Returns `(clk_div, clk_f)`, where `clk_div` is the divisor register | ||
| 131 | /// value and `clk_f` is the resulting new clock frequency. | ||
| 132 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(u8, Hertz), Error> { | ||
| 133 | let clk_div = match ker_ck.0 / sdmmc_ck { | ||
| 134 | 0 | 1 => Ok(0), | ||
| 135 | x @ 2..=258 => { | ||
| 136 | Ok((x - 2) as u8) | ||
| 137 | } | ||
| 138 | _ => Err(Error::BadClock), | ||
| 139 | }?; | ||
| 140 | |||
| 141 | let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); | ||
| 142 | Ok((clk_div, clk_f)) | ||
| 143 | } | ||
| 144 | } else if #[cfg(sdmmc_v2)] { | ||
| 145 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | ||
| 146 | /// `sdmmc_ck` in Hertz. | ||
| 147 | /// | ||
| 148 | /// Returns `(clk_div, clk_f)`, where `clk_div` is the divisor register | ||
| 149 | /// value and `clk_f` is the resulting new clock frequency. | ||
| 150 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(u16, Hertz), Error> { | ||
| 151 | match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { | ||
| 152 | 0 | 1 => Ok((0, ker_ck)), | ||
| 153 | x @ 2..=2046 => { | ||
| 154 | let clk_div = ((x + 1) / 2) as u16; | ||
| 155 | let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); | ||
| 156 | |||
| 157 | Ok((clk_div, clk)) | ||
| 158 | } | ||
| 159 | _ => Err(Error::BadClock), | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | /// SDMMC configuration | ||
| 166 | /// | ||
| 167 | /// You should probably change the default clock values to match your configuration | ||
| 168 | /// | ||
| 169 | /// Default values: | ||
| 170 | /// hclk = 400_000_000 Hz | ||
| 171 | /// kernel_clk: 100_000_000 Hz | ||
| 172 | /// data_transfer_timeout: 5_000_000 | ||
| 173 | #[non_exhaustive] | ||
| 174 | pub struct Config { | ||
| 175 | /// AHB clock | ||
| 176 | pub hclk: Hertz, | ||
| 177 | /// SDMMC kernel clock | ||
| 178 | pub kernel_clk: Hertz, | ||
| 179 | /// The timeout to be set for data transfers, in card bus clock periods | ||
| 180 | pub data_transfer_timeout: u32, | ||
| 181 | } | ||
| 182 | |||
| 183 | impl Default for Config { | ||
| 184 | fn default() -> Self { | ||
| 185 | Self { | ||
| 186 | hclk: Hertz(400_000_000), | ||
| 187 | kernel_clk: Hertz(100_000_000), | ||
| 188 | data_transfer_timeout: 5_000_000, | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Sdmmc device | ||
| 194 | pub struct Sdmmc<'d, T: Instance, P: Pins<T>, Dma = NoDma> { | ||
| 195 | sdmmc: PhantomData<&'d mut T>, | ||
| 196 | pins: P, | ||
| 197 | irq: T::Interrupt, | ||
| 198 | config: Config, | ||
| 199 | /// Current clock to card | ||
| 200 | clock: Hertz, | ||
| 201 | /// Current signalling scheme to card | ||
| 202 | signalling: Signalling, | ||
| 203 | /// Card | ||
| 204 | card: Option<Card>, | ||
| 205 | dma: Dma, | ||
| 206 | } | ||
| 207 | |||
| 208 | impl<'d, T: Instance, P: Pins<T>, Dma> Sdmmc<'d, T, P, Dma> { | ||
| 209 | /// # Safety | ||
| 210 | /// | ||
| 211 | /// Futures that borrow this type can't be leaked | ||
| 212 | #[inline(always)] | ||
| 213 | pub unsafe fn new( | ||
| 214 | _peripheral: impl Unborrow<Target = T> + 'd, | ||
| 215 | pins: impl Unborrow<Target = P> + 'd, | ||
| 216 | irq: impl Unborrow<Target = T::Interrupt> + 'd, | ||
| 217 | config: Config, | ||
| 218 | dma: Dma, | ||
| 219 | ) -> Self { | ||
| 220 | unborrow!(irq, pins); | ||
| 221 | pins.configure(); | ||
| 222 | |||
| 223 | let inner = T::inner(); | ||
| 224 | let clock = inner.new_inner(config.kernel_clk); | ||
| 225 | |||
| 226 | irq.set_handler(Self::on_interrupt); | ||
| 227 | irq.unpend(); | ||
| 228 | irq.enable(); | ||
| 229 | |||
| 230 | Self { | ||
| 231 | sdmmc: PhantomData, | ||
| 232 | pins, | ||
| 233 | irq, | ||
| 234 | config, | ||
| 235 | clock, | ||
| 236 | signalling: Default::default(), | ||
| 237 | card: None, | ||
| 238 | dma, | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | #[inline(always)] | ||
| 243 | pub async fn init_card(&mut self, freq: impl Into<Hertz>) -> Result<(), Error> { | ||
| 244 | let inner = T::inner(); | ||
| 245 | let freq = freq.into(); | ||
| 246 | |||
| 247 | inner | ||
| 248 | .init_card( | ||
| 249 | freq, | ||
| 250 | P::BUSWIDTH, | ||
| 251 | &mut self.card, | ||
| 252 | &mut self.signalling, | ||
| 253 | self.config.hclk, | ||
| 254 | self.config.kernel_clk, | ||
| 255 | &mut self.clock, | ||
| 256 | T::state(), | ||
| 257 | self.config.data_transfer_timeout, | ||
| 258 | ) | ||
| 259 | .await | ||
| 260 | } | ||
| 261 | |||
| 262 | #[inline(always)] | ||
| 263 | pub async fn read_block( | ||
| 264 | &mut self, | ||
| 265 | block_idx: u32, | ||
| 266 | buffer: &mut DataBlock, | ||
| 267 | ) -> Result<(), Error> { | ||
| 268 | let card_capacity = self.card()?.card_type; | ||
| 269 | let inner = T::inner(); | ||
| 270 | let state = T::state(); | ||
| 271 | |||
| 272 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 273 | let buf = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 274 | inner | ||
| 275 | .read_block( | ||
| 276 | block_idx, | ||
| 277 | buf, | ||
| 278 | card_capacity, | ||
| 279 | state, | ||
| 280 | self.config.data_transfer_timeout, | ||
| 281 | ) | ||
| 282 | .await | ||
| 283 | } | ||
| 284 | |||
| 285 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { | ||
| 286 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 287 | let inner = T::inner(); | ||
| 288 | let state = T::state(); | ||
| 289 | |||
| 290 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 291 | let buf = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; | ||
| 292 | inner | ||
| 293 | .write_block( | ||
| 294 | block_idx, | ||
| 295 | buf, | ||
| 296 | card, | ||
| 297 | state, | ||
| 298 | self.config.data_transfer_timeout, | ||
| 299 | ) | ||
| 300 | .await | ||
| 301 | } | ||
| 302 | |||
| 303 | /// Get a reference to the initialized card | ||
| 304 | /// | ||
| 305 | /// # Errors | ||
| 306 | /// | ||
| 307 | /// Returns Error::NoCard if [`init_card`](#method.init_card) | ||
| 308 | /// has not previously succeeded | ||
| 309 | #[inline(always)] | ||
| 310 | pub fn card(&self) -> Result<&Card, Error> { | ||
| 311 | self.card.as_ref().ok_or(Error::NoCard) | ||
| 312 | } | ||
| 313 | |||
| 314 | #[inline(always)] | ||
| 315 | fn on_interrupt(_: *mut ()) { | ||
| 316 | let regs = T::inner(); | ||
| 317 | let state = T::state(); | ||
| 318 | |||
| 319 | regs.data_interrupts(false); | ||
| 320 | state.wake(); | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | impl<'d, T: Instance, P: Pins<T>, Dma> Drop for Sdmmc<'d, T, P, Dma> { | ||
| 325 | fn drop(&mut self) { | ||
| 326 | self.irq.disable(); | ||
| 327 | let inner = T::inner(); | ||
| 328 | unsafe { inner.on_drop() }; | ||
| 329 | self.pins.deconfigure(); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | pub struct SdmmcInner(pub(crate) RegBlock); | ||
| 334 | |||
| 335 | impl SdmmcInner { | ||
| 336 | /// # Safety | ||
| 337 | /// | ||
| 338 | /// Access to `regs` registers should be exclusive | ||
| 339 | unsafe fn new_inner(&self, kernel_clk: Hertz) -> Hertz { | ||
| 340 | let regs = self.0; | ||
| 341 | |||
| 342 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 343 | // the SDMMC_CK frequency must be less than 400 kHz. | ||
| 344 | let (clkdiv, clock) = unwrap!(clk_div(kernel_clk, 400_000)); | ||
| 345 | |||
| 346 | regs.clkcr().write(|w| { | ||
| 347 | w.set_widbus(0); | ||
| 348 | w.set_clkdiv(clkdiv); | ||
| 349 | w.set_pwrsav(false); | ||
| 350 | w.set_negedge(false); | ||
| 351 | w.set_hwfc_en(true); | ||
| 352 | }); | ||
| 353 | |||
| 354 | // Power off, writen 00: Clock to the card is stopped; | ||
| 355 | // D[7:0], CMD, and CK are driven high. | ||
| 356 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); | ||
| 357 | |||
| 358 | clock | ||
| 359 | } | ||
| 360 | |||
| 361 | /// Initializes card (if present) and sets the bus at the | ||
| 362 | /// specified frequency. | ||
| 363 | #[allow(clippy::too_many_arguments)] | ||
| 364 | async fn init_card( | ||
| 365 | &self, | ||
| 366 | freq: Hertz, | ||
| 367 | bus_width: BusWidth, | ||
| 368 | old_card: &mut Option<Card>, | ||
| 369 | signalling: &mut Signalling, | ||
| 370 | hclk: Hertz, | ||
| 371 | ker_ck: Hertz, | ||
| 372 | clock: &mut Hertz, | ||
| 373 | waker_reg: &AtomicWaker, | ||
| 374 | data_transfer_timeout: u32, | ||
| 375 | ) -> Result<(), Error> { | ||
| 376 | let regs = self.0; | ||
| 377 | |||
| 378 | // NOTE(unsafe) We have exclusive access to the peripheral | ||
| 379 | unsafe { | ||
| 380 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 381 | self.cmd(Cmd::idle(), false)?; | ||
| 382 | |||
| 383 | // Check if cards supports CMD8 (with pattern) | ||
| 384 | self.cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; | ||
| 385 | let r1 = regs.resp(0).read().cardstatus(); | ||
| 386 | |||
| 387 | let mut card = if r1 == 0x1AA { | ||
| 388 | // Card echoed back the pattern. Must be at least v2 | ||
| 389 | Card::default() | ||
| 390 | } else { | ||
| 391 | return Err(Error::UnsupportedCardVersion); | ||
| 392 | }; | ||
| 393 | |||
| 394 | let ocr = loop { | ||
| 395 | // Signal that next command is a app command | ||
| 396 | self.cmd(Cmd::app_cmd(0), false)?; // CMD55 | ||
| 397 | |||
| 398 | let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 | ||
| 399 | | CmdAppOper::HIGH_CAPACITY as u32 | ||
| 400 | | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; | ||
| 401 | |||
| 402 | // Initialize card | ||
| 403 | match self.cmd(Cmd::app_op_cmd(arg), false) { | ||
| 404 | // ACMD41 | ||
| 405 | Ok(_) => (), | ||
| 406 | Err(Error::Crc) => (), | ||
| 407 | Err(err) => return Err(err), | ||
| 408 | } | ||
| 409 | let ocr: OCR = regs.resp(0).read().cardstatus().into(); | ||
| 410 | if !ocr.is_busy() { | ||
| 411 | // Power up done | ||
| 412 | break ocr; | ||
| 413 | } | ||
| 414 | }; | ||
| 415 | |||
| 416 | if ocr.high_capacity() { | ||
| 417 | // Card is SDHC or SDXC or SDUC | ||
| 418 | card.card_type = CardCapacity::SDHC; | ||
| 419 | } else { | ||
| 420 | card.card_type = CardCapacity::SDSC; | ||
| 421 | } | ||
| 422 | card.ocr = ocr; | ||
| 423 | |||
| 424 | self.cmd(Cmd::all_send_cid(), false)?; // CMD2 | ||
| 425 | let cid0 = regs.resp(0).read().cardstatus() as u128; | ||
| 426 | let cid1 = regs.resp(1).read().cardstatus() as u128; | ||
| 427 | let cid2 = regs.resp(2).read().cardstatus() as u128; | ||
| 428 | let cid3 = regs.resp(3).read().cardstatus() as u128; | ||
| 429 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | ||
| 430 | card.cid = cid.into(); | ||
| 431 | |||
| 432 | self.cmd(Cmd::send_rel_addr(), false)?; | ||
| 433 | card.rca = regs.resp(0).read().cardstatus() >> 16; | ||
| 434 | |||
| 435 | self.cmd(Cmd::send_csd(card.rca << 16), false)?; | ||
| 436 | let csd0 = regs.resp(0).read().cardstatus() as u128; | ||
| 437 | let csd1 = regs.resp(1).read().cardstatus() as u128; | ||
| 438 | let csd2 = regs.resp(2).read().cardstatus() as u128; | ||
| 439 | let csd3 = regs.resp(3).read().cardstatus() as u128; | ||
| 440 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | ||
| 441 | card.csd = csd.into(); | ||
| 442 | |||
| 443 | self.select_card(Some(&card))?; | ||
| 444 | self.get_scr(&mut card, waker_reg, data_transfer_timeout) | ||
| 445 | .await?; | ||
| 446 | |||
| 447 | // Set bus width | ||
| 448 | let (width, acmd_arg) = match bus_width { | ||
| 449 | BusWidth::Eight => unimplemented!(), | ||
| 450 | BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), | ||
| 451 | _ => (BusWidth::One, 0), | ||
| 452 | }; | ||
| 453 | self.cmd(Cmd::app_cmd(card.rca << 16), false)?; | ||
| 454 | self.cmd(Cmd::cmd6(acmd_arg), false)?; | ||
| 455 | |||
| 456 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 457 | self.wait_idle(); | ||
| 458 | |||
| 459 | regs.clkcr().modify(|w| { | ||
| 460 | w.set_widbus(match width { | ||
| 461 | BusWidth::One => 0, | ||
| 462 | BusWidth::Four => 1, | ||
| 463 | BusWidth::Eight => 2, | ||
| 464 | _ => panic!("Invalid Bus Width"), | ||
| 465 | }) | ||
| 466 | }); | ||
| 467 | |||
| 468 | // Set Clock | ||
| 469 | if freq.0 <= 25_000_000 { | ||
| 470 | // Final clock frequency | ||
| 471 | self.clkcr_set_clkdiv(freq.0, width, hclk, ker_ck, clock)?; | ||
| 472 | } else { | ||
| 473 | // Switch to max clock for SDR12 | ||
| 474 | self.clkcr_set_clkdiv(25_000_000, width, hclk, ker_ck, clock)?; | ||
| 475 | } | ||
| 476 | |||
| 477 | // Read status | ||
| 478 | self.read_sd_status(&mut card, waker_reg, data_transfer_timeout) | ||
| 479 | .await?; | ||
| 480 | |||
| 481 | if freq.0 > 25_000_000 { | ||
| 482 | // Switch to SDR25 | ||
| 483 | *signalling = self | ||
| 484 | .switch_signalling_mode(Signalling::SDR25, waker_reg, data_transfer_timeout) | ||
| 485 | .await?; | ||
| 486 | |||
| 487 | if *signalling == Signalling::SDR25 { | ||
| 488 | // Set final clock frequency | ||
| 489 | self.clkcr_set_clkdiv(freq.0, width, hclk, ker_ck, clock)?; | ||
| 490 | |||
| 491 | if self.read_status(&card)?.state() != CurrentState::Transfer { | ||
| 492 | return Err(Error::SignalingSwitchFailed); | ||
| 493 | } | ||
| 494 | } | ||
| 495 | } | ||
| 496 | // Read status after signalling change | ||
| 497 | self.read_sd_status(&mut card, waker_reg, data_transfer_timeout) | ||
| 498 | .await?; | ||
| 499 | old_card.replace(card); | ||
| 500 | } | ||
| 501 | |||
| 502 | Ok(()) | ||
| 503 | } | ||
| 504 | |||
| 505 | async fn read_block( | ||
| 506 | &self, | ||
| 507 | block_idx: u32, | ||
| 508 | buffer: &mut [u32; 128], | ||
| 509 | capacity: CardCapacity, | ||
| 510 | waker_reg: &AtomicWaker, | ||
| 511 | data_transfer_timeout: u32, | ||
| 512 | ) -> Result<(), Error> { | ||
| 513 | // Always read 1 block of 512 bytes | ||
| 514 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 515 | let address = match capacity { | ||
| 516 | CardCapacity::SDSC => block_idx * 512, | ||
| 517 | _ => block_idx, | ||
| 518 | }; | ||
| 519 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 520 | |||
| 521 | let regs = self.0; | ||
| 522 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 523 | |||
| 524 | let buf_addr = buffer as *mut [u32; 128] as u32; | ||
| 525 | unsafe { | ||
| 526 | self.prepare_datapath_transfer( | ||
| 527 | buf_addr, | ||
| 528 | 512, | ||
| 529 | 9, | ||
| 530 | Dir::CardToHost, | ||
| 531 | data_transfer_timeout, | ||
| 532 | ); | ||
| 533 | self.data_interrupts(true); | ||
| 534 | } | ||
| 535 | self.cmd(Cmd::read_single_block(address), true)?; | ||
| 536 | |||
| 537 | let res = poll_fn(|cx| { | ||
| 538 | waker_reg.register(cx.waker()); | ||
| 539 | let status = unsafe { regs.sta().read() }; | ||
| 540 | |||
| 541 | if status.dcrcfail() { | ||
| 542 | return Poll::Ready(Err(Error::Crc)); | ||
| 543 | } else if status.dtimeout() { | ||
| 544 | return Poll::Ready(Err(Error::Timeout)); | ||
| 545 | } else if status.dataend() { | ||
| 546 | return Poll::Ready(Ok(())); | ||
| 547 | } | ||
| 548 | Poll::Pending | ||
| 549 | }) | ||
| 550 | .await; | ||
| 551 | self.clear_interrupt_flags(); | ||
| 552 | |||
| 553 | if res.is_ok() { | ||
| 554 | on_drop.defuse(); | ||
| 555 | unsafe { | ||
| 556 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 557 | } | ||
| 558 | } | ||
| 559 | res | ||
| 560 | } | ||
| 561 | |||
| 562 | async fn write_block( | ||
| 563 | &self, | ||
| 564 | block_idx: u32, | ||
| 565 | buffer: &[u32; 128], | ||
| 566 | card: &mut Card, | ||
| 567 | waker_reg: &AtomicWaker, | ||
| 568 | data_transfer_timeout: u32, | ||
| 569 | ) -> Result<(), Error> { | ||
| 570 | // Always read 1 block of 512 bytes | ||
| 571 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 572 | let address = match card.card_type { | ||
| 573 | CardCapacity::SDSC => block_idx * 512, | ||
| 574 | _ => block_idx, | ||
| 575 | }; | ||
| 576 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 577 | |||
| 578 | let regs = self.0; | ||
| 579 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 580 | |||
| 581 | let buf_addr = buffer as *const [u32; 128] as u32; | ||
| 582 | unsafe { | ||
| 583 | self.prepare_datapath_transfer( | ||
| 584 | buf_addr, | ||
| 585 | 512, | ||
| 586 | 9, | ||
| 587 | Dir::HostToCard, | ||
| 588 | data_transfer_timeout, | ||
| 589 | ); | ||
| 590 | self.data_interrupts(true); | ||
| 591 | } | ||
| 592 | self.cmd(Cmd::write_single_block(address), true)?; | ||
| 593 | |||
| 594 | let res = poll_fn(|cx| { | ||
| 595 | waker_reg.register(cx.waker()); | ||
| 596 | let status = unsafe { regs.sta().read() }; | ||
| 597 | |||
| 598 | if status.dcrcfail() { | ||
| 599 | return Poll::Ready(Err(Error::Crc)); | ||
| 600 | } else if status.dtimeout() { | ||
| 601 | return Poll::Ready(Err(Error::Timeout)); | ||
| 602 | } else if status.dataend() { | ||
| 603 | return Poll::Ready(Ok(())); | ||
| 604 | } | ||
| 605 | Poll::Pending | ||
| 606 | }) | ||
| 607 | .await; | ||
| 608 | self.clear_interrupt_flags(); | ||
| 609 | |||
| 610 | match res { | ||
| 611 | Ok(_) => { | ||
| 612 | on_drop.defuse(); | ||
| 613 | unsafe { | ||
| 614 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 615 | } | ||
| 616 | // TODO: Make this configurable | ||
| 617 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 618 | |||
| 619 | // Try to read card status (ACMD13) | ||
| 620 | while timeout > 0 { | ||
| 621 | match self | ||
| 622 | .read_sd_status(card, waker_reg, data_transfer_timeout) | ||
| 623 | .await | ||
| 624 | { | ||
| 625 | Ok(_) => return Ok(()), | ||
| 626 | Err(Error::Timeout) => (), // Try again | ||
| 627 | Err(e) => return Err(e), | ||
| 628 | } | ||
| 629 | timeout -= 1; | ||
| 630 | } | ||
| 631 | Err(Error::SoftwareTimeout) | ||
| 632 | } | ||
| 633 | Err(e) => Err(e), | ||
| 634 | } | ||
| 635 | } | ||
| 636 | |||
| 637 | /// Get the current SDMMC bus clock | ||
| 638 | //pub fn clock(&self) -> Hertz { | ||
| 639 | // self.clock | ||
| 640 | //} | ||
| 641 | |||
| 642 | /// Data transfer is in progress | ||
| 643 | #[inline(always)] | ||
| 644 | fn data_active(&self) -> bool { | ||
| 645 | let regs = self.0; | ||
| 646 | |||
| 647 | // NOTE(unsafe) Atomic read with no side-effects | ||
| 648 | unsafe { | ||
| 649 | let status = regs.sta().read(); | ||
| 650 | cfg_if::cfg_if! { | ||
| 651 | if #[cfg(sdmmc_v1)] { | ||
| 652 | status.rxact() || status.txact() | ||
| 653 | } else if #[cfg(sdmmc_v2)] { | ||
| 654 | status.dpsmact() | ||
| 655 | } | ||
| 656 | } | ||
| 657 | } | ||
| 658 | } | ||
| 659 | |||
| 660 | /// Coammand transfer is in progress | ||
| 661 | #[inline(always)] | ||
| 662 | fn cmd_active(&self) -> bool { | ||
| 663 | let regs = self.0; | ||
| 664 | |||
| 665 | // NOTE(unsafe) Atomic read with no side-effects | ||
| 666 | unsafe { | ||
| 667 | let status = regs.sta().read(); | ||
| 668 | cfg_if::cfg_if! { | ||
| 669 | if #[cfg(sdmmc_v1)] { | ||
| 670 | status.cmdact() | ||
| 671 | } else if #[cfg(sdmmc_v2)] { | ||
| 672 | status.cpsmact() | ||
| 673 | } | ||
| 674 | } | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) | ||
| 679 | #[inline(always)] | ||
| 680 | fn wait_idle(&self) { | ||
| 681 | while self.data_active() || self.cmd_active() {} | ||
| 682 | } | ||
| 683 | |||
| 684 | /// # Safety | ||
| 685 | /// | ||
| 686 | /// `buffer_addr` must be valid for the whole transfer and word aligned | ||
| 687 | unsafe fn prepare_datapath_transfer( | ||
| 688 | &self, | ||
| 689 | buffer_addr: u32, | ||
| 690 | length_bytes: u32, | ||
| 691 | block_size: u8, | ||
| 692 | direction: Dir, | ||
| 693 | data_transfer_timeout: u32, | ||
| 694 | ) { | ||
| 695 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | ||
| 696 | let regs = self.0; | ||
| 697 | |||
| 698 | let dtdir = match direction { | ||
| 699 | Dir::CardToHost => true, | ||
| 700 | Dir::HostToCard => false, | ||
| 701 | }; | ||
| 702 | |||
| 703 | // Command AND Data state machines must be idle | ||
| 704 | self.wait_idle(); | ||
| 705 | self.clear_interrupt_flags(); | ||
| 706 | |||
| 707 | // NOTE(unsafe) We have exclusive access to the regisers | ||
| 708 | |||
| 709 | regs.dtimer() | ||
| 710 | .write(|w| w.set_datatime(data_transfer_timeout)); | ||
| 711 | regs.dlen().write(|w| w.set_datalength(length_bytes)); | ||
| 712 | |||
| 713 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer_addr)); | ||
| 714 | regs.idmactrlr().modify(|w| w.set_idmaen(true)); | ||
| 715 | regs.dctrl().modify(|w| { | ||
| 716 | w.set_dblocksize(block_size); | ||
| 717 | w.set_dtdir(dtdir); | ||
| 718 | }); | ||
| 719 | } | ||
| 720 | |||
| 721 | /// Sets the CLKDIV field in CLKCR. Updates clock field in self | ||
| 722 | fn clkcr_set_clkdiv( | ||
| 723 | &self, | ||
| 724 | freq: u32, | ||
| 725 | width: BusWidth, | ||
| 726 | hclk: Hertz, | ||
| 727 | ker_ck: Hertz, | ||
| 728 | clock: &mut Hertz, | ||
| 729 | ) -> Result<(), Error> { | ||
| 730 | let regs = self.0; | ||
| 731 | |||
| 732 | let (clkdiv, new_clock) = clk_div(ker_ck, freq)?; | ||
| 733 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 | ||
| 734 | // Section 55.5.8 | ||
| 735 | let sdmmc_bus_bandwidth = new_clock.0 * (width as u32); | ||
| 736 | assert!(hclk.0 > 3 * sdmmc_bus_bandwidth / 32); | ||
| 737 | *clock = new_clock; | ||
| 738 | |||
| 739 | // NOTE(unsafe) We have exclusive access to the regblock | ||
| 740 | unsafe { | ||
| 741 | // CPSMACT and DPSMACT must be 0 to set CLKDIV | ||
| 742 | self.wait_idle(); | ||
| 743 | regs.clkcr().modify(|w| w.set_clkdiv(clkdiv)); | ||
| 744 | } | ||
| 745 | |||
| 746 | Ok(()) | ||
| 747 | } | ||
| 748 | |||
| 749 | /// Switch mode using CMD6. | ||
| 750 | /// | ||
| 751 | /// Attempt to set a new signalling mode. The selected | ||
| 752 | /// signalling mode is returned. Expects the current clock | ||
| 753 | /// frequency to be > 12.5MHz. | ||
| 754 | async fn switch_signalling_mode( | ||
| 755 | &self, | ||
| 756 | signalling: Signalling, | ||
| 757 | waker_reg: &AtomicWaker, | ||
| 758 | data_transfer_timeout: u32, | ||
| 759 | ) -> Result<Signalling, Error> { | ||
| 760 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 761 | // necessary" | ||
| 762 | |||
| 763 | let set_function = 0x8000_0000 | ||
| 764 | | match signalling { | ||
| 765 | // See PLSS v7_10 Table 4-11 | ||
| 766 | Signalling::DDR50 => 0xFF_FF04, | ||
| 767 | Signalling::SDR104 => 0xFF_1F03, | ||
| 768 | Signalling::SDR50 => 0xFF_1F02, | ||
| 769 | Signalling::SDR25 => 0xFF_FF01, | ||
| 770 | Signalling::SDR12 => 0xFF_FF00, | ||
| 771 | }; | ||
| 772 | |||
| 773 | let mut status = [0u32; 16]; | ||
| 774 | let status_addr = &mut status as *mut [u32; 16] as u32; | ||
| 775 | |||
| 776 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 777 | let regs = self.0; | ||
| 778 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 779 | |||
| 780 | unsafe { | ||
| 781 | self.prepare_datapath_transfer( | ||
| 782 | status_addr, | ||
| 783 | 64, | ||
| 784 | 6, | ||
| 785 | Dir::CardToHost, | ||
| 786 | data_transfer_timeout, | ||
| 787 | ); | ||
| 788 | self.data_interrupts(true); | ||
| 789 | } | ||
| 790 | self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 | ||
| 791 | |||
| 792 | let res = poll_fn(|cx| { | ||
| 793 | waker_reg.register(cx.waker()); | ||
| 794 | let status = unsafe { regs.sta().read() }; | ||
| 795 | |||
| 796 | if status.dcrcfail() { | ||
| 797 | return Poll::Ready(Err(Error::Crc)); | ||
| 798 | } else if status.dtimeout() { | ||
| 799 | return Poll::Ready(Err(Error::Timeout)); | ||
| 800 | } else if status.dataend() { | ||
| 801 | return Poll::Ready(Ok(())); | ||
| 802 | } | ||
| 803 | Poll::Pending | ||
| 804 | }) | ||
| 805 | .await; | ||
| 806 | self.clear_interrupt_flags(); | ||
| 807 | |||
| 808 | // Host is allowed to use the new functions at least 8 | ||
| 809 | // clocks after the end of the switch command | ||
| 810 | // transaction. We know the current clock period is < 80ns, | ||
| 811 | // so a total delay of 640ns is required here | ||
| 812 | for _ in 0..300 { | ||
| 813 | cortex_m::asm::nop(); | ||
| 814 | } | ||
| 815 | |||
| 816 | match res { | ||
| 817 | Ok(_) => { | ||
| 818 | on_drop.defuse(); | ||
| 819 | unsafe { | ||
| 820 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 821 | } | ||
| 822 | // Function Selection of Function Group 1 | ||
| 823 | let selection = (u32::from_be(status[4]) >> 24) & 0xF; | ||
| 824 | |||
| 825 | match selection { | ||
| 826 | 0 => Ok(Signalling::SDR12), | ||
| 827 | 1 => Ok(Signalling::SDR25), | ||
| 828 | 2 => Ok(Signalling::SDR50), | ||
| 829 | 3 => Ok(Signalling::SDR104), | ||
| 830 | 4 => Ok(Signalling::DDR50), | ||
| 831 | _ => Err(Error::UnsupportedCardType), | ||
| 832 | } | ||
| 833 | } | ||
| 834 | Err(e) => Err(e), | ||
| 835 | } | ||
| 836 | } | ||
| 837 | |||
| 838 | /// Query the card status (CMD13, returns R1) | ||
| 839 | /// | ||
| 840 | fn read_status(&self, card: &Card) -> Result<CardStatus, Error> { | ||
| 841 | let regs = self.0; | ||
| 842 | let rca = card.rca; | ||
| 843 | |||
| 844 | self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 | ||
| 845 | |||
| 846 | // NOTE(unsafe) Atomic read with no side-effects | ||
| 847 | let r1 = unsafe { regs.resp(0).read().cardstatus() }; | ||
| 848 | Ok(r1.into()) | ||
| 849 | } | ||
| 850 | |||
| 851 | /// Reads the SD Status (ACMD13) | ||
| 852 | async fn read_sd_status( | ||
| 853 | &self, | ||
| 854 | card: &mut Card, | ||
| 855 | waker_reg: &AtomicWaker, | ||
| 856 | data_transfer_timeout: u32, | ||
| 857 | ) -> Result<(), Error> { | ||
| 858 | let rca = card.rca; | ||
| 859 | self.cmd(Cmd::set_block_length(64), false)?; // CMD16 | ||
| 860 | self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP | ||
| 861 | |||
| 862 | let mut status = [0u32; 16]; | ||
| 863 | let status_addr = &mut status as *mut [u32; 16] as u32; | ||
| 864 | |||
| 865 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 866 | let regs = self.0; | ||
| 867 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 868 | |||
| 869 | unsafe { | ||
| 870 | self.prepare_datapath_transfer( | ||
| 871 | status_addr, | ||
| 872 | 64, | ||
| 873 | 6, | ||
| 874 | Dir::CardToHost, | ||
| 875 | data_transfer_timeout, | ||
| 876 | ); | ||
| 877 | self.data_interrupts(true); | ||
| 878 | } | ||
| 879 | self.cmd(Cmd::card_status(0), true)?; | ||
| 880 | |||
| 881 | let res = poll_fn(|cx| { | ||
| 882 | waker_reg.register(cx.waker()); | ||
| 883 | let status = unsafe { regs.sta().read() }; | ||
| 884 | |||
| 885 | if status.dcrcfail() { | ||
| 886 | return Poll::Ready(Err(Error::Crc)); | ||
| 887 | } else if status.dtimeout() { | ||
| 888 | return Poll::Ready(Err(Error::Timeout)); | ||
| 889 | } else if status.dataend() { | ||
| 890 | return Poll::Ready(Ok(())); | ||
| 891 | } | ||
| 892 | Poll::Pending | ||
| 893 | }) | ||
| 894 | .await; | ||
| 895 | self.clear_interrupt_flags(); | ||
| 896 | |||
| 897 | if res.is_ok() { | ||
| 898 | on_drop.defuse(); | ||
| 899 | unsafe { | ||
| 900 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 901 | } | ||
| 902 | for byte in status.iter_mut() { | ||
| 903 | *byte = u32::from_be(*byte); | ||
| 904 | } | ||
| 905 | card.status = status.into(); | ||
| 906 | } | ||
| 907 | res | ||
| 908 | } | ||
| 909 | |||
| 910 | /// Select one card and place it into the _Tranfer State_ | ||
| 911 | /// | ||
| 912 | /// If `None` is specifed for `card`, all cards are put back into | ||
| 913 | /// _Stand-by State_ | ||
| 914 | fn select_card(&self, card: Option<&Card>) -> Result<(), Error> { | ||
| 915 | // Determine Relative Card Address (RCA) of given card | ||
| 916 | let rca = card.map(|c| c.rca << 16).unwrap_or(0); | ||
| 917 | |||
| 918 | let r = self.cmd(Cmd::sel_desel_card(rca), false); | ||
| 919 | match (r, rca) { | ||
| 920 | (Err(Error::Timeout), 0) => Ok(()), | ||
| 921 | _ => r, | ||
| 922 | } | ||
| 923 | } | ||
| 924 | |||
| 925 | /// Clear flags in interrupt clear register | ||
| 926 | #[inline(always)] | ||
| 927 | fn clear_interrupt_flags(&self) { | ||
| 928 | let regs = self.0; | ||
| 929 | // NOTE(unsafe) Atomic write | ||
| 930 | unsafe { | ||
| 931 | regs.icr().write(|w| { | ||
| 932 | w.set_ccrcfailc(true); | ||
| 933 | w.set_dcrcfailc(true); | ||
| 934 | w.set_ctimeoutc(true); | ||
| 935 | w.set_dtimeoutc(true); | ||
| 936 | w.set_txunderrc(true); | ||
| 937 | w.set_rxoverrc(true); | ||
| 938 | w.set_cmdrendc(true); | ||
| 939 | w.set_cmdsentc(true); | ||
| 940 | w.set_dataendc(true); | ||
| 941 | w.set_dbckendc(true); | ||
| 942 | w.set_sdioitc(true); | ||
| 943 | |||
| 944 | cfg_if::cfg_if! { | ||
| 945 | if #[cfg(sdmmc_v1)] { | ||
| 946 | w.set_stbiterrc(true); | ||
| 947 | w.set_ceataendc(true); | ||
| 948 | } else if #[cfg(sdmmc_v2)] { | ||
| 949 | w.set_dholdc(true); | ||
| 950 | w.set_dabortc(true); | ||
| 951 | w.set_busyd0endc(true); | ||
| 952 | w.set_ackfailc(true); | ||
| 953 | w.set_acktimeoutc(true); | ||
| 954 | w.set_vswendc(true); | ||
| 955 | w.set_ckstopc(true); | ||
| 956 | w.set_idmatec(true); | ||
| 957 | w.set_idmabtcc(true); | ||
| 958 | } | ||
| 959 | } | ||
| 960 | }); | ||
| 961 | } | ||
| 962 | } | ||
| 963 | |||
| 964 | /// Enables the interrupts for data transfer | ||
| 965 | #[inline(always)] | ||
| 966 | fn data_interrupts(&self, enable: bool) { | ||
| 967 | let regs = self.0; | ||
| 968 | // NOTE(unsafe) Atomic write | ||
| 969 | unsafe { | ||
| 970 | regs.mask().write(|w| { | ||
| 971 | w.set_dcrcfailie(enable); | ||
| 972 | w.set_dtimeoutie(enable); | ||
| 973 | w.set_dataendie(enable); | ||
| 974 | |||
| 975 | #[cfg(sdmmc_v2)] | ||
| 976 | w.set_dabortie(enable); | ||
| 977 | }); | ||
| 978 | } | ||
| 979 | } | ||
| 980 | |||
| 981 | async fn get_scr( | ||
| 982 | &self, | ||
| 983 | card: &mut Card, | ||
| 984 | waker_reg: &AtomicWaker, | ||
| 985 | data_transfer_timeout: u32, | ||
| 986 | ) -> Result<(), Error> { | ||
| 987 | // Read the the 64-bit SCR register | ||
| 988 | self.cmd(Cmd::set_block_length(8), false)?; // CMD16 | ||
| 989 | self.cmd(Cmd::app_cmd(card.rca << 16), false)?; | ||
| 990 | |||
| 991 | let mut scr = [0u32; 2]; | ||
| 992 | let scr_addr = &mut scr as *mut u32 as u32; | ||
| 993 | |||
| 994 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 995 | let regs = self.0; | ||
| 996 | let on_drop = OnDrop::new(move || unsafe { self.on_drop() }); | ||
| 997 | |||
| 998 | unsafe { | ||
| 999 | self.prepare_datapath_transfer(scr_addr, 8, 3, Dir::CardToHost, data_transfer_timeout); | ||
| 1000 | self.data_interrupts(true); | ||
| 1001 | } | ||
| 1002 | self.cmd(Cmd::cmd51(), true)?; | ||
| 1003 | |||
| 1004 | let res = poll_fn(|cx| { | ||
| 1005 | waker_reg.register(cx.waker()); | ||
| 1006 | let status = unsafe { regs.sta().read() }; | ||
| 1007 | |||
| 1008 | if status.dcrcfail() { | ||
| 1009 | return Poll::Ready(Err(Error::Crc)); | ||
| 1010 | } else if status.dtimeout() { | ||
| 1011 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1012 | } else if status.dataend() { | ||
| 1013 | return Poll::Ready(Ok(())); | ||
| 1014 | } | ||
| 1015 | Poll::Pending | ||
| 1016 | }) | ||
| 1017 | .await; | ||
| 1018 | self.clear_interrupt_flags(); | ||
| 1019 | |||
| 1020 | if res.is_ok() { | ||
| 1021 | on_drop.defuse(); | ||
| 1022 | |||
| 1023 | unsafe { | ||
| 1024 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 1025 | let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); | ||
| 1026 | card.scr = SCR(u64::from_be_bytes(*scr_bytes)); | ||
| 1027 | } | ||
| 1028 | } | ||
| 1029 | res | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | /// Send command to card | ||
| 1033 | fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { | ||
| 1034 | let regs = self.0; | ||
| 1035 | |||
| 1036 | self.clear_interrupt_flags(); | ||
| 1037 | // NOTE(safety) Atomic operations | ||
| 1038 | unsafe { | ||
| 1039 | // CP state machine must be idle | ||
| 1040 | while self.cmd_active() {} | ||
| 1041 | |||
| 1042 | // Command arg | ||
| 1043 | regs.arg().write(|w| w.set_cmdarg(cmd.arg)); | ||
| 1044 | |||
| 1045 | // Command index and start CP State Machine | ||
| 1046 | regs.cmd().write(|w| { | ||
| 1047 | w.set_waitint(false); | ||
| 1048 | w.set_waitresp(cmd.resp as u8); | ||
| 1049 | w.set_cmdindex(cmd.cmd); | ||
| 1050 | w.set_cpsmen(true); | ||
| 1051 | |||
| 1052 | cfg_if::cfg_if! { | ||
| 1053 | if #[cfg(sdmmc_v2)] { | ||
| 1054 | // Special mode in CP State Machine | ||
| 1055 | // CMD12: Stop Transmission | ||
| 1056 | let cpsm_stop_transmission = cmd.cmd == 12; | ||
| 1057 | w.set_cmdstop(cpsm_stop_transmission); | ||
| 1058 | w.set_cmdtrans(data); | ||
| 1059 | } | ||
| 1060 | } | ||
| 1061 | }); | ||
| 1062 | |||
| 1063 | let mut status; | ||
| 1064 | if cmd.resp == Response::None { | ||
| 1065 | // Wait for CMDSENT or a timeout | ||
| 1066 | while { | ||
| 1067 | status = regs.sta().read(); | ||
| 1068 | !(status.ctimeout() || status.cmdsent()) | ||
| 1069 | } {} | ||
| 1070 | } else { | ||
| 1071 | // Wait for CMDREND or CCRCFAIL or a timeout | ||
| 1072 | while { | ||
| 1073 | status = regs.sta().read(); | ||
| 1074 | !(status.ctimeout() || status.cmdrend() || status.ccrcfail()) | ||
| 1075 | } {} | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | if status.ctimeout() { | ||
| 1079 | return Err(Error::Timeout); | ||
| 1080 | } else if status.ccrcfail() { | ||
| 1081 | return Err(Error::Crc); | ||
| 1082 | } | ||
| 1083 | Ok(()) | ||
| 1084 | } | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | /// # Safety | ||
| 1088 | /// | ||
| 1089 | /// Ensure that `regs` has exclusive access to the regblocks | ||
| 1090 | unsafe fn on_drop(&self) { | ||
| 1091 | let regs = self.0; | ||
| 1092 | if self.data_active() { | ||
| 1093 | self.clear_interrupt_flags(); | ||
| 1094 | // Send abort | ||
| 1095 | // CP state machine must be idle | ||
| 1096 | while self.cmd_active() {} | ||
| 1097 | |||
| 1098 | // Command arg | ||
| 1099 | regs.arg().write(|w| w.set_cmdarg(0)); | ||
| 1100 | |||
| 1101 | // Command index and start CP State Machine | ||
| 1102 | regs.cmd().write(|w| { | ||
| 1103 | w.set_waitint(false); | ||
| 1104 | w.set_waitresp(Response::Short as u8); | ||
| 1105 | w.set_cmdindex(12); | ||
| 1106 | w.set_cpsmen(true); | ||
| 1107 | |||
| 1108 | cfg_if::cfg_if! { | ||
| 1109 | if #[cfg(sdmmc_v2)] { | ||
| 1110 | w.set_cmdstop(true); | ||
| 1111 | w.set_cmdtrans(false); | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | }); | ||
| 1115 | |||
| 1116 | // Wait for the abort | ||
| 1117 | while self.data_active() {} | ||
| 1118 | } | ||
| 1119 | self.data_interrupts(false); | ||
| 1120 | self.clear_interrupt_flags(); | ||
| 1121 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 1122 | } | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | /// SD card Commands | ||
| 1126 | impl Cmd { | ||
| 1127 | const fn new(cmd: u8, arg: u32, resp: Response) -> Cmd { | ||
| 1128 | Cmd { cmd, arg, resp } | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | /// CMD0: Idle | ||
| 1132 | const fn idle() -> Cmd { | ||
| 1133 | Cmd::new(0, 0, Response::None) | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | /// CMD2: Send CID | ||
| 1137 | const fn all_send_cid() -> Cmd { | ||
| 1138 | Cmd::new(2, 0, Response::Long) | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | /// CMD3: Send Relative Address | ||
| 1142 | const fn send_rel_addr() -> Cmd { | ||
| 1143 | Cmd::new(3, 0, Response::Short) | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | /// CMD6: Switch Function Command | ||
| 1147 | /// ACMD6: Bus Width | ||
| 1148 | const fn cmd6(arg: u32) -> Cmd { | ||
| 1149 | Cmd::new(6, arg, Response::Short) | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | /// CMD7: Select one card and put it into the _Tranfer State_ | ||
| 1153 | const fn sel_desel_card(rca: u32) -> Cmd { | ||
| 1154 | Cmd::new(7, rca, Response::Short) | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | /// CMD8: | ||
| 1158 | const fn hs_send_ext_csd(arg: u32) -> Cmd { | ||
| 1159 | Cmd::new(8, arg, Response::Short) | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | /// CMD9: | ||
| 1163 | const fn send_csd(rca: u32) -> Cmd { | ||
| 1164 | Cmd::new(9, rca, Response::Long) | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | /// CMD12: | ||
| 1168 | //const fn stop_transmission() -> Cmd { | ||
| 1169 | // Cmd::new(12, 0, Response::Short) | ||
| 1170 | //} | ||
| 1171 | |||
| 1172 | /// CMD13: Ask card to send status register | ||
| 1173 | /// ACMD13: SD Status | ||
| 1174 | const fn card_status(rca: u32) -> Cmd { | ||
| 1175 | Cmd::new(13, rca, Response::Short) | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | /// CMD16: | ||
| 1179 | const fn set_block_length(blocklen: u32) -> Cmd { | ||
| 1180 | Cmd::new(16, blocklen, Response::Short) | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | /// CMD17: Block Read | ||
| 1184 | const fn read_single_block(addr: u32) -> Cmd { | ||
| 1185 | Cmd::new(17, addr, Response::Short) | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | /// CMD18: Multiple Block Read | ||
| 1189 | //const fn read_multiple_blocks(addr: u32) -> Cmd { | ||
| 1190 | // Cmd::new(18, addr, Response::Short) | ||
| 1191 | //} | ||
| 1192 | |||
| 1193 | /// CMD24: Block Write | ||
| 1194 | const fn write_single_block(addr: u32) -> Cmd { | ||
| 1195 | Cmd::new(24, addr, Response::Short) | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | const fn app_op_cmd(arg: u32) -> Cmd { | ||
| 1199 | Cmd::new(41, arg, Response::Short) | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | const fn cmd51() -> Cmd { | ||
| 1203 | Cmd::new(51, 0, Response::Short) | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | /// App Command. Indicates that next command will be a app command | ||
| 1207 | const fn app_cmd(rca: u32) -> Cmd { | ||
| 1208 | Cmd::new(55, rca, Response::Short) | ||
| 1209 | } | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | ////////////////////////////////////////////////////// | ||
| 1213 | |||
| 1214 | pub(crate) mod sealed { | ||
| 1215 | use super::*; | ||
| 1216 | |||
| 1217 | pub trait Instance { | ||
| 1218 | type Interrupt: Interrupt; | ||
| 1219 | |||
| 1220 | fn inner() -> SdmmcInner; | ||
| 1221 | fn state() -> &'static AtomicWaker; | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | pub trait Pins<T: Instance> {} | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | pub trait Instance: sealed::Instance + 'static {} | ||
| 1228 | pin_trait!(CkPin, Instance); | ||
| 1229 | pin_trait!(CmdPin, Instance); | ||
| 1230 | pin_trait!(D0Pin, Instance); | ||
| 1231 | pin_trait!(D1Pin, Instance); | ||
| 1232 | pin_trait!(D2Pin, Instance); | ||
| 1233 | pin_trait!(D3Pin, Instance); | ||
| 1234 | pin_trait!(D4Pin, Instance); | ||
| 1235 | pin_trait!(D5Pin, Instance); | ||
| 1236 | pin_trait!(D6Pin, Instance); | ||
| 1237 | pin_trait!(D7Pin, Instance); | ||
| 1238 | |||
| 1239 | dma_trait!(SdioDma, Instance); | ||
| 1240 | |||
| 1241 | pub trait Pins<T: Instance>: sealed::Pins<T> + 'static { | ||
| 1242 | const BUSWIDTH: BusWidth; | ||
| 1243 | |||
| 1244 | fn configure(&mut self); | ||
| 1245 | fn deconfigure(&mut self); | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | impl<T, CLK, CMD, D0, D1, D2, D3> sealed::Pins<T> for (CLK, CMD, D0, D1, D2, D3) | ||
| 1249 | where | ||
| 1250 | T: Instance, | ||
| 1251 | CLK: CkPin<T>, | ||
| 1252 | CMD: CmdPin<T>, | ||
| 1253 | D0: D0Pin<T>, | ||
| 1254 | D1: D1Pin<T>, | ||
| 1255 | D2: D2Pin<T>, | ||
| 1256 | D3: D3Pin<T>, | ||
| 1257 | { | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | impl<T, CLK, CMD, D0> sealed::Pins<T> for (CLK, CMD, D0) | ||
| 1261 | where | ||
| 1262 | T: Instance, | ||
| 1263 | CLK: CkPin<T>, | ||
| 1264 | CMD: CmdPin<T>, | ||
| 1265 | D0: D0Pin<T>, | ||
| 1266 | { | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | impl<T, CLK, CMD, D0, D1, D2, D3> Pins<T> for (CLK, CMD, D0, D1, D2, D3) | ||
| 1270 | where | ||
| 1271 | T: Instance, | ||
| 1272 | CLK: CkPin<T>, | ||
| 1273 | CMD: CmdPin<T>, | ||
| 1274 | D0: D0Pin<T>, | ||
| 1275 | D1: D1Pin<T>, | ||
| 1276 | D2: D2Pin<T>, | ||
| 1277 | D3: D3Pin<T>, | ||
| 1278 | { | ||
| 1279 | const BUSWIDTH: BusWidth = BusWidth::Four; | ||
| 1280 | |||
| 1281 | fn configure(&mut self) { | ||
| 1282 | let (clk_pin, cmd_pin, d0_pin, d1_pin, d2_pin, d3_pin) = self; | ||
| 1283 | |||
| 1284 | critical_section::with(|_| unsafe { | ||
| 1285 | clk_pin.set_as_af_pull(clk_pin.af_num(), AFType::OutputPushPull, Pull::None); | ||
| 1286 | cmd_pin.set_as_af_pull(cmd_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1287 | d0_pin.set_as_af_pull(d0_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1288 | d1_pin.set_as_af_pull(d1_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1289 | d2_pin.set_as_af_pull(d2_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1290 | d3_pin.set_as_af_pull(d3_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1291 | |||
| 1292 | clk_pin.set_speed(Speed::VeryHigh); | ||
| 1293 | cmd_pin.set_speed(Speed::VeryHigh); | ||
| 1294 | d0_pin.set_speed(Speed::VeryHigh); | ||
| 1295 | d1_pin.set_speed(Speed::VeryHigh); | ||
| 1296 | d2_pin.set_speed(Speed::VeryHigh); | ||
| 1297 | d3_pin.set_speed(Speed::VeryHigh); | ||
| 1298 | }); | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | fn deconfigure(&mut self) { | ||
| 1302 | let (clk_pin, cmd_pin, d0_pin, d1_pin, d2_pin, d3_pin) = self; | ||
| 1303 | |||
| 1304 | critical_section::with(|_| unsafe { | ||
| 1305 | clk_pin.set_as_disconnected(); | ||
| 1306 | cmd_pin.set_as_disconnected(); | ||
| 1307 | d0_pin.set_as_disconnected(); | ||
| 1308 | d1_pin.set_as_disconnected(); | ||
| 1309 | d2_pin.set_as_disconnected(); | ||
| 1310 | d3_pin.set_as_disconnected(); | ||
| 1311 | }); | ||
| 1312 | } | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | impl<T, CLK, CMD, D0> Pins<T> for (CLK, CMD, D0) | ||
| 1316 | where | ||
| 1317 | T: Instance, | ||
| 1318 | CLK: CkPin<T>, | ||
| 1319 | CMD: CmdPin<T>, | ||
| 1320 | D0: D0Pin<T>, | ||
| 1321 | { | ||
| 1322 | const BUSWIDTH: BusWidth = BusWidth::One; | ||
| 1323 | |||
| 1324 | fn configure(&mut self) { | ||
| 1325 | let (clk_pin, cmd_pin, d0_pin) = self; | ||
| 1326 | |||
| 1327 | critical_section::with(|_| unsafe { | ||
| 1328 | clk_pin.set_as_af_pull(clk_pin.af_num(), AFType::OutputPushPull, Pull::None); | ||
| 1329 | cmd_pin.set_as_af_pull(cmd_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1330 | d0_pin.set_as_af_pull(d0_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1331 | |||
| 1332 | clk_pin.set_speed(Speed::VeryHigh); | ||
| 1333 | cmd_pin.set_speed(Speed::VeryHigh); | ||
| 1334 | d0_pin.set_speed(Speed::VeryHigh); | ||
| 1335 | }); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | fn deconfigure(&mut self) { | ||
| 1339 | let (clk_pin, cmd_pin, d0_pin) = self; | ||
| 1340 | |||
| 1341 | critical_section::with(|_| unsafe { | ||
| 1342 | clk_pin.set_as_disconnected(); | ||
| 1343 | cmd_pin.set_as_disconnected(); | ||
| 1344 | d0_pin.set_as_disconnected(); | ||
| 1345 | }); | ||
| 1346 | } | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | foreach_peripheral!( | ||
| 1350 | (sdmmc, $inst:ident) => { | ||
| 1351 | impl sealed::Instance for peripherals::$inst { | ||
| 1352 | type Interrupt = crate::interrupt::$inst; | ||
| 1353 | |||
| 1354 | fn inner() -> SdmmcInner { | ||
| 1355 | const INNER: SdmmcInner = SdmmcInner(crate::pac::$inst); | ||
| 1356 | INNER | ||
| 1357 | } | ||
| 1358 | |||
| 1359 | fn state() -> &'static ::embassy::waitqueue::AtomicWaker { | ||
| 1360 | static WAKER: ::embassy::waitqueue::AtomicWaker = ::embassy::waitqueue::AtomicWaker::new(); | ||
| 1361 | &WAKER | ||
| 1362 | } | ||
| 1363 | } | ||
| 1364 | |||
| 1365 | impl Instance for peripherals::$inst {} | ||
| 1366 | }; | ||
| 1367 | ); | ||
| 1368 | |||
| 1369 | #[cfg(feature = "sdmmc-rs")] | ||
| 1370 | mod sdmmc_rs { | ||
| 1371 | use super::*; | ||
| 1372 | use core::future::Future; | ||
| 1373 | use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx}; | ||
| 1374 | |||
| 1375 | impl<'d, T: Instance, P: Pins<T>> BlockDevice for Sdmmc<'d, T, P> { | ||
| 1376 | type Error = Error; | ||
| 1377 | type ReadFuture<'a> | ||
| 1378 | where | ||
| 1379 | Self: 'a, | ||
| 1380 | = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 1381 | type WriteFuture<'a> | ||
| 1382 | where | ||
| 1383 | Self: 'a, | ||
| 1384 | = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 1385 | |||
| 1386 | fn read<'a>( | ||
| 1387 | &'a mut self, | ||
| 1388 | blocks: &'a mut [Block], | ||
| 1389 | start_block_idx: BlockIdx, | ||
| 1390 | _reason: &str, | ||
| 1391 | ) -> Self::ReadFuture<'a> { | ||
| 1392 | async move { | ||
| 1393 | let card_capacity = self.card()?.card_type; | ||
| 1394 | let inner = T::inner(); | ||
| 1395 | let state = T::state(); | ||
| 1396 | let mut address = start_block_idx.0; | ||
| 1397 | |||
| 1398 | for block in blocks.iter_mut() { | ||
| 1399 | let block: &mut [u8; 512] = &mut block.contents; | ||
| 1400 | |||
| 1401 | // NOTE(unsafe) Block uses align(4) | ||
| 1402 | let buf = unsafe { &mut *(block as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 1403 | inner | ||
| 1404 | .read_block( | ||
| 1405 | address, | ||
| 1406 | buf, | ||
| 1407 | card_capacity, | ||
| 1408 | state, | ||
| 1409 | self.config.data_transfer_timeout, | ||
| 1410 | ) | ||
| 1411 | .await?; | ||
| 1412 | address += 1; | ||
| 1413 | } | ||
| 1414 | Ok(()) | ||
| 1415 | } | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | fn write<'a>( | ||
| 1419 | &'a mut self, | ||
| 1420 | blocks: &'a [Block], | ||
| 1421 | start_block_idx: BlockIdx, | ||
| 1422 | ) -> Self::WriteFuture<'a> { | ||
| 1423 | async move { | ||
| 1424 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 1425 | let inner = T::inner(); | ||
| 1426 | let state = T::state(); | ||
| 1427 | let mut address = start_block_idx.0; | ||
| 1428 | |||
| 1429 | for block in blocks.iter() { | ||
| 1430 | let block: &[u8; 512] = &block.contents; | ||
| 1431 | |||
| 1432 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1433 | let buf = unsafe { &*(block as *const [u8; 512] as *const [u32; 128]) }; | ||
| 1434 | inner | ||
| 1435 | .write_block(address, buf, card, state, self.config.data_transfer_timeout) | ||
| 1436 | .await?; | ||
| 1437 | address += 1; | ||
| 1438 | } | ||
| 1439 | Ok(()) | ||
| 1440 | } | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | fn num_blocks(&self) -> Result<BlockCount, Self::Error> { | ||
| 1444 | let card = self.card()?; | ||
| 1445 | let count = card.csd.block_count(); | ||
| 1446 | Ok(BlockCount(count)) | ||
| 1447 | } | ||
| 1448 | } | ||
| 1449 | } | ||
diff --git a/embassy-stm32/src/sdmmc/v1.rs b/embassy-stm32/src/sdmmc/v1.rs deleted file mode 100644 index 8b1378917..000000000 --- a/embassy-stm32/src/sdmmc/v1.rs +++ /dev/null | |||
| @@ -1 +0,0 @@ | |||
| 1 | |||
diff --git a/embassy-stm32/src/sdmmc/v2.rs b/embassy-stm32/src/sdmmc/v2.rs deleted file mode 100644 index cb8fa5544..000000000 --- a/embassy-stm32/src/sdmmc/v2.rs +++ /dev/null | |||
| @@ -1,1374 +0,0 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | use core::default::Default; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::task::Poll; | ||
| 6 | |||
| 7 | use embassy::interrupt::InterruptExt; | ||
| 8 | use embassy::util::Unborrow; | ||
| 9 | use embassy::waitqueue::AtomicWaker; | ||
| 10 | use embassy_hal_common::drop::OnDrop; | ||
| 11 | use embassy_hal_common::unborrow; | ||
| 12 | use futures::future::poll_fn; | ||
| 13 | use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; | ||
| 14 | |||
| 15 | use crate::gpio::sealed::AFType; | ||
| 16 | use crate::gpio::{Pull, Speed}; | ||
| 17 | use crate::interrupt::Interrupt; | ||
| 18 | use crate::pac::sdmmc::Sdmmc as RegBlock; | ||
| 19 | use crate::peripherals; | ||
| 20 | use crate::time::Hertz; | ||
| 21 | |||
| 22 | /// The signalling scheme used on the SDMMC bus | ||
| 23 | #[non_exhaustive] | ||
| 24 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
| 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | pub enum Signalling { | ||
| 27 | SDR12, | ||
| 28 | SDR25, | ||
| 29 | SDR50, | ||
| 30 | SDR104, | ||
| 31 | DDR50, | ||
| 32 | } | ||
| 33 | |||
| 34 | impl Default for Signalling { | ||
| 35 | fn default() -> Self { | ||
| 36 | Signalling::SDR12 | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | #[repr(align(4))] | ||
| 41 | pub struct DataBlock([u8; 512]); | ||
| 42 | |||
| 43 | /// Errors | ||
| 44 | #[non_exhaustive] | ||
| 45 | #[derive(Debug, Copy, Clone)] | ||
| 46 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 47 | pub enum Error { | ||
| 48 | Timeout, | ||
| 49 | SoftwareTimeout, | ||
| 50 | UnsupportedCardVersion, | ||
| 51 | UnsupportedCardType, | ||
| 52 | Crc, | ||
| 53 | DataCrcFail, | ||
| 54 | RxOverFlow, | ||
| 55 | NoCard, | ||
| 56 | BadClock, | ||
| 57 | SignalingSwitchFailed, | ||
| 58 | PeripheralBusy, | ||
| 59 | } | ||
| 60 | |||
| 61 | /// A SD command | ||
| 62 | struct Cmd { | ||
| 63 | cmd: u8, | ||
| 64 | arg: u32, | ||
| 65 | resp: Response, | ||
| 66 | } | ||
| 67 | |||
| 68 | #[derive(Clone, Copy, Debug, Default)] | ||
| 69 | /// SD Card | ||
| 70 | pub struct Card { | ||
| 71 | /// The type of this card | ||
| 72 | pub card_type: CardCapacity, | ||
| 73 | /// Operation Conditions Register | ||
| 74 | pub ocr: OCR, | ||
| 75 | /// Relative Card Address | ||
| 76 | pub rca: u32, | ||
| 77 | /// Card ID | ||
| 78 | pub cid: CID, | ||
| 79 | /// Card Specific Data | ||
| 80 | pub csd: CSD, | ||
| 81 | /// SD CARD Configuration Register | ||
| 82 | pub scr: SCR, | ||
| 83 | /// SD Status | ||
| 84 | pub status: SDStatus, | ||
| 85 | } | ||
| 86 | impl Card { | ||
| 87 | /// Size in bytes | ||
| 88 | pub fn size(&self) -> u64 { | ||
| 89 | // SDHC / SDXC / SDUC | ||
| 90 | u64::from(self.csd.block_count()) * 512 | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Indicates transfer direction | ||
| 95 | enum Dir { | ||
| 96 | CardToHost, | ||
| 97 | HostToCard, | ||
| 98 | } | ||
| 99 | |||
| 100 | #[repr(u8)] | ||
| 101 | enum PowerCtrl { | ||
| 102 | Off = 0b00, | ||
| 103 | On = 0b11, | ||
| 104 | } | ||
| 105 | |||
| 106 | #[repr(u32)] | ||
| 107 | #[allow(dead_code)] | ||
| 108 | #[allow(non_camel_case_types)] | ||
| 109 | enum CmdAppOper { | ||
| 110 | VOLTAGE_WINDOW_SD = 0x8010_0000, | ||
| 111 | HIGH_CAPACITY = 0x4000_0000, | ||
| 112 | SDMMC_STD_CAPACITY = 0x0000_0000, | ||
| 113 | SDMMC_CHECK_PATTERN = 0x0000_01AA, | ||
| 114 | SD_SWITCH_1_8V_CAPACITY = 0x0100_0000, | ||
| 115 | } | ||
| 116 | |||
| 117 | #[derive(Eq, PartialEq, Copy, Clone)] | ||
| 118 | enum Response { | ||
| 119 | None = 0, | ||
| 120 | Short = 1, | ||
| 121 | Long = 3, | ||
| 122 | } | ||
| 123 | |||
| 124 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | ||
| 125 | /// `sdmmc_ck` in Hertz. | ||
| 126 | /// | ||
| 127 | /// Returns `(clk_div, clk_f)`, where `clk_div` is the divisor register | ||
| 128 | /// value and `clk_f` is the resulting new clock frequency. | ||
| 129 | fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(u16, Hertz), Error> { | ||
| 130 | match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { | ||
| 131 | 0 | 1 => Ok((0, ker_ck)), | ||
| 132 | x @ 2..=2046 => { | ||
| 133 | let clk_div = ((x + 1) / 2) as u16; | ||
| 134 | let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); | ||
| 135 | |||
| 136 | Ok((clk_div, clk)) | ||
| 137 | } | ||
| 138 | _ => Err(Error::BadClock), | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | /// SDMMC configuration | ||
| 143 | /// | ||
| 144 | /// You should probably change the default clock values to match your configuration | ||
| 145 | /// | ||
| 146 | /// Default values: | ||
| 147 | /// hclk = 400_000_000 Hz | ||
| 148 | /// kernel_clk: 100_000_000 Hz | ||
| 149 | /// data_transfer_timeout: 5_000_000 | ||
| 150 | #[non_exhaustive] | ||
| 151 | pub struct Config { | ||
| 152 | /// AHB clock | ||
| 153 | pub hclk: Hertz, | ||
| 154 | /// SDMMC kernel clock | ||
| 155 | pub kernel_clk: Hertz, | ||
| 156 | /// The timeout to be set for data transfers, in card bus clock periods | ||
| 157 | pub data_transfer_timeout: u32, | ||
| 158 | } | ||
| 159 | |||
| 160 | impl Default for Config { | ||
| 161 | fn default() -> Self { | ||
| 162 | Self { | ||
| 163 | hclk: Hertz(400_000_000), | ||
| 164 | kernel_clk: Hertz(100_000_000), | ||
| 165 | data_transfer_timeout: 5_000_000, | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | /// Sdmmc device | ||
| 171 | pub struct Sdmmc<'d, T: Instance, P: Pins<T>> { | ||
| 172 | sdmmc: PhantomData<&'d mut T>, | ||
| 173 | pins: P, | ||
| 174 | irq: T::Interrupt, | ||
| 175 | config: Config, | ||
| 176 | /// Current clock to card | ||
| 177 | clock: Hertz, | ||
| 178 | /// Current signalling scheme to card | ||
| 179 | signalling: Signalling, | ||
| 180 | /// Card | ||
| 181 | card: Option<Card>, | ||
| 182 | } | ||
| 183 | |||
| 184 | impl<'d, T: Instance, P: Pins<T>> Sdmmc<'d, T, P> { | ||
| 185 | /// # Safety | ||
| 186 | /// | ||
| 187 | /// Futures that borrow this type can't be leaked | ||
| 188 | #[inline(always)] | ||
| 189 | pub unsafe fn new( | ||
| 190 | _peripheral: impl Unborrow<Target = T> + 'd, | ||
| 191 | pins: impl Unborrow<Target = P> + 'd, | ||
| 192 | irq: impl Unborrow<Target = T::Interrupt> + 'd, | ||
| 193 | config: Config, | ||
| 194 | ) -> Self { | ||
| 195 | unborrow!(irq, pins); | ||
| 196 | pins.configure(); | ||
| 197 | |||
| 198 | let inner = T::inner(); | ||
| 199 | let clock = inner.new_inner(config.kernel_clk); | ||
| 200 | |||
| 201 | irq.set_handler(Self::on_interrupt); | ||
| 202 | irq.unpend(); | ||
| 203 | irq.enable(); | ||
| 204 | |||
| 205 | Self { | ||
| 206 | sdmmc: PhantomData, | ||
| 207 | pins, | ||
| 208 | irq, | ||
| 209 | config, | ||
| 210 | clock, | ||
| 211 | signalling: Default::default(), | ||
| 212 | card: None, | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | #[inline(always)] | ||
| 217 | pub async fn init_card(&mut self, freq: impl Into<Hertz>) -> Result<(), Error> { | ||
| 218 | let inner = T::inner(); | ||
| 219 | let freq = freq.into(); | ||
| 220 | |||
| 221 | inner | ||
| 222 | .init_card( | ||
| 223 | freq, | ||
| 224 | P::BUSWIDTH, | ||
| 225 | &mut self.card, | ||
| 226 | &mut self.signalling, | ||
| 227 | self.config.hclk, | ||
| 228 | self.config.kernel_clk, | ||
| 229 | &mut self.clock, | ||
| 230 | T::state(), | ||
| 231 | self.config.data_transfer_timeout, | ||
| 232 | ) | ||
| 233 | .await | ||
| 234 | } | ||
| 235 | |||
| 236 | #[inline(always)] | ||
| 237 | pub async fn read_block( | ||
| 238 | &mut self, | ||
| 239 | block_idx: u32, | ||
| 240 | buffer: &mut DataBlock, | ||
| 241 | ) -> Result<(), Error> { | ||
| 242 | let card_capacity = self.card()?.card_type; | ||
| 243 | let inner = T::inner(); | ||
| 244 | let state = T::state(); | ||
| 245 | |||
| 246 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 247 | let buf = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 248 | inner | ||
| 249 | .read_block( | ||
| 250 | block_idx, | ||
| 251 | buf, | ||
| 252 | card_capacity, | ||
| 253 | state, | ||
| 254 | self.config.data_transfer_timeout, | ||
| 255 | ) | ||
| 256 | .await | ||
| 257 | } | ||
| 258 | |||
| 259 | pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { | ||
| 260 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 261 | let inner = T::inner(); | ||
| 262 | let state = T::state(); | ||
| 263 | |||
| 264 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 265 | let buf = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; | ||
| 266 | inner | ||
| 267 | .write_block( | ||
| 268 | block_idx, | ||
| 269 | buf, | ||
| 270 | card, | ||
| 271 | state, | ||
| 272 | self.config.data_transfer_timeout, | ||
| 273 | ) | ||
| 274 | .await | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Get a reference to the initialized card | ||
| 278 | /// | ||
| 279 | /// # Errors | ||
| 280 | /// | ||
| 281 | /// Returns Error::NoCard if [`init_card`](#method.init_card) | ||
| 282 | /// has not previously succeeded | ||
| 283 | #[inline(always)] | ||
| 284 | pub fn card(&self) -> Result<&Card, Error> { | ||
| 285 | self.card.as_ref().ok_or(Error::NoCard) | ||
| 286 | } | ||
| 287 | |||
| 288 | #[inline(always)] | ||
| 289 | fn on_interrupt(_: *mut ()) { | ||
| 290 | let regs = T::inner(); | ||
| 291 | let state = T::state(); | ||
| 292 | |||
| 293 | regs.data_interrupts(false); | ||
| 294 | state.wake(); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | impl<'d, T: Instance, P: Pins<T>> Drop for Sdmmc<'d, T, P> { | ||
| 299 | fn drop(&mut self) { | ||
| 300 | self.irq.disable(); | ||
| 301 | let inner = T::inner(); | ||
| 302 | unsafe { inner.on_drop() }; | ||
| 303 | self.pins.deconfigure(); | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | pub struct SdmmcInner(pub(crate) RegBlock); | ||
| 308 | |||
| 309 | impl SdmmcInner { | ||
| 310 | /// # Safety | ||
| 311 | /// | ||
| 312 | /// Access to `regs` registers should be exclusive | ||
| 313 | unsafe fn new_inner(&self, kernel_clk: Hertz) -> Hertz { | ||
| 314 | let regs = self.0; | ||
| 315 | |||
| 316 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 317 | // the SDMMC_CK frequency must be less than 400 kHz. | ||
| 318 | let (clkdiv, clock) = unwrap!(clk_div(kernel_clk, 400_000)); | ||
| 319 | |||
| 320 | regs.clkcr().write(|w| { | ||
| 321 | w.set_widbus(0); | ||
| 322 | w.set_clkdiv(clkdiv); | ||
| 323 | w.set_pwrsav(false); | ||
| 324 | w.set_negedge(false); | ||
| 325 | w.set_hwfc_en(true); | ||
| 326 | }); | ||
| 327 | |||
| 328 | // Power off, writen 00: Clock to the card is stopped; | ||
| 329 | // D[7:0], CMD, and CK are driven high. | ||
| 330 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); | ||
| 331 | |||
| 332 | clock | ||
| 333 | } | ||
| 334 | |||
| 335 | /// Initializes card (if present) and sets the bus at the | ||
| 336 | /// specified frequency. | ||
| 337 | #[allow(clippy::too_many_arguments)] | ||
| 338 | async fn init_card( | ||
| 339 | &self, | ||
| 340 | freq: Hertz, | ||
| 341 | bus_width: BusWidth, | ||
| 342 | old_card: &mut Option<Card>, | ||
| 343 | signalling: &mut Signalling, | ||
| 344 | hclk: Hertz, | ||
| 345 | ker_ck: Hertz, | ||
| 346 | clock: &mut Hertz, | ||
| 347 | waker_reg: &AtomicWaker, | ||
| 348 | data_transfer_timeout: u32, | ||
| 349 | ) -> Result<(), Error> { | ||
| 350 | let regs = self.0; | ||
| 351 | |||
| 352 | // NOTE(unsafe) We have exclusive access to the peripheral | ||
| 353 | unsafe { | ||
| 354 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 355 | self.cmd(Cmd::idle(), false)?; | ||
| 356 | |||
| 357 | // Check if cards supports CMD8 (with pattern) | ||
| 358 | self.cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; | ||
| 359 | let r1 = regs.respr(0).read().cardstatus1(); | ||
| 360 | |||
| 361 | let mut card = if r1 == 0x1AA { | ||
| 362 | // Card echoed back the pattern. Must be at least v2 | ||
| 363 | Card::default() | ||
| 364 | } else { | ||
| 365 | return Err(Error::UnsupportedCardVersion); | ||
| 366 | }; | ||
| 367 | |||
| 368 | let ocr = loop { | ||
| 369 | // Signal that next command is a app command | ||
| 370 | self.cmd(Cmd::app_cmd(0), false)?; // CMD55 | ||
| 371 | |||
| 372 | let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 | ||
| 373 | | CmdAppOper::HIGH_CAPACITY as u32 | ||
| 374 | | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; | ||
| 375 | |||
| 376 | // Initialize card | ||
| 377 | match self.cmd(Cmd::app_op_cmd(arg), false) { | ||
| 378 | // ACMD41 | ||
| 379 | Ok(_) => (), | ||
| 380 | Err(Error::Crc) => (), | ||
| 381 | Err(err) => return Err(err), | ||
| 382 | } | ||
| 383 | let ocr: OCR = regs.respr(0).read().cardstatus1().into(); | ||
| 384 | if !ocr.is_busy() { | ||
| 385 | // Power up done | ||
| 386 | break ocr; | ||
| 387 | } | ||
| 388 | }; | ||
| 389 | |||
| 390 | if ocr.high_capacity() { | ||
| 391 | // Card is SDHC or SDXC or SDUC | ||
| 392 | card.card_type = CardCapacity::SDHC; | ||
| 393 | } else { | ||
| 394 | card.card_type = CardCapacity::SDSC; | ||
| 395 | } | ||
| 396 | card.ocr = ocr; | ||
| 397 | |||
| 398 | self.cmd(Cmd::all_send_cid(), false)?; // CMD2 | ||
| 399 | let cid0 = regs.respr(0).read().cardstatus1() as u128; | ||
| 400 | let cid1 = regs.respr(1).read().cardstatus1() as u128; | ||
| 401 | let cid2 = regs.respr(2).read().cardstatus1() as u128; | ||
| 402 | let cid3 = regs.respr(3).read().cardstatus1() as u128; | ||
| 403 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | ||
| 404 | card.cid = cid.into(); | ||
| 405 | |||
| 406 | self.cmd(Cmd::send_rel_addr(), false)?; | ||
| 407 | card.rca = regs.respr(0).read().cardstatus1() >> 16; | ||
| 408 | |||
| 409 | self.cmd(Cmd::send_csd(card.rca << 16), false)?; | ||
| 410 | let csd0 = regs.respr(0).read().cardstatus1() as u128; | ||
| 411 | let csd1 = regs.respr(1).read().cardstatus1() as u128; | ||
| 412 | let csd2 = regs.respr(2).read().cardstatus1() as u128; | ||
| 413 | let csd3 = regs.respr(3).read().cardstatus1() as u128; | ||
| 414 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | ||
| 415 | card.csd = csd.into(); | ||
| 416 | |||
| 417 | self.select_card(Some(&card))?; | ||
| 418 | self.get_scr(&mut card, waker_reg, data_transfer_timeout) | ||
| 419 | .await?; | ||
| 420 | |||
| 421 | // Set bus width | ||
| 422 | let (width, acmd_arg) = match bus_width { | ||
| 423 | BusWidth::Eight => unimplemented!(), | ||
| 424 | BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), | ||
| 425 | _ => (BusWidth::One, 0), | ||
| 426 | }; | ||
| 427 | self.cmd(Cmd::app_cmd(card.rca << 16), false)?; | ||
| 428 | self.cmd(Cmd::cmd6(acmd_arg), false)?; | ||
| 429 | |||
| 430 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 431 | self.wait_idle(); | ||
| 432 | |||
| 433 | regs.clkcr().modify(|w| { | ||
| 434 | w.set_widbus(match width { | ||
| 435 | BusWidth::One => 0, | ||
| 436 | BusWidth::Four => 1, | ||
| 437 | BusWidth::Eight => 2, | ||
| 438 | _ => panic!("Invalid Bus Width"), | ||
| 439 | }) | ||
| 440 | }); | ||
| 441 | |||
| 442 | // Set Clock | ||
| 443 | if freq.0 <= 25_000_000 { | ||
| 444 | // Final clock frequency | ||
| 445 | self.clkcr_set_clkdiv(freq.0, width, hclk, ker_ck, clock)?; | ||
| 446 | } else { | ||
| 447 | // Switch to max clock for SDR12 | ||
| 448 | self.clkcr_set_clkdiv(25_000_000, width, hclk, ker_ck, clock)?; | ||
| 449 | } | ||
| 450 | |||
| 451 | // Read status | ||
| 452 | self.read_sd_status(&mut card, waker_reg, data_transfer_timeout) | ||
| 453 | .await?; | ||
| 454 | |||
| 455 | if freq.0 > 25_000_000 { | ||
| 456 | // Switch to SDR25 | ||
| 457 | *signalling = self | ||
| 458 | .switch_signalling_mode(Signalling::SDR25, waker_reg, data_transfer_timeout) | ||
| 459 | .await?; | ||
| 460 | |||
| 461 | if *signalling == Signalling::SDR25 { | ||
| 462 | // Set final clock frequency | ||
| 463 | self.clkcr_set_clkdiv(freq.0, width, hclk, ker_ck, clock)?; | ||
| 464 | |||
| 465 | if self.read_status(&card)?.state() != CurrentState::Transfer { | ||
| 466 | return Err(Error::SignalingSwitchFailed); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | } | ||
| 470 | // Read status after signalling change | ||
| 471 | self.read_sd_status(&mut card, waker_reg, data_transfer_timeout) | ||
| 472 | .await?; | ||
| 473 | old_card.replace(card); | ||
| 474 | } | ||
| 475 | |||
| 476 | Ok(()) | ||
| 477 | } | ||
| 478 | |||
| 479 | async fn read_block( | ||
| 480 | &self, | ||
| 481 | block_idx: u32, | ||
| 482 | buffer: &mut [u32; 128], | ||
| 483 | capacity: CardCapacity, | ||
| 484 | waker_reg: &AtomicWaker, | ||
| 485 | data_transfer_timeout: u32, | ||
| 486 | ) -> Result<(), Error> { | ||
| 487 | // Always read 1 block of 512 bytes | ||
| 488 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 489 | let address = match capacity { | ||
| 490 | CardCapacity::SDSC => block_idx * 512, | ||
| 491 | _ => block_idx, | ||
| 492 | }; | ||
| 493 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 494 | |||
| 495 | let regs = self.0; | ||
| 496 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 497 | |||
| 498 | let buf_addr = buffer as *mut [u32; 128] as u32; | ||
| 499 | unsafe { | ||
| 500 | self.prepare_datapath_transfer( | ||
| 501 | buf_addr, | ||
| 502 | 512, | ||
| 503 | 9, | ||
| 504 | Dir::CardToHost, | ||
| 505 | data_transfer_timeout, | ||
| 506 | ); | ||
| 507 | self.data_interrupts(true); | ||
| 508 | } | ||
| 509 | self.cmd(Cmd::read_single_block(address), true)?; | ||
| 510 | |||
| 511 | let res = poll_fn(|cx| { | ||
| 512 | waker_reg.register(cx.waker()); | ||
| 513 | let status = unsafe { regs.star().read() }; | ||
| 514 | |||
| 515 | if status.dcrcfail() { | ||
| 516 | return Poll::Ready(Err(Error::Crc)); | ||
| 517 | } else if status.dtimeout() { | ||
| 518 | return Poll::Ready(Err(Error::Timeout)); | ||
| 519 | } else if status.dataend() { | ||
| 520 | return Poll::Ready(Ok(())); | ||
| 521 | } | ||
| 522 | Poll::Pending | ||
| 523 | }) | ||
| 524 | .await; | ||
| 525 | self.clear_interrupt_flags(); | ||
| 526 | |||
| 527 | if res.is_ok() { | ||
| 528 | on_drop.defuse(); | ||
| 529 | unsafe { | ||
| 530 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 531 | } | ||
| 532 | } | ||
| 533 | res | ||
| 534 | } | ||
| 535 | |||
| 536 | async fn write_block( | ||
| 537 | &self, | ||
| 538 | block_idx: u32, | ||
| 539 | buffer: &[u32; 128], | ||
| 540 | card: &mut Card, | ||
| 541 | waker_reg: &AtomicWaker, | ||
| 542 | data_transfer_timeout: u32, | ||
| 543 | ) -> Result<(), Error> { | ||
| 544 | // Always read 1 block of 512 bytes | ||
| 545 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 546 | let address = match card.card_type { | ||
| 547 | CardCapacity::SDSC => block_idx * 512, | ||
| 548 | _ => block_idx, | ||
| 549 | }; | ||
| 550 | self.cmd(Cmd::set_block_length(512), false)?; // CMD16 | ||
| 551 | |||
| 552 | let regs = self.0; | ||
| 553 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 554 | |||
| 555 | let buf_addr = buffer as *const [u32; 128] as u32; | ||
| 556 | unsafe { | ||
| 557 | self.prepare_datapath_transfer( | ||
| 558 | buf_addr, | ||
| 559 | 512, | ||
| 560 | 9, | ||
| 561 | Dir::HostToCard, | ||
| 562 | data_transfer_timeout, | ||
| 563 | ); | ||
| 564 | self.data_interrupts(true); | ||
| 565 | } | ||
| 566 | self.cmd(Cmd::write_single_block(address), true)?; | ||
| 567 | |||
| 568 | let res = poll_fn(|cx| { | ||
| 569 | waker_reg.register(cx.waker()); | ||
| 570 | let status = unsafe { regs.star().read() }; | ||
| 571 | |||
| 572 | if status.dcrcfail() { | ||
| 573 | return Poll::Ready(Err(Error::Crc)); | ||
| 574 | } else if status.dtimeout() { | ||
| 575 | return Poll::Ready(Err(Error::Timeout)); | ||
| 576 | } else if status.dataend() { | ||
| 577 | return Poll::Ready(Ok(())); | ||
| 578 | } | ||
| 579 | Poll::Pending | ||
| 580 | }) | ||
| 581 | .await; | ||
| 582 | self.clear_interrupt_flags(); | ||
| 583 | |||
| 584 | match res { | ||
| 585 | Ok(_) => { | ||
| 586 | on_drop.defuse(); | ||
| 587 | unsafe { | ||
| 588 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 589 | } | ||
| 590 | // TODO: Make this configurable | ||
| 591 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 592 | |||
| 593 | // Try to read card status (ACMD13) | ||
| 594 | while timeout > 0 { | ||
| 595 | match self | ||
| 596 | .read_sd_status(card, waker_reg, data_transfer_timeout) | ||
| 597 | .await | ||
| 598 | { | ||
| 599 | Ok(_) => return Ok(()), | ||
| 600 | Err(Error::Timeout) => (), // Try again | ||
| 601 | Err(e) => return Err(e), | ||
| 602 | } | ||
| 603 | timeout -= 1; | ||
| 604 | } | ||
| 605 | Err(Error::SoftwareTimeout) | ||
| 606 | } | ||
| 607 | Err(e) => Err(e), | ||
| 608 | } | ||
| 609 | } | ||
| 610 | |||
| 611 | /// Get the current SDMMC bus clock | ||
| 612 | //pub fn clock(&self) -> Hertz { | ||
| 613 | // self.clock | ||
| 614 | //} | ||
| 615 | |||
| 616 | /// Wait idle on DOSNACT and CPSMACT | ||
| 617 | #[inline(always)] | ||
| 618 | fn wait_idle(&self) { | ||
| 619 | let regs = self.0; | ||
| 620 | |||
| 621 | // NOTE(unsafe) Atomic read with no side-effects | ||
| 622 | unsafe { | ||
| 623 | while { | ||
| 624 | let status = regs.star().read(); | ||
| 625 | status.dpsmact() || status.cpsmact() | ||
| 626 | } {} | ||
| 627 | } | ||
| 628 | } | ||
| 629 | |||
| 630 | /// # Safety | ||
| 631 | /// | ||
| 632 | /// `buffer_addr` must be valid for the whole transfer and word aligned | ||
| 633 | unsafe fn prepare_datapath_transfer( | ||
| 634 | &self, | ||
| 635 | buffer_addr: u32, | ||
| 636 | length_bytes: u32, | ||
| 637 | block_size: u8, | ||
| 638 | direction: Dir, | ||
| 639 | data_transfer_timeout: u32, | ||
| 640 | ) { | ||
| 641 | assert!(block_size <= 14, "Block size up to 2^14 bytes"); | ||
| 642 | let regs = self.0; | ||
| 643 | |||
| 644 | let dtdir = match direction { | ||
| 645 | Dir::CardToHost => true, | ||
| 646 | Dir::HostToCard => false, | ||
| 647 | }; | ||
| 648 | |||
| 649 | // Command AND Data state machines must be idle | ||
| 650 | self.wait_idle(); | ||
| 651 | self.clear_interrupt_flags(); | ||
| 652 | |||
| 653 | // NOTE(unsafe) We have exclusive access to the regisers | ||
| 654 | |||
| 655 | regs.dtimer() | ||
| 656 | .write(|w| w.set_datatime(data_transfer_timeout)); | ||
| 657 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | ||
| 658 | |||
| 659 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer_addr)); | ||
| 660 | regs.idmactrlr().modify(|w| w.set_idmaen(true)); | ||
| 661 | regs.dctrl().modify(|w| { | ||
| 662 | w.set_dblocksize(block_size); | ||
| 663 | w.set_dtdir(dtdir); | ||
| 664 | }); | ||
| 665 | } | ||
| 666 | |||
| 667 | /// Sets the CLKDIV field in CLKCR. Updates clock field in self | ||
| 668 | fn clkcr_set_clkdiv( | ||
| 669 | &self, | ||
| 670 | freq: u32, | ||
| 671 | width: BusWidth, | ||
| 672 | hclk: Hertz, | ||
| 673 | ker_ck: Hertz, | ||
| 674 | clock: &mut Hertz, | ||
| 675 | ) -> Result<(), Error> { | ||
| 676 | let regs = self.0; | ||
| 677 | |||
| 678 | let (clkdiv, new_clock) = clk_div(ker_ck, freq)?; | ||
| 679 | // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 | ||
| 680 | // Section 55.5.8 | ||
| 681 | let sdmmc_bus_bandwidth = new_clock.0 * (width as u32); | ||
| 682 | assert!(hclk.0 > 3 * sdmmc_bus_bandwidth / 32); | ||
| 683 | *clock = new_clock; | ||
| 684 | |||
| 685 | // NOTE(unsafe) We have exclusive access to the regblock | ||
| 686 | unsafe { | ||
| 687 | // CPSMACT and DPSMACT must be 0 to set CLKDIV | ||
| 688 | self.wait_idle(); | ||
| 689 | regs.clkcr().modify(|w| w.set_clkdiv(clkdiv)); | ||
| 690 | } | ||
| 691 | |||
| 692 | Ok(()) | ||
| 693 | } | ||
| 694 | |||
| 695 | /// Switch mode using CMD6. | ||
| 696 | /// | ||
| 697 | /// Attempt to set a new signalling mode. The selected | ||
| 698 | /// signalling mode is returned. Expects the current clock | ||
| 699 | /// frequency to be > 12.5MHz. | ||
| 700 | async fn switch_signalling_mode( | ||
| 701 | &self, | ||
| 702 | signalling: Signalling, | ||
| 703 | waker_reg: &AtomicWaker, | ||
| 704 | data_transfer_timeout: u32, | ||
| 705 | ) -> Result<Signalling, Error> { | ||
| 706 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 707 | // necessary" | ||
| 708 | |||
| 709 | let set_function = 0x8000_0000 | ||
| 710 | | match signalling { | ||
| 711 | // See PLSS v7_10 Table 4-11 | ||
| 712 | Signalling::DDR50 => 0xFF_FF04, | ||
| 713 | Signalling::SDR104 => 0xFF_1F03, | ||
| 714 | Signalling::SDR50 => 0xFF_1F02, | ||
| 715 | Signalling::SDR25 => 0xFF_FF01, | ||
| 716 | Signalling::SDR12 => 0xFF_FF00, | ||
| 717 | }; | ||
| 718 | |||
| 719 | let mut status = [0u32; 16]; | ||
| 720 | let status_addr = &mut status as *mut [u32; 16] as u32; | ||
| 721 | |||
| 722 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 723 | let regs = self.0; | ||
| 724 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 725 | |||
| 726 | unsafe { | ||
| 727 | self.prepare_datapath_transfer( | ||
| 728 | status_addr, | ||
| 729 | 64, | ||
| 730 | 6, | ||
| 731 | Dir::CardToHost, | ||
| 732 | data_transfer_timeout, | ||
| 733 | ); | ||
| 734 | self.data_interrupts(true); | ||
| 735 | } | ||
| 736 | self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 | ||
| 737 | |||
| 738 | let res = poll_fn(|cx| { | ||
| 739 | waker_reg.register(cx.waker()); | ||
| 740 | let status = unsafe { regs.star().read() }; | ||
| 741 | |||
| 742 | if status.dcrcfail() { | ||
| 743 | return Poll::Ready(Err(Error::Crc)); | ||
| 744 | } else if status.dtimeout() { | ||
| 745 | return Poll::Ready(Err(Error::Timeout)); | ||
| 746 | } else if status.dataend() { | ||
| 747 | return Poll::Ready(Ok(())); | ||
| 748 | } | ||
| 749 | Poll::Pending | ||
| 750 | }) | ||
| 751 | .await; | ||
| 752 | self.clear_interrupt_flags(); | ||
| 753 | |||
| 754 | // Host is allowed to use the new functions at least 8 | ||
| 755 | // clocks after the end of the switch command | ||
| 756 | // transaction. We know the current clock period is < 80ns, | ||
| 757 | // so a total delay of 640ns is required here | ||
| 758 | for _ in 0..300 { | ||
| 759 | cortex_m::asm::nop(); | ||
| 760 | } | ||
| 761 | |||
| 762 | match res { | ||
| 763 | Ok(_) => { | ||
| 764 | on_drop.defuse(); | ||
| 765 | unsafe { | ||
| 766 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 767 | } | ||
| 768 | // Function Selection of Function Group 1 | ||
| 769 | let selection = (u32::from_be(status[4]) >> 24) & 0xF; | ||
| 770 | |||
| 771 | match selection { | ||
| 772 | 0 => Ok(Signalling::SDR12), | ||
| 773 | 1 => Ok(Signalling::SDR25), | ||
| 774 | 2 => Ok(Signalling::SDR50), | ||
| 775 | 3 => Ok(Signalling::SDR104), | ||
| 776 | 4 => Ok(Signalling::DDR50), | ||
| 777 | _ => Err(Error::UnsupportedCardType), | ||
| 778 | } | ||
| 779 | } | ||
| 780 | Err(e) => Err(e), | ||
| 781 | } | ||
| 782 | } | ||
| 783 | |||
| 784 | /// Query the card status (CMD13, returns R1) | ||
| 785 | /// | ||
| 786 | fn read_status(&self, card: &Card) -> Result<CardStatus, Error> { | ||
| 787 | let regs = self.0; | ||
| 788 | let rca = card.rca; | ||
| 789 | |||
| 790 | self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 | ||
| 791 | |||
| 792 | // NOTE(unsafe) Atomic read with no side-effects | ||
| 793 | let r1 = unsafe { regs.respr(0).read().cardstatus1() }; | ||
| 794 | Ok(r1.into()) | ||
| 795 | } | ||
| 796 | |||
| 797 | /// Reads the SD Status (ACMD13) | ||
| 798 | async fn read_sd_status( | ||
| 799 | &self, | ||
| 800 | card: &mut Card, | ||
| 801 | waker_reg: &AtomicWaker, | ||
| 802 | data_transfer_timeout: u32, | ||
| 803 | ) -> Result<(), Error> { | ||
| 804 | let rca = card.rca; | ||
| 805 | self.cmd(Cmd::set_block_length(64), false)?; // CMD16 | ||
| 806 | self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP | ||
| 807 | |||
| 808 | let mut status = [0u32; 16]; | ||
| 809 | let status_addr = &mut status as *mut [u32; 16] as u32; | ||
| 810 | |||
| 811 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 812 | let regs = self.0; | ||
| 813 | let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); | ||
| 814 | |||
| 815 | unsafe { | ||
| 816 | self.prepare_datapath_transfer( | ||
| 817 | status_addr, | ||
| 818 | 64, | ||
| 819 | 6, | ||
| 820 | Dir::CardToHost, | ||
| 821 | data_transfer_timeout, | ||
| 822 | ); | ||
| 823 | self.data_interrupts(true); | ||
| 824 | } | ||
| 825 | self.cmd(Cmd::card_status(0), true)?; | ||
| 826 | |||
| 827 | let res = poll_fn(|cx| { | ||
| 828 | waker_reg.register(cx.waker()); | ||
| 829 | let status = unsafe { regs.star().read() }; | ||
| 830 | |||
| 831 | if status.dcrcfail() { | ||
| 832 | return Poll::Ready(Err(Error::Crc)); | ||
| 833 | } else if status.dtimeout() { | ||
| 834 | return Poll::Ready(Err(Error::Timeout)); | ||
| 835 | } else if status.dataend() { | ||
| 836 | return Poll::Ready(Ok(())); | ||
| 837 | } | ||
| 838 | Poll::Pending | ||
| 839 | }) | ||
| 840 | .await; | ||
| 841 | self.clear_interrupt_flags(); | ||
| 842 | |||
| 843 | if res.is_ok() { | ||
| 844 | on_drop.defuse(); | ||
| 845 | unsafe { | ||
| 846 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 847 | } | ||
| 848 | for byte in status.iter_mut() { | ||
| 849 | *byte = u32::from_be(*byte); | ||
| 850 | } | ||
| 851 | card.status = status.into(); | ||
| 852 | } | ||
| 853 | res | ||
| 854 | } | ||
| 855 | |||
| 856 | /// Select one card and place it into the _Tranfer State_ | ||
| 857 | /// | ||
| 858 | /// If `None` is specifed for `card`, all cards are put back into | ||
| 859 | /// _Stand-by State_ | ||
| 860 | fn select_card(&self, card: Option<&Card>) -> Result<(), Error> { | ||
| 861 | // Determine Relative Card Address (RCA) of given card | ||
| 862 | let rca = card.map(|c| c.rca << 16).unwrap_or(0); | ||
| 863 | |||
| 864 | let r = self.cmd(Cmd::sel_desel_card(rca), false); | ||
| 865 | match (r, rca) { | ||
| 866 | (Err(Error::Timeout), 0) => Ok(()), | ||
| 867 | _ => r, | ||
| 868 | } | ||
| 869 | } | ||
| 870 | |||
| 871 | /// Clear flags in interrupt clear register | ||
| 872 | #[inline(always)] | ||
| 873 | fn clear_interrupt_flags(&self) { | ||
| 874 | let regs = self.0; | ||
| 875 | // NOTE(unsafe) Atomic write | ||
| 876 | unsafe { | ||
| 877 | regs.icr().write(|w| { | ||
| 878 | w.set_ccrcfailc(true); | ||
| 879 | w.set_dcrcfailc(true); | ||
| 880 | w.set_ctimeoutc(true); | ||
| 881 | w.set_dtimeoutc(true); | ||
| 882 | w.set_txunderrc(true); | ||
| 883 | w.set_rxoverrc(true); | ||
| 884 | w.set_cmdrendc(true); | ||
| 885 | w.set_cmdsentc(true); | ||
| 886 | w.set_dataendc(true); | ||
| 887 | w.set_dholdc(true); | ||
| 888 | w.set_dbckendc(true); | ||
| 889 | w.set_dabortc(true); | ||
| 890 | w.set_busyd0endc(true); | ||
| 891 | w.set_sdioitc(true); | ||
| 892 | w.set_ackfailc(true); | ||
| 893 | w.set_acktimeoutc(true); | ||
| 894 | w.set_vswendc(true); | ||
| 895 | w.set_ckstopc(true); | ||
| 896 | w.set_idmatec(true); | ||
| 897 | w.set_idmabtcc(true); | ||
| 898 | }); | ||
| 899 | } | ||
| 900 | } | ||
| 901 | |||
| 902 | /// Enables the interrupts for data transfer | ||
| 903 | #[inline(always)] | ||
| 904 | fn data_interrupts(&self, enable: bool) { | ||
| 905 | let regs = self.0; | ||
| 906 | // NOTE(unsafe) Atomic write | ||
| 907 | unsafe { | ||
| 908 | regs.maskr().write(|w| { | ||
| 909 | w.set_dcrcfailie(enable); | ||
| 910 | w.set_dtimeoutie(enable); | ||
| 911 | w.set_dataendie(enable); | ||
| 912 | w.set_dabortie(enable); | ||
| 913 | }); | ||
| 914 | } | ||
| 915 | } | ||
| 916 | |||
| 917 | async fn get_scr( | ||
| 918 | &self, | ||
| 919 | card: &mut Card, | ||
| 920 | waker_reg: &AtomicWaker, | ||
| 921 | data_transfer_timeout: u32, | ||
| 922 | ) -> Result<(), Error> { | ||
| 923 | // Read the the 64-bit SCR register | ||
| 924 | self.cmd(Cmd::set_block_length(8), false)?; // CMD16 | ||
| 925 | self.cmd(Cmd::app_cmd(card.rca << 16), false)?; | ||
| 926 | |||
| 927 | let mut scr = [0u32; 2]; | ||
| 928 | let scr_addr = &mut scr as *mut u32 as u32; | ||
| 929 | |||
| 930 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 931 | let regs = self.0; | ||
| 932 | let on_drop = OnDrop::new(move || unsafe { self.on_drop() }); | ||
| 933 | |||
| 934 | unsafe { | ||
| 935 | self.prepare_datapath_transfer(scr_addr, 8, 3, Dir::CardToHost, data_transfer_timeout); | ||
| 936 | self.data_interrupts(true); | ||
| 937 | } | ||
| 938 | self.cmd(Cmd::cmd51(), true)?; | ||
| 939 | |||
| 940 | let res = poll_fn(|cx| { | ||
| 941 | waker_reg.register(cx.waker()); | ||
| 942 | let status = unsafe { regs.star().read() }; | ||
| 943 | |||
| 944 | if status.dcrcfail() { | ||
| 945 | return Poll::Ready(Err(Error::Crc)); | ||
| 946 | } else if status.dtimeout() { | ||
| 947 | return Poll::Ready(Err(Error::Timeout)); | ||
| 948 | } else if status.dataend() { | ||
| 949 | return Poll::Ready(Ok(())); | ||
| 950 | } | ||
| 951 | Poll::Pending | ||
| 952 | }) | ||
| 953 | .await; | ||
| 954 | self.clear_interrupt_flags(); | ||
| 955 | |||
| 956 | if res.is_ok() { | ||
| 957 | on_drop.defuse(); | ||
| 958 | |||
| 959 | unsafe { | ||
| 960 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 961 | let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); | ||
| 962 | card.scr = SCR(u64::from_be_bytes(*scr_bytes)); | ||
| 963 | } | ||
| 964 | } | ||
| 965 | res | ||
| 966 | } | ||
| 967 | |||
| 968 | /// Send command to card | ||
| 969 | fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { | ||
| 970 | let regs = self.0; | ||
| 971 | |||
| 972 | self.clear_interrupt_flags(); | ||
| 973 | // NOTE(safety) Atomic operations | ||
| 974 | unsafe { | ||
| 975 | // CP state machine must be idle | ||
| 976 | while regs.star().read().cpsmact() {} | ||
| 977 | |||
| 978 | // Command arg | ||
| 979 | regs.argr().write(|w| w.set_cmdarg(cmd.arg)); | ||
| 980 | |||
| 981 | // Special mode in CP State Machine | ||
| 982 | // CMD12: Stop Transmission | ||
| 983 | let cpsm_stop_transmission = cmd.cmd == 12; | ||
| 984 | |||
| 985 | // Command index and start CP State Machine | ||
| 986 | regs.cmdr().write(|w| { | ||
| 987 | w.set_waitint(false); | ||
| 988 | w.set_waitresp(cmd.resp as u8); | ||
| 989 | w.set_cmdstop(cpsm_stop_transmission); | ||
| 990 | w.set_cmdindex(cmd.cmd); | ||
| 991 | w.set_cpsmen(true); | ||
| 992 | w.set_cmdtrans(data); | ||
| 993 | }); | ||
| 994 | |||
| 995 | let mut status; | ||
| 996 | if cmd.resp == Response::None { | ||
| 997 | // Wait for CMDSENT or a timeout | ||
| 998 | while { | ||
| 999 | status = regs.star().read(); | ||
| 1000 | !(status.ctimeout() || status.cmdsent()) | ||
| 1001 | } {} | ||
| 1002 | } else { | ||
| 1003 | // Wait for CMDREND or CCRCFAIL or a timeout | ||
| 1004 | while { | ||
| 1005 | status = regs.star().read(); | ||
| 1006 | !(status.ctimeout() || status.cmdrend() || status.ccrcfail()) | ||
| 1007 | } {} | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | if status.ctimeout() { | ||
| 1011 | return Err(Error::Timeout); | ||
| 1012 | } else if status.ccrcfail() { | ||
| 1013 | return Err(Error::Crc); | ||
| 1014 | } | ||
| 1015 | Ok(()) | ||
| 1016 | } | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | /// # Safety | ||
| 1020 | /// | ||
| 1021 | /// Ensure that `regs` has exclusive access to the regblocks | ||
| 1022 | unsafe fn on_drop(&self) { | ||
| 1023 | let regs = self.0; | ||
| 1024 | if regs.star().read().dpsmact() { | ||
| 1025 | self.clear_interrupt_flags(); | ||
| 1026 | // Send abort | ||
| 1027 | // CP state machine must be idle | ||
| 1028 | while regs.star().read().cpsmact() {} | ||
| 1029 | |||
| 1030 | // Command arg | ||
| 1031 | regs.argr().write(|w| w.set_cmdarg(0)); | ||
| 1032 | |||
| 1033 | // Command index and start CP State Machine | ||
| 1034 | regs.cmdr().write(|w| { | ||
| 1035 | w.set_waitint(false); | ||
| 1036 | w.set_waitresp(Response::Short as u8); | ||
| 1037 | w.set_cmdstop(true); | ||
| 1038 | w.set_cmdindex(12); | ||
| 1039 | w.set_cpsmen(true); | ||
| 1040 | w.set_cmdtrans(false); | ||
| 1041 | }); | ||
| 1042 | |||
| 1043 | // Wait for the abort | ||
| 1044 | while regs.star().read().dpsmact() {} | ||
| 1045 | } | ||
| 1046 | self.data_interrupts(false); | ||
| 1047 | self.clear_interrupt_flags(); | ||
| 1048 | regs.idmactrlr().modify(|w| w.set_idmaen(false)); | ||
| 1049 | } | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | /// SD card Commands | ||
| 1053 | impl Cmd { | ||
| 1054 | const fn new(cmd: u8, arg: u32, resp: Response) -> Cmd { | ||
| 1055 | Cmd { cmd, arg, resp } | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | /// CMD0: Idle | ||
| 1059 | const fn idle() -> Cmd { | ||
| 1060 | Cmd::new(0, 0, Response::None) | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | /// CMD2: Send CID | ||
| 1064 | const fn all_send_cid() -> Cmd { | ||
| 1065 | Cmd::new(2, 0, Response::Long) | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | /// CMD3: Send Relative Address | ||
| 1069 | const fn send_rel_addr() -> Cmd { | ||
| 1070 | Cmd::new(3, 0, Response::Short) | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | /// CMD6: Switch Function Command | ||
| 1074 | /// ACMD6: Bus Width | ||
| 1075 | const fn cmd6(arg: u32) -> Cmd { | ||
| 1076 | Cmd::new(6, arg, Response::Short) | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | /// CMD7: Select one card and put it into the _Tranfer State_ | ||
| 1080 | const fn sel_desel_card(rca: u32) -> Cmd { | ||
| 1081 | Cmd::new(7, rca, Response::Short) | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | /// CMD8: | ||
| 1085 | const fn hs_send_ext_csd(arg: u32) -> Cmd { | ||
| 1086 | Cmd::new(8, arg, Response::Short) | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | /// CMD9: | ||
| 1090 | const fn send_csd(rca: u32) -> Cmd { | ||
| 1091 | Cmd::new(9, rca, Response::Long) | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | /// CMD12: | ||
| 1095 | //const fn stop_transmission() -> Cmd { | ||
| 1096 | // Cmd::new(12, 0, Response::Short) | ||
| 1097 | //} | ||
| 1098 | |||
| 1099 | /// CMD13: Ask card to send status register | ||
| 1100 | /// ACMD13: SD Status | ||
| 1101 | const fn card_status(rca: u32) -> Cmd { | ||
| 1102 | Cmd::new(13, rca, Response::Short) | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | /// CMD16: | ||
| 1106 | const fn set_block_length(blocklen: u32) -> Cmd { | ||
| 1107 | Cmd::new(16, blocklen, Response::Short) | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | /// CMD17: Block Read | ||
| 1111 | const fn read_single_block(addr: u32) -> Cmd { | ||
| 1112 | Cmd::new(17, addr, Response::Short) | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | /// CMD18: Multiple Block Read | ||
| 1116 | //const fn read_multiple_blocks(addr: u32) -> Cmd { | ||
| 1117 | // Cmd::new(18, addr, Response::Short) | ||
| 1118 | //} | ||
| 1119 | |||
| 1120 | /// CMD24: Block Write | ||
| 1121 | const fn write_single_block(addr: u32) -> Cmd { | ||
| 1122 | Cmd::new(24, addr, Response::Short) | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | const fn app_op_cmd(arg: u32) -> Cmd { | ||
| 1126 | Cmd::new(41, arg, Response::Short) | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | const fn cmd51() -> Cmd { | ||
| 1130 | Cmd::new(51, 0, Response::Short) | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | /// App Command. Indicates that next command will be a app command | ||
| 1134 | const fn app_cmd(rca: u32) -> Cmd { | ||
| 1135 | Cmd::new(55, rca, Response::Short) | ||
| 1136 | } | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | ////////////////////////////////////////////////////// | ||
| 1140 | |||
| 1141 | pub(crate) mod sealed { | ||
| 1142 | use super::*; | ||
| 1143 | |||
| 1144 | pub trait Instance { | ||
| 1145 | type Interrupt: Interrupt; | ||
| 1146 | |||
| 1147 | fn inner() -> SdmmcInner; | ||
| 1148 | fn state() -> &'static AtomicWaker; | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | pub trait Pins<T: Instance> {} | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | pub trait Instance: sealed::Instance + 'static {} | ||
| 1155 | pin_trait!(CkPin, Instance); | ||
| 1156 | pin_trait!(CmdPin, Instance); | ||
| 1157 | pin_trait!(D0Pin, Instance); | ||
| 1158 | pin_trait!(D1Pin, Instance); | ||
| 1159 | pin_trait!(D2Pin, Instance); | ||
| 1160 | pin_trait!(D3Pin, Instance); | ||
| 1161 | pin_trait!(D4Pin, Instance); | ||
| 1162 | pin_trait!(D5Pin, Instance); | ||
| 1163 | pin_trait!(D6Pin, Instance); | ||
| 1164 | pin_trait!(D7Pin, Instance); | ||
| 1165 | |||
| 1166 | pub trait Pins<T: Instance>: sealed::Pins<T> + 'static { | ||
| 1167 | const BUSWIDTH: BusWidth; | ||
| 1168 | |||
| 1169 | fn configure(&mut self); | ||
| 1170 | fn deconfigure(&mut self); | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | impl<T, CLK, CMD, D0, D1, D2, D3> sealed::Pins<T> for (CLK, CMD, D0, D1, D2, D3) | ||
| 1174 | where | ||
| 1175 | T: Instance, | ||
| 1176 | CLK: CkPin<T>, | ||
| 1177 | CMD: CmdPin<T>, | ||
| 1178 | D0: D0Pin<T>, | ||
| 1179 | D1: D1Pin<T>, | ||
| 1180 | D2: D2Pin<T>, | ||
| 1181 | D3: D3Pin<T>, | ||
| 1182 | { | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | impl<T, CLK, CMD, D0> sealed::Pins<T> for (CLK, CMD, D0) | ||
| 1186 | where | ||
| 1187 | T: Instance, | ||
| 1188 | CLK: CkPin<T>, | ||
| 1189 | CMD: CmdPin<T>, | ||
| 1190 | D0: D0Pin<T>, | ||
| 1191 | { | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | impl<T, CLK, CMD, D0, D1, D2, D3> Pins<T> for (CLK, CMD, D0, D1, D2, D3) | ||
| 1195 | where | ||
| 1196 | T: Instance, | ||
| 1197 | CLK: CkPin<T>, | ||
| 1198 | CMD: CmdPin<T>, | ||
| 1199 | D0: D0Pin<T>, | ||
| 1200 | D1: D1Pin<T>, | ||
| 1201 | D2: D2Pin<T>, | ||
| 1202 | D3: D3Pin<T>, | ||
| 1203 | { | ||
| 1204 | const BUSWIDTH: BusWidth = BusWidth::Four; | ||
| 1205 | |||
| 1206 | fn configure(&mut self) { | ||
| 1207 | let (clk_pin, cmd_pin, d0_pin, d1_pin, d2_pin, d3_pin) = self; | ||
| 1208 | |||
| 1209 | critical_section::with(|_| unsafe { | ||
| 1210 | clk_pin.set_as_af_pull(clk_pin.af_num(), AFType::OutputPushPull, Pull::None); | ||
| 1211 | cmd_pin.set_as_af_pull(cmd_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1212 | d0_pin.set_as_af_pull(d0_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1213 | d1_pin.set_as_af_pull(d1_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1214 | d2_pin.set_as_af_pull(d2_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1215 | d3_pin.set_as_af_pull(d3_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1216 | |||
| 1217 | clk_pin.set_speed(Speed::VeryHigh); | ||
| 1218 | cmd_pin.set_speed(Speed::VeryHigh); | ||
| 1219 | d0_pin.set_speed(Speed::VeryHigh); | ||
| 1220 | d1_pin.set_speed(Speed::VeryHigh); | ||
| 1221 | d2_pin.set_speed(Speed::VeryHigh); | ||
| 1222 | d3_pin.set_speed(Speed::VeryHigh); | ||
| 1223 | }); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | fn deconfigure(&mut self) { | ||
| 1227 | let (clk_pin, cmd_pin, d0_pin, d1_pin, d2_pin, d3_pin) = self; | ||
| 1228 | |||
| 1229 | critical_section::with(|_| unsafe { | ||
| 1230 | clk_pin.set_as_disconnected(); | ||
| 1231 | cmd_pin.set_as_disconnected(); | ||
| 1232 | d0_pin.set_as_disconnected(); | ||
| 1233 | d1_pin.set_as_disconnected(); | ||
| 1234 | d2_pin.set_as_disconnected(); | ||
| 1235 | d3_pin.set_as_disconnected(); | ||
| 1236 | }); | ||
| 1237 | } | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | impl<T, CLK, CMD, D0> Pins<T> for (CLK, CMD, D0) | ||
| 1241 | where | ||
| 1242 | T: Instance, | ||
| 1243 | CLK: CkPin<T>, | ||
| 1244 | CMD: CmdPin<T>, | ||
| 1245 | D0: D0Pin<T>, | ||
| 1246 | { | ||
| 1247 | const BUSWIDTH: BusWidth = BusWidth::One; | ||
| 1248 | |||
| 1249 | fn configure(&mut self) { | ||
| 1250 | let (clk_pin, cmd_pin, d0_pin) = self; | ||
| 1251 | |||
| 1252 | critical_section::with(|_| unsafe { | ||
| 1253 | clk_pin.set_as_af_pull(clk_pin.af_num(), AFType::OutputPushPull, Pull::None); | ||
| 1254 | cmd_pin.set_as_af_pull(cmd_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1255 | d0_pin.set_as_af_pull(d0_pin.af_num(), AFType::OutputPushPull, Pull::Up); | ||
| 1256 | |||
| 1257 | clk_pin.set_speed(Speed::VeryHigh); | ||
| 1258 | cmd_pin.set_speed(Speed::VeryHigh); | ||
| 1259 | d0_pin.set_speed(Speed::VeryHigh); | ||
| 1260 | }); | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | fn deconfigure(&mut self) { | ||
| 1264 | let (clk_pin, cmd_pin, d0_pin) = self; | ||
| 1265 | |||
| 1266 | critical_section::with(|_| unsafe { | ||
| 1267 | clk_pin.set_as_disconnected(); | ||
| 1268 | cmd_pin.set_as_disconnected(); | ||
| 1269 | d0_pin.set_as_disconnected(); | ||
| 1270 | }); | ||
| 1271 | } | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | foreach_peripheral!( | ||
| 1275 | (sdmmc, $inst:ident) => { | ||
| 1276 | impl sealed::Instance for peripherals::$inst { | ||
| 1277 | type Interrupt = crate::interrupt::$inst; | ||
| 1278 | |||
| 1279 | fn inner() -> SdmmcInner { | ||
| 1280 | const INNER: SdmmcInner = SdmmcInner(crate::pac::$inst); | ||
| 1281 | INNER | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | fn state() -> &'static ::embassy::waitqueue::AtomicWaker { | ||
| 1285 | static WAKER: ::embassy::waitqueue::AtomicWaker = ::embassy::waitqueue::AtomicWaker::new(); | ||
| 1286 | &WAKER | ||
| 1287 | } | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | impl Instance for peripherals::$inst {} | ||
| 1291 | }; | ||
| 1292 | ); | ||
| 1293 | |||
| 1294 | #[cfg(feature = "sdmmc-rs")] | ||
| 1295 | mod sdmmc_rs { | ||
| 1296 | use super::*; | ||
| 1297 | use core::future::Future; | ||
| 1298 | use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx}; | ||
| 1299 | |||
| 1300 | impl<'d, T: Instance, P: Pins<T>> BlockDevice for Sdmmc<'d, T, P> { | ||
| 1301 | type Error = Error; | ||
| 1302 | type ReadFuture<'a> | ||
| 1303 | where | ||
| 1304 | Self: 'a, | ||
| 1305 | = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 1306 | type WriteFuture<'a> | ||
| 1307 | where | ||
| 1308 | Self: 'a, | ||
| 1309 | = impl Future<Output = Result<(), Self::Error>> + 'a; | ||
| 1310 | |||
| 1311 | fn read<'a>( | ||
| 1312 | &'a mut self, | ||
| 1313 | blocks: &'a mut [Block], | ||
| 1314 | start_block_idx: BlockIdx, | ||
| 1315 | _reason: &str, | ||
| 1316 | ) -> Self::ReadFuture<'a> { | ||
| 1317 | async move { | ||
| 1318 | let card_capacity = self.card()?.card_type; | ||
| 1319 | let inner = T::inner(); | ||
| 1320 | let state = T::state(); | ||
| 1321 | let mut address = start_block_idx.0; | ||
| 1322 | |||
| 1323 | for block in blocks.iter_mut() { | ||
| 1324 | let block: &mut [u8; 512] = &mut block.contents; | ||
| 1325 | |||
| 1326 | // NOTE(unsafe) Block uses align(4) | ||
| 1327 | let buf = unsafe { &mut *(block as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 1328 | inner | ||
| 1329 | .read_block( | ||
| 1330 | address, | ||
| 1331 | buf, | ||
| 1332 | card_capacity, | ||
| 1333 | state, | ||
| 1334 | self.config.data_transfer_timeout, | ||
| 1335 | ) | ||
| 1336 | .await?; | ||
| 1337 | address += 1; | ||
| 1338 | } | ||
| 1339 | Ok(()) | ||
| 1340 | } | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | fn write<'a>( | ||
| 1344 | &'a mut self, | ||
| 1345 | blocks: &'a [Block], | ||
| 1346 | start_block_idx: BlockIdx, | ||
| 1347 | ) -> Self::WriteFuture<'a> { | ||
| 1348 | async move { | ||
| 1349 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 1350 | let inner = T::inner(); | ||
| 1351 | let state = T::state(); | ||
| 1352 | let mut address = start_block_idx.0; | ||
| 1353 | |||
| 1354 | for block in blocks.iter() { | ||
| 1355 | let block: &[u8; 512] = &block.contents; | ||
| 1356 | |||
| 1357 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1358 | let buf = unsafe { &*(block as *const [u8; 512] as *const [u32; 128]) }; | ||
| 1359 | inner | ||
| 1360 | .write_block(address, buf, card, state, self.config.data_transfer_timeout) | ||
| 1361 | .await?; | ||
| 1362 | address += 1; | ||
| 1363 | } | ||
| 1364 | Ok(()) | ||
| 1365 | } | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | fn num_blocks(&self) -> Result<BlockCount, Self::Error> { | ||
| 1369 | let card = self.card()?; | ||
| 1370 | let count = card.csd.block_count(); | ||
| 1371 | Ok(BlockCount(count)) | ||
| 1372 | } | ||
| 1373 | } | ||
| 1374 | } | ||
diff --git a/stm32-data b/stm32-data | |||
| Subproject d3e8a2fe63eeb403102559f3f9917d9fcf27e1a | Subproject 9a23a90cc038ce657611770d919c26e89719d1f | ||
