Skip to content

Commit 269b118

Browse files
committed
Add SDMMC support.
1 parent 9b42ebb commit 269b118

File tree

3 files changed

+333
-0
lines changed

3 files changed

+333
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ embedded-dma = "0.1"
2828
bxcan = ">=0.4, <0.7"
2929
fugit = "0.3.5"
3030
bitfield = "0.13.2"
31+
sdio-host = "0.7.0"
3132

3233
[dependencies.rand_core]
3334
version = "0.6.2"

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ pub mod qspi;
158158
pub mod rcc;
159159
pub mod rng;
160160
pub mod rtc;
161+
pub mod sdmmc;
161162
pub mod serial;
162163
pub mod signature;
163164
pub mod spi;

src/sdmmc.rs

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
use fugit::HertzU32 as Hertz;
2+
use sdio_host::{
3+
common_cmd::{self, ResponseLen},
4+
Cmd,
5+
};
6+
7+
use crate::{
8+
gpio::{self, Alternate, PushPull},
9+
pac::{sdmmc1, SDMMC1},
10+
rcc::{Clocks, Enable, Reset, APB2},
11+
};
12+
13+
pub trait PinClk {}
14+
pub trait PinCmd {}
15+
pub trait PinD0 {}
16+
pub trait PinD1 {}
17+
pub trait PinD2 {}
18+
pub trait PinD3 {}
19+
pub trait PinD4 {}
20+
pub trait PinD5 {}
21+
pub trait PinD6 {}
22+
pub trait PinD7 {}
23+
24+
pub trait Pins {
25+
const BUSWIDTH: BusWidth;
26+
}
27+
28+
impl<CLK, CMD, D0> Pins for (CLK, CMD, D0)
29+
where
30+
CLK: PinClk,
31+
CMD: PinCmd,
32+
D0: PinD0,
33+
{
34+
const BUSWIDTH: BusWidth = BusWidth::BusWidth1;
35+
}
36+
37+
impl<CLK, CMD, D0, D1, D2, D3> Pins for (CLK, CMD, D0, D1, D2, D3)
38+
where
39+
CLK: PinClk,
40+
CMD: PinCmd,
41+
D0: PinD0,
42+
D1: PinD1,
43+
D2: PinD2,
44+
D3: PinD3,
45+
{
46+
const BUSWIDTH: BusWidth = BusWidth::BusWidth4;
47+
}
48+
49+
impl<CLK, CMD, D0, D1, D2, D3, D4, D5, D6, D7> Pins for (CLK, CMD, D0, D1, D2, D3, D4, D5, D6, D7)
50+
where
51+
CLK: PinClk,
52+
CMD: PinCmd,
53+
D0: PinD0,
54+
D1: PinD1,
55+
D2: PinD2,
56+
D3: PinD3,
57+
D4: PinD4,
58+
D5: PinD5,
59+
D6: PinD6,
60+
D7: PinD7,
61+
{
62+
const BUSWIDTH: BusWidth = BusWidth::BusWidth8;
63+
}
64+
65+
macro_rules! pins {
66+
($(CLK: [$($CLK:ty),*] CMD: [$($CMD:ty),*] D0: [$($D0:ty),*] D1: [$($D1:ty),*] D2: [$($D2:ty),*] D3: [$($D3:ty),*] D4: [$($D4:ty),*] D5: [$($D5:ty),*] D6: [$($D6:ty),*] D7: [$($D7:ty),*])+) => {
67+
$(
68+
$(
69+
impl PinClk for $CLK {}
70+
)*
71+
$(
72+
impl PinCmd for $CMD {}
73+
)*
74+
$(
75+
impl PinD0 for $D0 {}
76+
)*
77+
$(
78+
impl PinD1 for $D1 {}
79+
)*
80+
$(
81+
impl PinD2 for $D2 {}
82+
)*
83+
$(
84+
impl PinD3 for $D3 {}
85+
)*
86+
$(
87+
impl PinD4 for $D4 {}
88+
)*
89+
$(
90+
impl PinD5 for $D5 {}
91+
)*
92+
$(
93+
impl PinD6 for $D6 {}
94+
)*
95+
$(
96+
impl PinD7 for $D7 {}
97+
)*
98+
)+
99+
}
100+
}
101+
102+
#[cfg(any(feature = "stm32l496",))]
103+
pins! {
104+
CLK: [gpio::PC12<Alternate<PushPull, 12>>]
105+
CMD: [gpio::PD2<Alternate<PushPull, 12>>]
106+
D0: [gpio::PC8<Alternate<PushPull, 12>>]
107+
D1: [gpio::PC9<Alternate<PushPull, 12>>]
108+
D2: [gpio::PC10<Alternate<PushPull, 12>>]
109+
D3: [gpio::PC11<Alternate<PushPull, 12>>]
110+
D4: [gpio::PB8<Alternate<PushPull, 12>>]
111+
D5: [gpio::PB9<Alternate<PushPull, 12>>]
112+
D6: [gpio::PC6<Alternate<PushPull, 12>>]
113+
D7: [gpio::PC7<Alternate<PushPull, 12>>]
114+
}
115+
116+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
117+
#[repr(u8)]
118+
pub enum BusWidth {
119+
BusWidth1 = 0,
120+
BusWidth4 = 1,
121+
BusWidth8 = 2,
122+
}
123+
124+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
125+
#[repr(u8)]
126+
pub enum ClockFreq {
127+
Freq24MHz = 0, // 48 MHz / (0 + 2) < 25 MHz
128+
Freq400KHz = 118, // 48 MHz / (118 + 2) < 400 kHz
129+
}
130+
131+
#[derive(Debug)]
132+
pub enum Error {
133+
NoCard,
134+
SoftwareTimeout,
135+
Crc,
136+
DataCrcFail,
137+
RxOverFlow,
138+
Timeout,
139+
TxUnderErr,
140+
}
141+
142+
fn status_to_error(sta: sdmmc1::sta::R) -> Result<(), Error> {
143+
if sta.ctimeout().bit_is_set() {
144+
return Err(Error::Timeout);
145+
} else if sta.ccrcfail().bit() {
146+
return Err(Error::Crc);
147+
} else if sta.dcrcfail().bit() {
148+
return Err(Error::DataCrcFail);
149+
} else if sta.rxoverr().bit() {
150+
return Err(Error::RxOverFlow);
151+
} else if sta.dtimeout().bit() {
152+
return Err(Error::Timeout);
153+
} else if sta.txunderr().bit() {
154+
return Err(Error::TxUnderErr);
155+
}
156+
Ok(())
157+
}
158+
159+
fn clear_all_interrupts(icr: &sdmmc1::ICR) {
160+
icr.modify(|_, w| {
161+
w.ccrcfailc()
162+
.set_bit()
163+
.ctimeoutc()
164+
.set_bit()
165+
.ceataendc()
166+
.set_bit()
167+
.cmdrendc()
168+
.set_bit()
169+
.cmdsentc()
170+
.set_bit()
171+
.dataendc()
172+
.set_bit()
173+
.dbckendc()
174+
.set_bit()
175+
.dcrcfailc()
176+
.set_bit()
177+
.dtimeoutc()
178+
.set_bit()
179+
.sdioitc()
180+
.set_bit()
181+
.stbiterrc()
182+
.set_bit()
183+
.rxoverrc()
184+
.set_bit()
185+
.txunderrc()
186+
.set_bit()
187+
});
188+
}
189+
190+
pub struct SdMmc {
191+
sdmmc: SDMMC1,
192+
clock: Hertz,
193+
}
194+
195+
impl SdMmc {
196+
pub fn new<PINS: Pins>(
197+
mut sdmmc: SDMMC1,
198+
_pins: PINS,
199+
apb2: &mut APB2,
200+
clocks: &Clocks,
201+
) -> Self {
202+
SDMMC1::enable(apb2);
203+
SDMMC1::reset(apb2);
204+
205+
let clock = clocks.sysclk();
206+
207+
sdmmc.clkcr.modify(|_, w| unsafe {
208+
w.negedge()
209+
.clear_bit() // Rising Edge
210+
.bypass()
211+
.clear_bit() // Disable bypass.
212+
.pwrsav()
213+
.clear_bit() // Disable power-save.
214+
.widbus()
215+
.bits(0) // Bus Width 1
216+
.hwfc_en()
217+
.clear_bit() // Disable hardware flow-control.
218+
.clkdiv()
219+
.bits(0 as u8) // Clock must be <= 400 kHz while in identification mode.
220+
.clken()
221+
.clear_bit() // Disable clock.
222+
});
223+
224+
let mut host = Self { sdmmc, clock };
225+
226+
host.power_card(false);
227+
228+
host
229+
}
230+
231+
pub fn init(&mut self) -> Result<(), Error> {
232+
self.power_card(true);
233+
234+
// Enable clock.
235+
self.sdmmc.clkcr.modify(|_, w| w.clken().set_bit());
236+
237+
self.cmd(common_cmd::idle())
238+
}
239+
240+
pub fn power_card(&mut self, on: bool) {
241+
self.sdmmc
242+
.power
243+
.modify(|_, w| unsafe { w.pwrctrl().bits(if on { 0b11 } else { 0b00 }) });
244+
245+
// Wait for 2 ms after power mode change.
246+
cortex_m::asm::delay(2 * (self.clock.raw() / 1000));
247+
}
248+
249+
pub fn cmd<R: common_cmd::Resp>(&self, cmd: Cmd<R>) -> Result<(), Error> {
250+
while self.sdmmc.sta.read().cmdact().bit_is_set() {}
251+
252+
// Clear the interrupts before we start
253+
clear_all_interrupts(&self.sdmmc.icr);
254+
255+
self.sdmmc
256+
.arg
257+
.write(|w| unsafe { w.cmdarg().bits(cmd.arg) });
258+
259+
let waitresp = match cmd.response_len() {
260+
ResponseLen::Zero => 0b00,
261+
ResponseLen::R48 => 0b01,
262+
ResponseLen::R136 => 0b11,
263+
};
264+
265+
self.sdmmc.cmd.write(|w| unsafe {
266+
w.cmdindex()
267+
.bits(cmd.cmd)
268+
.waitresp()
269+
.bits(waitresp)
270+
.waitint()
271+
.clear_bit()
272+
.cpsmen()
273+
.set_bit()
274+
});
275+
276+
let mut timeout = 5000 * (self.clock.raw() / 8 / 1000);
277+
278+
let sta = if cmd.response_len() == ResponseLen::Zero {
279+
280+
loop {
281+
let sta = self.sdmmc.sta.read();
282+
283+
if sta.cmdact().bit_is_clear()
284+
&& (sta.ctimeout().bit_is_set() || sta.cmdsent().bit_is_set())
285+
{
286+
break sta;
287+
}
288+
if timeout == 0 {
289+
return Err(Error::SoftwareTimeout);
290+
}
291+
292+
timeout -= 1;
293+
}
294+
} else {
295+
296+
let sta = loop {
297+
timeout -= 1;
298+
299+
if timeout == 0 {
300+
return Err(Error::SoftwareTimeout);
301+
}
302+
303+
let sta = self.sdmmc.sta.read();
304+
305+
if sta.ccrcfail().bit() || sta.cmdrend().bit() || sta.ctimeout().bit() {
306+
break sta;
307+
}
308+
};
309+
310+
if sta.ctimeout().bit() {
311+
self.sdmmc.icr.modify(|_, w| w.ctimeoutc().set_bit());
312+
313+
return Err(Error::Timeout);
314+
}
315+
316+
if sta.ccrcfail().bit() {
317+
self.sdmmc.icr.modify(|_, w| w.ccrcfailc().set_bit());
318+
319+
return Err(Error::Crc);
320+
}
321+
322+
if self.sdmmc.respcmd.read().respcmd().bits() != cmd.cmd {
323+
return Err(Error::Crc);
324+
}
325+
326+
sta
327+
};
328+
329+
status_to_error(sta)
330+
}
331+
}

0 commit comments

Comments
 (0)