diff options
| author | Anton Lazarev <[email protected]> | 2025-03-28 15:54:47 -0700 |
|---|---|---|
| committer | Anton Lazarev <[email protected]> | 2025-03-31 12:47:41 -0700 |
| commit | dc31bfd8295c3626794e0b1b38804af2acc59053 (patch) | |
| tree | 85a3290398cb513667d1cf5e59ee5f888cd9d6b5 | |
| parent | 0a231505d8225f3f36f39b0be1ded4304fb7ccca (diff) | |
refactor to reduce code duplication
| -rw-r--r-- | embassy-stm32/src/sdmmc/mod.rs | 549 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/sdmmc.rs | 2 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/sdmmc.rs | 2 | ||||
| -rw-r--r-- | examples/stm32h7/src/bin/sdmmc.rs | 2 | ||||
| -rw-r--r-- | tests/stm32/src/bin/sdmmc.rs | 4 |
5 files changed, 217 insertions, 342 deletions
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index a0c3573a9..63868e5ae 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -1010,6 +1010,37 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1010 | Self::stop_datapath(); | 1010 | Self::stop_datapath(); |
| 1011 | } | 1011 | } |
| 1012 | 1012 | ||
| 1013 | /// Wait for a previously started datapath transfer to complete from an interrupt. | ||
| 1014 | #[inline] | ||
| 1015 | async fn complete_datapath_transfer() -> Result<(), Error> { | ||
| 1016 | let regs = T::regs(); | ||
| 1017 | |||
| 1018 | let res = poll_fn(|cx| { | ||
| 1019 | T::state().register(cx.waker()); | ||
| 1020 | let status = regs.star().read(); | ||
| 1021 | |||
| 1022 | if status.dcrcfail() { | ||
| 1023 | return Poll::Ready(Err(Error::Crc)); | ||
| 1024 | } | ||
| 1025 | if status.dtimeout() { | ||
| 1026 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1027 | } | ||
| 1028 | #[cfg(sdmmc_v1)] | ||
| 1029 | if status.stbiterr() { | ||
| 1030 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 1031 | } | ||
| 1032 | if status.dataend() { | ||
| 1033 | return Poll::Ready(Ok(())); | ||
| 1034 | } | ||
| 1035 | Poll::Pending | ||
| 1036 | }) | ||
| 1037 | .await; | ||
| 1038 | |||
| 1039 | Self::clear_interrupt_flags(); | ||
| 1040 | |||
| 1041 | res | ||
| 1042 | } | ||
| 1043 | |||
| 1013 | /// Read a data block. | 1044 | /// Read a data block. |
| 1014 | #[inline] | 1045 | #[inline] |
| 1015 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | 1046 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { |
| @@ -1026,7 +1057,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1026 | }; | 1057 | }; |
| 1027 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | 1058 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 |
| 1028 | 1059 | ||
| 1029 | let regs = T::regs(); | ||
| 1030 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1060 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1031 | 1061 | ||
| 1032 | let transfer = Self::prepare_datapath_read( | 1062 | let transfer = Self::prepare_datapath_read( |
| @@ -1040,27 +1070,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1040 | InterruptHandler::<T>::data_interrupts(true); | 1070 | InterruptHandler::<T>::data_interrupts(true); |
| 1041 | Self::cmd(common_cmd::read_single_block(address), true)?; | 1071 | Self::cmd(common_cmd::read_single_block(address), true)?; |
| 1042 | 1072 | ||
| 1043 | let res = poll_fn(|cx| { | 1073 | let res = Self::complete_datapath_transfer().await; |
| 1044 | T::state().register(cx.waker()); | ||
| 1045 | let status = regs.star().read(); | ||
| 1046 | |||
| 1047 | if status.dcrcfail() { | ||
| 1048 | return Poll::Ready(Err(Error::Crc)); | ||
| 1049 | } | ||
| 1050 | if status.dtimeout() { | ||
| 1051 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1052 | } | ||
| 1053 | #[cfg(sdmmc_v1)] | ||
| 1054 | if status.stbiterr() { | ||
| 1055 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 1056 | } | ||
| 1057 | if status.dataend() { | ||
| 1058 | return Poll::Ready(Ok(())); | ||
| 1059 | } | ||
| 1060 | Poll::Pending | ||
| 1061 | }) | ||
| 1062 | .await; | ||
| 1063 | Self::clear_interrupt_flags(); | ||
| 1064 | 1074 | ||
| 1065 | if res.is_ok() { | 1075 | if res.is_ok() { |
| 1066 | on_drop.defuse(); | 1076 | on_drop.defuse(); |
| @@ -1085,7 +1095,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1085 | }; | 1095 | }; |
| 1086 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | 1096 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 |
| 1087 | 1097 | ||
| 1088 | let regs = T::regs(); | ||
| 1089 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1098 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1090 | 1099 | ||
| 1091 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | 1100 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes |
| @@ -1098,27 +1107,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1098 | #[cfg(sdmmc_v2)] | 1107 | #[cfg(sdmmc_v2)] |
| 1099 | Self::cmd(common_cmd::write_single_block(address), true)?; | 1108 | Self::cmd(common_cmd::write_single_block(address), true)?; |
| 1100 | 1109 | ||
| 1101 | let res = poll_fn(|cx| { | 1110 | let res = Self::complete_datapath_transfer().await; |
| 1102 | T::state().register(cx.waker()); | ||
| 1103 | let status = regs.star().read(); | ||
| 1104 | |||
| 1105 | if status.dcrcfail() { | ||
| 1106 | return Poll::Ready(Err(Error::Crc)); | ||
| 1107 | } | ||
| 1108 | if status.dtimeout() { | ||
| 1109 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1110 | } | ||
| 1111 | #[cfg(sdmmc_v1)] | ||
| 1112 | if status.stbiterr() { | ||
| 1113 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 1114 | } | ||
| 1115 | if status.dataend() { | ||
| 1116 | return Poll::Ready(Ok(())); | ||
| 1117 | } | ||
| 1118 | Poll::Pending | ||
| 1119 | }) | ||
| 1120 | .await; | ||
| 1121 | Self::clear_interrupt_flags(); | ||
| 1122 | 1111 | ||
| 1123 | match res { | 1112 | match res { |
| 1124 | Ok(_) => { | 1113 | Ok(_) => { |
| @@ -1151,8 +1140,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1151 | /// | 1140 | /// |
| 1152 | /// # Errors | 1141 | /// # Errors |
| 1153 | /// | 1142 | /// |
| 1154 | /// Returns Error::NoCard if [`init_card`](#method.init_card) | 1143 | /// Returns Error::NoCard if [`init_sd_card`](#method.init_sd_card) or |
| 1155 | /// has not previously succeeded | 1144 | /// [`init_emmc`](#method.init_emmc) has not previously succeeded |
| 1156 | #[inline] | 1145 | #[inline] |
| 1157 | pub fn card(&self) -> Result<&SdmmcPeripheral, Error> { | 1146 | pub fn card(&self) -> Result<&SdmmcPeripheral, Error> { |
| 1158 | self.card.as_ref().ok_or(Error::NoCard) | 1147 | self.card.as_ref().ok_or(Error::NoCard) |
| @@ -1170,22 +1159,20 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1170 | pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { | 1159 | pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { |
| 1171 | self.cmd_block = Some(cmd_block) | 1160 | self.cmd_block = Some(cmd_block) |
| 1172 | } | 1161 | } |
| 1173 | } | ||
| 1174 | |||
| 1175 | /// SD only | ||
| 1176 | impl<'d, T: Instance> Sdmmc<'d, T> { | ||
| 1177 | /// Initializes card (if present) and sets the bus at the specified frequency. | ||
| 1178 | pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 1179 | if self.d7.is_some() { | ||
| 1180 | return Err(Error::BusWidth); | ||
| 1181 | } | ||
| 1182 | 1162 | ||
| 1163 | async fn init_internal(&mut self, freq: Hertz, mut card: SdmmcPeripheral) -> Result<(), Error> { | ||
| 1183 | let regs = T::regs(); | 1164 | let regs = T::regs(); |
| 1184 | let ker_ck = T::frequency(); | 1165 | let ker_ck = T::frequency(); |
| 1185 | 1166 | ||
| 1186 | let bus_width = match self.d3.is_some() { | 1167 | let bus_width = match (self.d3.is_some(), self.d7.is_some()) { |
| 1187 | true => BusWidth::Four, | 1168 | (true, true) => { |
| 1188 | false => BusWidth::One, | 1169 | if matches!(card, SdmmcPeripheral::SdCard(_)) { |
| 1170 | return Err(Error::BusWidth); | ||
| 1171 | } | ||
| 1172 | BusWidth::Eight | ||
| 1173 | } | ||
| 1174 | (true, false) => BusWidth::Four, | ||
| 1175 | _ => BusWidth::One, | ||
| 1189 | }; | 1176 | }; |
| 1190 | 1177 | ||
| 1191 | // While the SD/SDIO card or eMMC is in identification mode, | 1178 | // While the SD/SDIO card or eMMC is in identification mode, |
| @@ -1206,47 +1193,75 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1206 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | 1193 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); |
| 1207 | Self::cmd(common_cmd::idle(), false)?; | 1194 | Self::cmd(common_cmd::idle(), false)?; |
| 1208 | 1195 | ||
| 1209 | // Check if cards supports CMD8 (with pattern) | 1196 | match card { |
| 1210 | Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | 1197 | SdmmcPeripheral::SdCard(ref mut card) => { |
| 1211 | let cic = CIC::from(regs.respr(0).read().cardstatus()); | 1198 | // Check if cards supports CMD8 (with pattern) |
| 1212 | 1199 | Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | |
| 1213 | if cic.pattern() != 0xAA { | 1200 | let cic = CIC::from(regs.respr(0).read().cardstatus()); |
| 1214 | return Err(Error::UnsupportedCardVersion); | ||
| 1215 | } | ||
| 1216 | 1201 | ||
| 1217 | if cic.voltage_accepted() & 1 == 0 { | 1202 | if cic.pattern() != 0xAA { |
| 1218 | return Err(Error::UnsupportedVoltage); | 1203 | return Err(Error::UnsupportedCardVersion); |
| 1219 | } | 1204 | } |
| 1220 | 1205 | ||
| 1221 | let mut card = Card::default(); | 1206 | if cic.voltage_accepted() & 1 == 0 { |
| 1207 | return Err(Error::UnsupportedVoltage); | ||
| 1208 | } | ||
| 1222 | 1209 | ||
| 1223 | let ocr = loop { | 1210 | let ocr = loop { |
| 1224 | // Signal that next command is a app command | 1211 | // Signal that next command is a app command |
| 1225 | Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55 | 1212 | Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55 |
| 1213 | |||
| 1214 | // 3.2-3.3V | ||
| 1215 | let voltage_window = 1 << 5; | ||
| 1216 | // Initialize card | ||
| 1217 | match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { | ||
| 1218 | // ACMD41 | ||
| 1219 | Ok(_) => (), | ||
| 1220 | Err(Error::Crc) => (), | ||
| 1221 | Err(err) => return Err(err), | ||
| 1222 | } | ||
| 1223 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | ||
| 1224 | if !ocr.is_busy() { | ||
| 1225 | // Power up done | ||
| 1226 | break ocr; | ||
| 1227 | } | ||
| 1228 | }; | ||
| 1226 | 1229 | ||
| 1227 | // 3.2-3.3V | 1230 | if ocr.high_capacity() { |
| 1228 | let voltage_window = 1 << 5; | 1231 | // Card is SDHC or SDXC or SDUC |
| 1229 | // Initialize card | 1232 | card.card_type = CardCapacity::HighCapacity; |
| 1230 | match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { | 1233 | } else { |
| 1231 | // ACMD41 | 1234 | card.card_type = CardCapacity::StandardCapacity; |
| 1232 | Ok(_) => (), | 1235 | } |
| 1233 | Err(Error::Crc) => (), | 1236 | card.ocr = ocr; |
| 1234 | Err(err) => return Err(err), | ||
| 1235 | } | 1237 | } |
| 1236 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | 1238 | SdmmcPeripheral::Emmc(ref mut emmc) => { |
| 1237 | if !ocr.is_busy() { | 1239 | let ocr = loop { |
| 1238 | // Power up done | 1240 | let high_voltage = 0b0 << 7; |
| 1239 | break ocr; | 1241 | let access_mode = 0b10 << 29; |
| 1242 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 1243 | // Initialize card | ||
| 1244 | match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 1245 | Ok(_) => (), | ||
| 1246 | Err(Error::Crc) => (), | ||
| 1247 | Err(err) => return Err(err), | ||
| 1248 | } | ||
| 1249 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 1250 | if !ocr.is_busy() { | ||
| 1251 | // Power up done | ||
| 1252 | break ocr; | ||
| 1253 | } | ||
| 1254 | }; | ||
| 1255 | |||
| 1256 | emmc.capacity = if ocr.access_mode() == 0b10 { | ||
| 1257 | // Card is SDHC or SDXC or SDUC | ||
| 1258 | CardCapacity::HighCapacity | ||
| 1259 | } else { | ||
| 1260 | CardCapacity::StandardCapacity | ||
| 1261 | }; | ||
| 1262 | emmc.ocr = ocr; | ||
| 1240 | } | 1263 | } |
| 1241 | }; | ||
| 1242 | |||
| 1243 | if ocr.high_capacity() { | ||
| 1244 | // Card is SDHC or SDXC or SDUC | ||
| 1245 | card.card_type = CardCapacity::HighCapacity; | ||
| 1246 | } else { | ||
| 1247 | card.card_type = CardCapacity::StandardCapacity; | ||
| 1248 | } | 1264 | } |
| 1249 | card.ocr = ocr; | ||
| 1250 | 1265 | ||
| 1251 | Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 | 1266 | Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 |
| 1252 | let cid0 = regs.respr(0).read().cardstatus() as u128; | 1267 | let cid0 = regs.respr(0).read().cardstatus() as u128; |
| @@ -1254,79 +1269,139 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1254 | let cid2 = regs.respr(2).read().cardstatus() as u128; | 1269 | let cid2 = regs.respr(2).read().cardstatus() as u128; |
| 1255 | let cid3 = regs.respr(3).read().cardstatus() as u128; | 1270 | let cid3 = regs.respr(3).read().cardstatus() as u128; |
| 1256 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | 1271 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); |
| 1257 | card.cid = cid.into(); | ||
| 1258 | 1272 | ||
| 1259 | Self::cmd(sd_cmd::send_relative_address(), false)?; | 1273 | match card { |
| 1260 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); | 1274 | SdmmcPeripheral::SdCard(ref mut card) => { |
| 1261 | card.rca = rca.address(); | 1275 | card.cid = cid.into(); |
| 1276 | |||
| 1277 | Self::cmd(sd_cmd::send_relative_address(), false)?; | ||
| 1278 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); | ||
| 1279 | card.rca = rca.address(); | ||
| 1280 | } | ||
| 1281 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1282 | emmc.cid = cid.into(); | ||
| 1283 | |||
| 1284 | emmc.rca = 1u16.into(); | ||
| 1285 | Self::cmd(emmc_cmd::assign_relative_address(emmc.rca), false)?; | ||
| 1286 | } | ||
| 1287 | } | ||
| 1262 | 1288 | ||
| 1263 | Self::cmd(common_cmd::send_csd(card.rca), false)?; | 1289 | Self::cmd(common_cmd::send_csd(card.get_address()), false)?; |
| 1264 | let csd0 = regs.respr(0).read().cardstatus() as u128; | 1290 | let csd0 = regs.respr(0).read().cardstatus() as u128; |
| 1265 | let csd1 = regs.respr(1).read().cardstatus() as u128; | 1291 | let csd1 = regs.respr(1).read().cardstatus() as u128; |
| 1266 | let csd2 = regs.respr(2).read().cardstatus() as u128; | 1292 | let csd2 = regs.respr(2).read().cardstatus() as u128; |
| 1267 | let csd3 = regs.respr(3).read().cardstatus() as u128; | 1293 | let csd3 = regs.respr(3).read().cardstatus() as u128; |
| 1268 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | 1294 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); |
| 1269 | card.csd = csd.into(); | ||
| 1270 | 1295 | ||
| 1271 | self.select_card(Some(card.rca))?; | 1296 | self.select_card(Some(card.get_address()))?; |
| 1297 | |||
| 1298 | let bus_width = match card { | ||
| 1299 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1300 | card.csd = csd.into(); | ||
| 1301 | |||
| 1302 | self.get_scr(card).await?; | ||
| 1272 | 1303 | ||
| 1273 | self.get_scr(&mut card).await?; | 1304 | if !card.scr.bus_width_four() { |
| 1305 | BusWidth::One | ||
| 1306 | } else { | ||
| 1307 | BusWidth::Four | ||
| 1308 | } | ||
| 1309 | } | ||
| 1310 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1311 | emmc.csd = csd.into(); | ||
| 1312 | |||
| 1313 | bus_width | ||
| 1314 | } | ||
| 1315 | }; | ||
| 1274 | 1316 | ||
| 1275 | // Set bus width | 1317 | // Set bus width |
| 1276 | let (width, acmd_arg) = match bus_width { | 1318 | let widbus = match bus_width { |
| 1277 | BusWidth::Eight => unimplemented!(), | 1319 | BusWidth::Eight => 2, |
| 1278 | BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), | 1320 | BusWidth::Four => 1, |
| 1279 | _ => (BusWidth::One, 0), | 1321 | BusWidth::One => 0, |
| 1322 | _ => unreachable!(), | ||
| 1280 | }; | 1323 | }; |
| 1281 | Self::cmd(common_cmd::app_cmd(card.rca), false)?; | 1324 | |
| 1282 | Self::cmd(sd_cmd::cmd6(acmd_arg), false)?; | 1325 | match card { |
| 1326 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1327 | let acmd_arg = match bus_width { | ||
| 1328 | BusWidth::Four if card.scr.bus_width_four() => 2, | ||
| 1329 | _ => 0, | ||
| 1330 | }; | ||
| 1331 | Self::cmd(common_cmd::app_cmd(card.rca), false)?; | ||
| 1332 | Self::cmd(sd_cmd::cmd6(acmd_arg), false)?; | ||
| 1333 | } | ||
| 1334 | SdmmcPeripheral::Emmc(_) => { | ||
| 1335 | // Write bus width to ExtCSD byte 183 | ||
| 1336 | Self::cmd( | ||
| 1337 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 1338 | false, | ||
| 1339 | )?; | ||
| 1340 | |||
| 1341 | // Wait for ready after R1b response | ||
| 1342 | loop { | ||
| 1343 | let status = self.read_status::<EMMC>(&card)?; | ||
| 1344 | |||
| 1345 | if status.ready_for_data() { | ||
| 1346 | break; | ||
| 1347 | } | ||
| 1348 | } | ||
| 1349 | } | ||
| 1350 | } | ||
| 1283 | 1351 | ||
| 1284 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | 1352 | // CPSMACT and DPSMACT must be 0 to set WIDBUS |
| 1285 | Self::wait_idle(); | 1353 | Self::wait_idle(); |
| 1286 | 1354 | ||
| 1287 | regs.clkcr().modify(|w| { | 1355 | regs.clkcr().modify(|w| w.set_widbus(widbus)); |
| 1288 | w.set_widbus(match width { | ||
| 1289 | BusWidth::One => 0, | ||
| 1290 | BusWidth::Four => 1, | ||
| 1291 | BusWidth::Eight => 2, | ||
| 1292 | _ => panic!("Invalid Bus Width"), | ||
| 1293 | }) | ||
| 1294 | }); | ||
| 1295 | 1356 | ||
| 1296 | // Set Clock | 1357 | // Set Clock |
| 1297 | if freq.0 <= 25_000_000 { | 1358 | if freq.0 <= 25_000_000 { |
| 1298 | // Final clock frequency | 1359 | // Final clock frequency |
| 1299 | self.clkcr_set_clkdiv(freq.0, width)?; | 1360 | self.clkcr_set_clkdiv(freq.0, bus_width)?; |
| 1300 | } else { | 1361 | } else { |
| 1301 | // Switch to max clock for SDR12 | 1362 | // Switch to max clock for SDR12 |
| 1302 | self.clkcr_set_clkdiv(25_000_000, width)?; | 1363 | self.clkcr_set_clkdiv(25_000_000, bus_width)?; |
| 1303 | } | 1364 | } |
| 1304 | 1365 | ||
| 1305 | self.card = Some(SdmmcPeripheral::SdCard(card)); | 1366 | self.card = Some(card); |
| 1306 | 1367 | ||
| 1307 | // Read status | 1368 | match card { |
| 1308 | self.read_sd_status().await?; | 1369 | SdmmcPeripheral::SdCard(_) => { |
| 1370 | // Read status | ||
| 1371 | self.read_sd_status().await?; | ||
| 1309 | 1372 | ||
| 1310 | if freq.0 > 25_000_000 { | 1373 | if freq.0 > 25_000_000 { |
| 1311 | // Switch to SDR25 | 1374 | // Switch to SDR25 |
| 1312 | self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; | 1375 | self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; |
| 1313 | 1376 | ||
| 1314 | if self.signalling == Signalling::SDR25 { | 1377 | if self.signalling == Signalling::SDR25 { |
| 1315 | // Set final clock frequency | 1378 | // Set final clock frequency |
| 1316 | self.clkcr_set_clkdiv(freq.0, width)?; | 1379 | self.clkcr_set_clkdiv(freq.0, bus_width)?; |
| 1317 | 1380 | ||
| 1318 | if self.read_status::<SD>(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer { | 1381 | if self.read_status::<SD>(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer { |
| 1319 | return Err(Error::SignalingSwitchFailed); | 1382 | return Err(Error::SignalingSwitchFailed); |
| 1383 | } | ||
| 1384 | } | ||
| 1320 | } | 1385 | } |
| 1386 | |||
| 1387 | // Read status after signalling change | ||
| 1388 | self.read_sd_status().await?; | ||
| 1389 | } | ||
| 1390 | SdmmcPeripheral::Emmc(_) => { | ||
| 1391 | self.read_ext_csd().await?; | ||
| 1321 | } | 1392 | } |
| 1322 | } | 1393 | } |
| 1323 | 1394 | ||
| 1324 | // Read status after signalling change | ||
| 1325 | self.read_sd_status().await?; | ||
| 1326 | |||
| 1327 | Ok(()) | 1395 | Ok(()) |
| 1328 | } | 1396 | } |
| 1329 | 1397 | ||
| 1398 | /// Initializes card (if present) and sets the bus at the specified frequency. | ||
| 1399 | /// | ||
| 1400 | /// SD only. | ||
| 1401 | pub async fn init_sd_card(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 1402 | self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await | ||
| 1403 | } | ||
| 1404 | |||
| 1330 | /// Switch mode using CMD6. | 1405 | /// Switch mode using CMD6. |
| 1331 | /// | 1406 | /// |
| 1332 | /// Attempt to set a new signalling mode. The selected | 1407 | /// Attempt to set a new signalling mode. The selected |
| @@ -1355,7 +1430,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1355 | }; | 1430 | }; |
| 1356 | 1431 | ||
| 1357 | // Arm `OnDrop` after the buffer, so it will be dropped first | 1432 | // Arm `OnDrop` after the buffer, so it will be dropped first |
| 1358 | let regs = T::regs(); | ||
| 1359 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1433 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1360 | 1434 | ||
| 1361 | let transfer = Self::prepare_datapath_read( | 1435 | let transfer = Self::prepare_datapath_read( |
| @@ -1369,27 +1443,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1369 | InterruptHandler::<T>::data_interrupts(true); | 1443 | InterruptHandler::<T>::data_interrupts(true); |
| 1370 | Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | 1444 | Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 |
| 1371 | 1445 | ||
| 1372 | let res = poll_fn(|cx| { | 1446 | let res = Self::complete_datapath_transfer().await; |
| 1373 | T::state().register(cx.waker()); | ||
| 1374 | let status = regs.star().read(); | ||
| 1375 | |||
| 1376 | if status.dcrcfail() { | ||
| 1377 | return Poll::Ready(Err(Error::Crc)); | ||
| 1378 | } | ||
| 1379 | if status.dtimeout() { | ||
| 1380 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1381 | } | ||
| 1382 | #[cfg(sdmmc_v1)] | ||
| 1383 | if status.stbiterr() { | ||
| 1384 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 1385 | } | ||
| 1386 | if status.dataend() { | ||
| 1387 | return Poll::Ready(Ok(())); | ||
| 1388 | } | ||
| 1389 | Poll::Pending | ||
| 1390 | }) | ||
| 1391 | .await; | ||
| 1392 | Self::clear_interrupt_flags(); | ||
| 1393 | 1447 | ||
| 1394 | // Host is allowed to use the new functions at least 8 | 1448 | // Host is allowed to use the new functions at least 8 |
| 1395 | // clocks after the end of the switch command | 1449 | // clocks after the end of the switch command |
| @@ -1436,7 +1490,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1436 | let scr = &mut cmd_block.0[..2]; | 1490 | let scr = &mut cmd_block.0[..2]; |
| 1437 | 1491 | ||
| 1438 | // Arm `OnDrop` after the buffer, so it will be dropped first | 1492 | // Arm `OnDrop` after the buffer, so it will be dropped first |
| 1439 | let regs = T::regs(); | ||
| 1440 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1493 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1441 | 1494 | ||
| 1442 | let transfer = Self::prepare_datapath_read( | 1495 | let transfer = Self::prepare_datapath_read( |
| @@ -1450,27 +1503,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1450 | InterruptHandler::<T>::data_interrupts(true); | 1503 | InterruptHandler::<T>::data_interrupts(true); |
| 1451 | Self::cmd(sd_cmd::send_scr(), true)?; | 1504 | Self::cmd(sd_cmd::send_scr(), true)?; |
| 1452 | 1505 | ||
| 1453 | let res = poll_fn(|cx| { | 1506 | let res = Self::complete_datapath_transfer().await; |
| 1454 | T::state().register(cx.waker()); | ||
| 1455 | let status = regs.star().read(); | ||
| 1456 | |||
| 1457 | if status.dcrcfail() { | ||
| 1458 | return Poll::Ready(Err(Error::Crc)); | ||
| 1459 | } | ||
| 1460 | if status.dtimeout() { | ||
| 1461 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1462 | } | ||
| 1463 | #[cfg(sdmmc_v1)] | ||
| 1464 | if status.stbiterr() { | ||
| 1465 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 1466 | } | ||
| 1467 | if status.dataend() { | ||
| 1468 | return Poll::Ready(Ok(())); | ||
| 1469 | } | ||
| 1470 | Poll::Pending | ||
| 1471 | }) | ||
| 1472 | .await; | ||
| 1473 | Self::clear_interrupt_flags(); | ||
| 1474 | 1507 | ||
| 1475 | if res.is_ok() { | 1508 | if res.is_ok() { |
| 1476 | on_drop.defuse(); | 1509 | on_drop.defuse(); |
| @@ -1503,7 +1536,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1503 | let status = cmd_block; | 1536 | let status = cmd_block; |
| 1504 | 1537 | ||
| 1505 | // Arm `OnDrop` after the buffer, so it will be dropped first | 1538 | // Arm `OnDrop` after the buffer, so it will be dropped first |
| 1506 | let regs = T::regs(); | ||
| 1507 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1539 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1508 | 1540 | ||
| 1509 | let transfer = Self::prepare_datapath_read( | 1541 | let transfer = Self::prepare_datapath_read( |
| @@ -1517,27 +1549,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1517 | InterruptHandler::<T>::data_interrupts(true); | 1549 | InterruptHandler::<T>::data_interrupts(true); |
| 1518 | Self::cmd(sd_cmd::sd_status(), true)?; | 1550 | Self::cmd(sd_cmd::sd_status(), true)?; |
| 1519 | 1551 | ||
| 1520 | let res = poll_fn(|cx| { | 1552 | let res = Self::complete_datapath_transfer().await; |
| 1521 | T::state().register(cx.waker()); | ||
| 1522 | let status = regs.star().read(); | ||
| 1523 | |||
| 1524 | if status.dcrcfail() { | ||
| 1525 | return Poll::Ready(Err(Error::Crc)); | ||
| 1526 | } | ||
| 1527 | if status.dtimeout() { | ||
| 1528 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1529 | } | ||
| 1530 | #[cfg(sdmmc_v1)] | ||
| 1531 | if status.stbiterr() { | ||
| 1532 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 1533 | } | ||
| 1534 | if status.dataend() { | ||
| 1535 | return Poll::Ready(Ok(())); | ||
| 1536 | } | ||
| 1537 | Poll::Pending | ||
| 1538 | }) | ||
| 1539 | .await; | ||
| 1540 | Self::clear_interrupt_flags(); | ||
| 1541 | 1553 | ||
| 1542 | if res.is_ok() { | 1554 | if res.is_ok() { |
| 1543 | on_drop.defuse(); | 1555 | on_drop.defuse(); |
| @@ -1551,128 +1563,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1551 | } | 1563 | } |
| 1552 | res | 1564 | res |
| 1553 | } | 1565 | } |
| 1554 | } | ||
| 1555 | 1566 | ||
| 1556 | /// eMMC only. | ||
| 1557 | impl<'d, T: Instance> Sdmmc<'d, T> { | ||
| 1558 | /// Initializes eMMC and sets the bus at the specified frequency. | 1567 | /// Initializes eMMC and sets the bus at the specified frequency. |
| 1568 | /// | ||
| 1569 | /// eMMC only. | ||
| 1559 | pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { | 1570 | pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { |
| 1560 | let regs = T::regs(); | 1571 | self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await |
| 1561 | let ker_ck = T::frequency(); | ||
| 1562 | |||
| 1563 | let bus_width = match (self.d3.is_some(), self.d7.is_some()) { | ||
| 1564 | (true, true) => BusWidth::Eight, | ||
| 1565 | (true, false) => BusWidth::Four, | ||
| 1566 | _ => BusWidth::One, | ||
| 1567 | }; | ||
| 1568 | |||
| 1569 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 1570 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 1571 | let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); | ||
| 1572 | self.clock = init_clock; | ||
| 1573 | |||
| 1574 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1575 | Self::wait_idle(); | ||
| 1576 | |||
| 1577 | regs.clkcr().modify(|w| { | ||
| 1578 | w.set_widbus(0); | ||
| 1579 | w.set_clkdiv(clkdiv); | ||
| 1580 | #[cfg(sdmmc_v1)] | ||
| 1581 | w.set_bypass(_bypass); | ||
| 1582 | }); | ||
| 1583 | |||
| 1584 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 1585 | Self::cmd(common_cmd::idle(), false)?; | ||
| 1586 | |||
| 1587 | let mut card = Emmc::default(); | ||
| 1588 | |||
| 1589 | let ocr = loop { | ||
| 1590 | let high_voltage = 0b0 << 7; | ||
| 1591 | let access_mode = 0b10 << 29; | ||
| 1592 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 1593 | // Initialize card | ||
| 1594 | match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 1595 | Ok(_) => (), | ||
| 1596 | Err(Error::Crc) => (), | ||
| 1597 | Err(err) => return Err(err), | ||
| 1598 | } | ||
| 1599 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 1600 | if !ocr.is_busy() { | ||
| 1601 | // Power up done | ||
| 1602 | break ocr; | ||
| 1603 | } | ||
| 1604 | }; | ||
| 1605 | |||
| 1606 | card.capacity = if ocr.access_mode() == 0b10 { | ||
| 1607 | // Card is SDHC or SDXC or SDUC | ||
| 1608 | CardCapacity::HighCapacity | ||
| 1609 | } else { | ||
| 1610 | CardCapacity::StandardCapacity | ||
| 1611 | }; | ||
| 1612 | card.ocr = ocr; | ||
| 1613 | |||
| 1614 | Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 | ||
| 1615 | let cid0 = regs.respr(0).read().cardstatus() as u128; | ||
| 1616 | let cid1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1617 | let cid2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1618 | let cid3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1619 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | ||
| 1620 | card.cid = cid.into(); | ||
| 1621 | |||
| 1622 | card.rca = 1u16.into(); | ||
| 1623 | Self::cmd(emmc_cmd::assign_relative_address(card.rca), false)?; | ||
| 1624 | |||
| 1625 | Self::cmd(common_cmd::send_csd(card.rca), false)?; | ||
| 1626 | let csd0 = regs.respr(0).read().cardstatus() as u128; | ||
| 1627 | let csd1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1628 | let csd2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1629 | let csd3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1630 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | ||
| 1631 | card.csd = csd.into(); | ||
| 1632 | |||
| 1633 | self.select_card(Some(card.rca))?; | ||
| 1634 | |||
| 1635 | // Set bus width | ||
| 1636 | let (width, widbus) = match bus_width { | ||
| 1637 | BusWidth::Eight => (BusWidth::Eight, 2), | ||
| 1638 | BusWidth::Four => (BusWidth::Four, 1), | ||
| 1639 | _ => (BusWidth::One, 0), | ||
| 1640 | }; | ||
| 1641 | // Write bus width to ExtCSD byte 183 | ||
| 1642 | Self::cmd( | ||
| 1643 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 1644 | false, | ||
| 1645 | )?; | ||
| 1646 | |||
| 1647 | self.card = Some(SdmmcPeripheral::Emmc(card)); | ||
| 1648 | |||
| 1649 | // Wait for ready after R1b response | ||
| 1650 | loop { | ||
| 1651 | let status = self.read_status::<EMMC>(self.card.as_ref().unwrap())?; | ||
| 1652 | |||
| 1653 | if status.ready_for_data() { | ||
| 1654 | break; | ||
| 1655 | } | ||
| 1656 | } | ||
| 1657 | |||
| 1658 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1659 | Self::wait_idle(); | ||
| 1660 | |||
| 1661 | regs.clkcr().modify(|w| w.set_widbus(widbus)); | ||
| 1662 | |||
| 1663 | // Set Clock | ||
| 1664 | if freq.0 <= 25_000_000 { | ||
| 1665 | // Final clock frequency | ||
| 1666 | self.clkcr_set_clkdiv(freq.0, width)?; | ||
| 1667 | } else { | ||
| 1668 | // Switch to max clock for SDR12 | ||
| 1669 | self.clkcr_set_clkdiv(25_000_000, width)?; | ||
| 1670 | } | ||
| 1671 | |||
| 1672 | // Read status | ||
| 1673 | self.read_ext_csd().await?; | ||
| 1674 | |||
| 1675 | Ok(()) | ||
| 1676 | } | 1572 | } |
| 1677 | 1573 | ||
| 1678 | /// Gets the EXT_CSD register. | 1574 | /// Gets the EXT_CSD register. |
| @@ -1690,7 +1586,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1690 | Self::cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 | 1586 | Self::cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 |
| 1691 | 1587 | ||
| 1692 | // Arm `OnDrop` after the buffer, so it will be dropped first | 1588 | // Arm `OnDrop` after the buffer, so it will be dropped first |
| 1693 | let regs = T::regs(); | ||
| 1694 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1589 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1695 | 1590 | ||
| 1696 | let transfer = Self::prepare_datapath_read( | 1591 | let transfer = Self::prepare_datapath_read( |
| @@ -1704,27 +1599,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { | |||
| 1704 | InterruptHandler::<T>::data_interrupts(true); | 1599 | InterruptHandler::<T>::data_interrupts(true); |
| 1705 | Self::cmd(emmc_cmd::send_ext_csd(), true)?; | 1600 | Self::cmd(emmc_cmd::send_ext_csd(), true)?; |
| 1706 | 1601 | ||
| 1707 | let res = poll_fn(|cx| { | 1602 | let res = Self::complete_datapath_transfer().await; |
| 1708 | T::state().register(cx.waker()); | ||
| 1709 | let status = regs.star().read(); | ||
| 1710 | |||
| 1711 | if status.dcrcfail() { | ||
| 1712 | return Poll::Ready(Err(Error::Crc)); | ||
| 1713 | } | ||
| 1714 | if status.dtimeout() { | ||
| 1715 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1716 | } | ||
| 1717 | #[cfg(sdmmc_v1)] | ||
| 1718 | if status.stbiterr() { | ||
| 1719 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 1720 | } | ||
| 1721 | if status.dataend() { | ||
| 1722 | return Poll::Ready(Ok(())); | ||
| 1723 | } | ||
| 1724 | Poll::Pending | ||
| 1725 | }) | ||
| 1726 | .await; | ||
| 1727 | Self::clear_interrupt_flags(); | ||
| 1728 | 1603 | ||
| 1729 | if res.is_ok() { | 1604 | if res.is_ok() { |
| 1730 | on_drop.defuse(); | 1605 | on_drop.defuse(); |
diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 66e4e527c..e97b63925 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs | |||
| @@ -59,7 +59,7 @@ async fn main(_spawner: Spawner) { | |||
| 59 | 59 | ||
| 60 | let mut err = None; | 60 | let mut err = None; |
| 61 | loop { | 61 | loop { |
| 62 | match sdmmc.init_card(mhz(24)).await { | 62 | match sdmmc.init_sd_card(mhz(24)).await { |
| 63 | Ok(_) => break, | 63 | Ok(_) => break, |
| 64 | Err(e) => { | 64 | Err(e) => { |
| 65 | if err != Some(e) { | 65 | if err != Some(e) { |
diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 6d36ef518..787bef25e 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs | |||
| @@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) { | |||
| 54 | // Should print 400kHz for initialization | 54 | // Should print 400kHz for initialization |
| 55 | info!("Configured clock: {}", sdmmc.clock().0); | 55 | info!("Configured clock: {}", sdmmc.clock().0); |
| 56 | 56 | ||
| 57 | unwrap!(sdmmc.init_card(mhz(25)).await); | 57 | unwrap!(sdmmc.init_sd_card(mhz(25)).await); |
| 58 | 58 | ||
| 59 | let card = unwrap!(sdmmc.card()); | 59 | let card = unwrap!(sdmmc.card()); |
| 60 | 60 | ||
diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index abe2d4ba7..96840d8ff 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs | |||
| @@ -53,7 +53,7 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 53 | // Should print 400kHz for initialization | 53 | // Should print 400kHz for initialization |
| 54 | info!("Configured clock: {}", sdmmc.clock().0); | 54 | info!("Configured clock: {}", sdmmc.clock().0); |
| 55 | 55 | ||
| 56 | unwrap!(sdmmc.init_card(mhz(25)).await); | 56 | unwrap!(sdmmc.init_sd_card(mhz(25)).await); |
| 57 | 57 | ||
| 58 | let card = unwrap!(sdmmc.card()); | 58 | let card = unwrap!(sdmmc.card()); |
| 59 | 59 | ||
diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index 07f17b569..c1ed45588 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs | |||
| @@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) { | |||
| 54 | 54 | ||
| 55 | let mut err = None; | 55 | let mut err = None; |
| 56 | loop { | 56 | loop { |
| 57 | match s.init_card(mhz(24)).await { | 57 | match s.init_sd_card(mhz(24)).await { |
| 58 | Ok(_) => break, | 58 | Ok(_) => break, |
| 59 | Err(e) => { | 59 | Err(e) => { |
| 60 | if err != Some(e) { | 60 | if err != Some(e) { |
| @@ -100,7 +100,7 @@ async fn main(_spawner: Spawner) { | |||
| 100 | 100 | ||
| 101 | let mut err = None; | 101 | let mut err = None; |
| 102 | loop { | 102 | loop { |
| 103 | match s.init_card(mhz(24)).await { | 103 | match s.init_sd_card(mhz(24)).await { |
| 104 | Ok(_) => break, | 104 | Ok(_) => break, |
| 105 | Err(e) => { | 105 | Err(e) => { |
| 106 | if err != Some(e) { | 106 | if err != Some(e) { |
