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