Skip to content

Commit 78e4758

Browse files
committed
Improve SDMMC reliability.
1 parent 1bc395e commit 78e4758

File tree

1 file changed

+147
-71
lines changed

1 file changed

+147
-71
lines changed

src/sdmmc.rs

Lines changed: 147 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -167,38 +167,73 @@ pub enum Error {
167167
TxUnderrun,
168168
}
169169

170-
macro_rules! datapath_err {
171-
($sta:expr) => {
170+
macro_rules! sta_rx_tx_err {
171+
($sta:expr, $sdmmc:expr) => {
172172
if $sta.dcrcfail().bit() {
173+
clear_static_data_flags(&$sdmmc.icr);
174+
173175
return Err(Error::DataCrc);
174176
}
175177

176178
if $sta.dtimeout().bit() {
179+
clear_static_data_flags(&$sdmmc.icr);
180+
177181
return Err(Error::Timeout);
178182
}
179183
};
180184
}
181185

182-
macro_rules! datapath_rx_err {
183-
($sta:expr) => {
184-
if $sta.rxoverr().bit() {
186+
macro_rules! sta_rx {
187+
($sdmmc:expr) => {{
188+
let sta = $sdmmc.sta.read();
189+
190+
if sta.rxoverr().bit() {
191+
clear_static_data_flags(&$sdmmc.icr);
192+
185193
return Err(Error::RxOverrun);
186194
}
187195

188-
datapath_err!($sta)
189-
};
196+
sta_rx_tx_err!(sta, $sdmmc);
197+
198+
sta
199+
}};
190200
}
191201

192-
macro_rules! datapath_tx_err {
193-
($sta:expr) => {
194-
if $sta.rxoverr().bit() {
195-
return Err(Error::RxOverrun);
202+
macro_rules! sta_tx {
203+
($sdmmc:expr) => {{
204+
let sta = $sdmmc.sta.read();
205+
206+
if sta.txunderr().bit() {
207+
clear_static_data_flags(&$sdmmc.icr);
208+
209+
return Err(Error::TxUnderrun);
196210
}
197211

198-
datapath_err!($sta)
199-
};
212+
sta_rx_tx_err!(sta, $sdmmc);
213+
214+
sta
215+
}};
200216
}
201217

218+
#[inline]
219+
fn clear_static_data_flags(icr: &sdmmc1::ICR) {
220+
icr.modify(|_, w| {
221+
w.dcrcfailc()
222+
.set_bit()
223+
.dtimeoutc()
224+
.set_bit()
225+
.txunderrc()
226+
.set_bit()
227+
.rxoverrc()
228+
.set_bit()
229+
.dataendc()
230+
.set_bit()
231+
.dbckendc()
232+
.set_bit()
233+
})
234+
}
235+
236+
#[inline]
202237
fn clear_all_interrupts(icr: &sdmmc1::ICR) {
203238
icr.modify(|_, w| {
204239
w.ccrcfailc()
@@ -440,40 +475,64 @@ impl Sdmmc {
440475

441476
self.cmd(common_cmd::set_block_length(512))?;
442477

443-
let bytes = blocks.len() * 512;
444-
self.start_datapath_transfer(bytes as u32, 9, Dir::CardToHost);
478+
let block_count = blocks.len();
479+
let byte_count = block_count * 512;
480+
self.start_datapath_transfer(byte_count as u32, 9, Dir::CardToHost);
445481

446-
match blocks.len() {
482+
match block_count {
447483
0 => return Ok(()),
448484
1 => self.cmd(common_cmd::read_single_block(addr))?,
449485
_ => self.cmd(common_cmd::read_multiple_blocks(addr))?,
450486
}
451487

488+
let mut current_block = &mut blocks[0];
489+
452490
let mut i = 0;
453491
let timeout: u32 = 0xffff_ffff;
454492
for _ in 0..timeout {
455-
let sta = self.sdmmc.sta.read();
456-
457-
datapath_rx_err!(sta);
493+
let sta = sta_rx!(self.sdmmc);
458494

459-
if i == bytes {
495+
// If we received all data, wait for transfer to end.
496+
if i == byte_count {
460497
if sta.dbckend().bit() {
461-
if blocks.len() > 1 {
498+
clear_static_data_flags(&self.sdmmc.icr);
499+
500+
if block_count > 1 {
462501
self.cmd(common_cmd::stop_transmission())?;
463502
}
464503

465504
return Ok(());
466505
}
467-
} else if sta.rxfifohf().bit() {
468-
for _ in 0..8 {
469-
let bits = u32::from_be(self.sdmmc.fifo.read().bits());
470-
let start = i % 512;
471-
blocks[i / 512][start..(start + 4)].copy_from_slice(&bits.to_be_bytes());
472-
i += 4;
506+
}
507+
// If the FIFO is half-full, receive some data.
508+
else if sta.rxfifohf().bit() {
509+
let offset = i % 512;
510+
511+
// SAFETY: We always read exactly 32 bytes from the FIFO into
512+
// a 512-byte block. We move to the next block if needed before
513+
// the next iteration.
514+
//
515+
// NOTE: This is needed since checked access takes to long and
516+
// results in a FIFO overrun error on higher clock speeds.
517+
unsafe {
518+
for j in 0..8 {
519+
let current_block = current_block.as_mut_ptr() as *mut [u8; 4];
520+
let current_bytes = &mut *current_block.add(offset / 4 + j);
521+
522+
let bytes = u32::from_be(self.sdmmc.fifo.read().bits()).to_be_bytes();
523+
*current_bytes = bytes;
524+
}
525+
}
526+
527+
i += 4 * 8;
528+
529+
if i % 512 == 0 && i != byte_count {
530+
current_block = &mut blocks[i / 512];
473531
}
474532
}
475533
}
476534

535+
clear_static_data_flags(&self.sdmmc.icr);
477536
Err(Error::SoftwareTimeout)
478537
}
479538

@@ -499,51 +558,64 @@ impl Sdmmc {
499558
_ => addr,
500559
};
501560

502-
let bytes = blocks.len() * 512;
561+
let byte_count = blocks.len() * 512;
503562
self.cmd(common_cmd::set_block_length(512))?;
504-
self.start_datapath_transfer(bytes as u32, 9, Dir::HostToCard);
563+
self.start_datapath_transfer(byte_count as u32, 9, Dir::HostToCard);
505564

506565
match blocks.len() {
507566
0 => return Ok(()),
508567
1 => self.cmd(common_cmd::write_single_block(addr))?,
509568
_ => self.cmd(common_cmd::write_multiple_blocks(addr))?,
510569
}
511570

571+
let mut current_block = &blocks[0];
572+
512573
let mut i = 0;
513574
loop {
514-
let sta = self.sdmmc.sta.read();
515-
516-
datapath_tx_err!(sta);
575+
let sta = sta_tx!(self.sdmmc);
517576

518-
if i == bytes {
519-
// If we sent all data, wait for transfer to end.
577+
// If we sent all data, wait for transfer to end.
578+
if i == byte_count {
520579
if sta.dbckend().bit() {
580+
clear_static_data_flags(&self.sdmmc.icr);
581+
521582
if blocks.len() > 1 {
522583
self.cmd(common_cmd::stop_transmission())?;
523584
}
524585

525586
break;
526587
}
527-
} else {
528-
// If the FIFO is half-empty, send some data.
529-
if sta.txfifohe().bit() {
530-
for _ in 0..8 {
531-
let block = &blocks[i / 512];
532-
let start = i % 512;
533-
534-
let bits = u32::from_be_bytes([
535-
block[start],
536-
block[start + 1],
537-
block[start + 2],
538-
block[start + 3],
539-
]);
540-
self.sdmmc.fifo.write(|w| unsafe { w.bits(bits.to_be()) });
541-
i += 4;
588+
}
589+
// If the FIFO is half-empty, send some data.
590+
else if sta.txfifohe().bit() {
591+
let offset = i % 512;
592+
593+
// SAFETY: We always write exactly 32 bytes into the FIFO from
594+
// a 512-byte block. We move to the next block if needed before
595+
// the next iteration.
596+
//
597+
// NOTE: This is needed since checked access takes to long and
598+
// results in a FIFO underrun error on higher clock speeds.
599+
unsafe {
600+
for j in 0..8 {
601+
let current_block = current_block.as_ptr() as *const [u8; 4];
602+
let current_bytes = &*current_block.add(offset / 4 + j);
603+
604+
let bits = u32::from_be_bytes(*current_bytes).to_be();
605+
self.sdmmc.fifo.write(|w| w.bits(bits));
542606
}
543607
}
608+
609+
i += 4 * 8;
610+
611+
if i % 512 == 0 && i != byte_count {
612+
current_block = &blocks[i / 512];
613+
}
544614
}
545615
}
546616

617+
clear_static_data_flags(&self.sdmmc.icr);
618+
547619
let timeout: u32 = 0xffff_ffff;
548620
for _ in 0..timeout {
549621
if self.card_ready()? {
@@ -631,23 +703,22 @@ impl Sdmmc {
631703

632704
let timeout: u32 = 0xffff_ffff;
633705
for _ in 0..timeout {
634-
let sta = self.sdmmc.sta.read();
635-
636-
datapath_rx_err!(sta);
706+
let sta = sta_rx!(self.sdmmc);
637707

708+
// If we received all data, wait for transfer to end.
638709
if i == 0 {
639710
if sta.dbckend().bit() {
711+
clear_static_data_flags(&self.sdmmc.icr);
712+
640713
return Ok(SCR::from(scr));
641714
}
642-
} else {
643-
if sta.rxdavl().bit_is_set() {
644-
i -= 1;
715+
} else if sta.rxdavl().bit_is_set() {
716+
i -= 1;
645717

646-
let bits = u32::from_be(self.sdmmc.fifo.read().bits());
647-
scr[i] = bits.to_le();
718+
let bits = u32::from_be(self.sdmmc.fifo.read().bits());
719+
scr[i] = bits.to_le();
648720

649-
continue;
650-
}
721+
continue;
651722
}
652723
}
653724

@@ -688,12 +759,13 @@ impl Sdmmc {
688759
let mut i = sd_status.len();
689760
let timeout: u32 = 0xffff_ffff;
690761
for _ in 0..timeout {
691-
let sta = self.sdmmc.sta.read();
692-
693-
datapath_rx_err!(sta);
762+
let sta = sta_rx!(self.sdmmc);
694763

764+
// If we received all data, wait for transfer to end.
695765
if i == 0 {
696766
if sta.dbckend().bit() {
767+
clear_static_data_flags(&self.sdmmc.icr);
768+
697769
return Ok(SDStatus::from(sd_status));
698770
}
699771
} else if sta.rxfifohf().bit() {
@@ -764,29 +836,33 @@ impl Sdmmc {
764836
for _ in 0..timeout {
765837
let sta = self.sdmmc.sta.read();
766838

839+
// Command transfer still in progress.
767840
if sta.cmdact().bit_is_set() {
768-
// Command transfer still in progress.
769841
continue;
770842
}
771843

772-
if cmd.response_len() == ResponseLen::Zero {
773-
if sta.ctimeout().bit() {
774-
return Err(Error::Timeout);
775-
}
844+
if sta.ctimeout().bit() {
845+
self.sdmmc.icr.modify(|_, w| w.ctimeoutc().set_bit());
776846

847+
return Err(Error::Timeout);
848+
}
849+
850+
if cmd.response_len() == ResponseLen::Zero {
777851
if sta.cmdsent().bit() {
852+
self.sdmmc.icr.modify(|_, w| w.cmdsentc().set_bit());
853+
778854
return Ok(());
779855
}
780856
} else {
781-
if sta.ctimeout().bit() {
782-
return Err(Error::Timeout);
783-
}
784-
785857
if sta.ccrcfail().bit() {
858+
self.sdmmc.icr.modify(|_, w| w.ccrcfailc().set_bit());
859+
786860
return Err(Error::CommandCrc);
787861
}
788862

789863
if sta.cmdrend().bit() {
864+
self.sdmmc.icr.modify(|_, w| w.cmdrendc().set_bit());
865+
790866
return Ok(());
791867
}
792868
}

0 commit comments

Comments
 (0)