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