mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-10-02 14:44:32 +00:00
scaffold eMMC support
This commit is contained in:
parent
14bb4ee9e4
commit
5325f1d911
@ -10,11 +10,10 @@ use core::task::Poll;
|
|||||||
use embassy_hal_internal::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_internal::{Peri, PeripheralType};
|
use embassy_hal_internal::{Peri, PeripheralType};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use sdio_host::{
|
use sdio_host::common_cmd::{self, Resp, ResponseLen};
|
||||||
common_cmd::{self, Resp, ResponseLen},
|
use sdio_host::emmc::{ExtCSD, EMMC};
|
||||||
sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD},
|
use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD};
|
||||||
sd_cmd, Cmd,
|
use sdio_host::{sd_cmd, Cmd};
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(sdmmc_v1)]
|
#[cfg(sdmmc_v1)]
|
||||||
use crate::dma::ChannelAndRequest;
|
use crate::dma::ChannelAndRequest;
|
||||||
@ -174,12 +173,21 @@ pub struct Card {
|
|||||||
pub status: SDStatus,
|
pub status: SDStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Card {
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
/// Size in bytes
|
/// eMMC storage
|
||||||
pub fn size(&self) -> u64 {
|
pub struct Emmc {
|
||||||
// SDHC / SDXC / SDUC
|
/// The capacity of this card
|
||||||
u64::from(self.csd.block_count()) * 512
|
pub capacity: CardCapacity,
|
||||||
}
|
/// Operation Conditions Register
|
||||||
|
pub ocr: OCR<EMMC>,
|
||||||
|
/// Relative Card Address
|
||||||
|
pub rca: u16,
|
||||||
|
/// Card ID
|
||||||
|
pub cid: CID<EMMC>,
|
||||||
|
/// Card Specific Data
|
||||||
|
pub csd: CSD<EMMC>,
|
||||||
|
/// Extended Card Specific Data
|
||||||
|
pub ext_csd: ExtCSD,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@ -290,6 +298,61 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Peripheral that can be operated over SDMMC
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum SdmmcPeripheral {
|
||||||
|
/// SD Card
|
||||||
|
SdCard(Card),
|
||||||
|
/// eMMC memory
|
||||||
|
Emmc(Emmc),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdmmcPeripheral {
|
||||||
|
/// Get this peripheral's address on the SDMMC bus
|
||||||
|
fn get_address(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Self::SdCard(c) => c.rca,
|
||||||
|
Self::Emmc(e) => e.rca,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Is this a standard or high capacity peripheral?
|
||||||
|
fn get_capacity(&self) -> CardCapacity {
|
||||||
|
match self {
|
||||||
|
Self::SdCard(c) => c.card_type,
|
||||||
|
Self::Emmc(e) => e.capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Size in bytes
|
||||||
|
fn size(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
// SDHC / SDXC / SDUC
|
||||||
|
Self::SdCard(c) => u64::from(c.csd.block_count()) * 512,
|
||||||
|
// capacity > 2GB
|
||||||
|
Self::Emmc(e) => u64::from(e.ext_csd.sector_count()) * 512,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the SD Card.
|
||||||
|
///
|
||||||
|
/// Panics if there is another peripheral instead.
|
||||||
|
fn get_sd_card(&mut self) -> &mut Card {
|
||||||
|
match *self {
|
||||||
|
Self::SdCard(ref mut c) => c,
|
||||||
|
_ => unreachable!("SD only"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the eMMC.
|
||||||
|
///
|
||||||
|
/// Panics if there is another peripheral instead.
|
||||||
|
fn get_emmc(&mut self) -> &mut Emmc {
|
||||||
|
match *self {
|
||||||
|
Self::Emmc(ref mut e) => e,
|
||||||
|
_ => unreachable!("eMMC only"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sdmmc device
|
/// Sdmmc device
|
||||||
pub struct Sdmmc<'d, T: Instance> {
|
pub struct Sdmmc<'d, T: Instance> {
|
||||||
_peri: Peri<'d, T>,
|
_peri: Peri<'d, T>,
|
||||||
@ -309,7 +372,7 @@ pub struct Sdmmc<'d, T: Instance> {
|
|||||||
/// Current signalling scheme to card
|
/// Current signalling scheme to card
|
||||||
signalling: Signalling,
|
signalling: Signalling,
|
||||||
/// Card
|
/// Card
|
||||||
card: Option<Card>,
|
card: Option<SdmmcPeripheral>,
|
||||||
|
|
||||||
/// An optional buffer to be used for commands
|
/// An optional buffer to be used for commands
|
||||||
/// This should be used if there are special memory location requirements for dma
|
/// This should be used if there are special memory location requirements for dma
|
||||||
@ -662,101 +725,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Switch mode using CMD6.
|
|
||||||
///
|
|
||||||
/// Attempt to set a new signalling mode. The selected
|
|
||||||
/// signalling mode is returned. Expects the current clock
|
|
||||||
/// frequency to be > 12.5MHz.
|
|
||||||
async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result<Signalling, Error> {
|
|
||||||
// NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not
|
|
||||||
// necessary"
|
|
||||||
|
|
||||||
let set_function = 0x8000_0000
|
|
||||||
| match signalling {
|
|
||||||
// See PLSS v7_10 Table 4-11
|
|
||||||
Signalling::DDR50 => 0xFF_FF04,
|
|
||||||
Signalling::SDR104 => 0xFF_1F03,
|
|
||||||
Signalling::SDR50 => 0xFF_1F02,
|
|
||||||
Signalling::SDR25 => 0xFF_FF01,
|
|
||||||
Signalling::SDR12 => 0xFF_FF00,
|
|
||||||
};
|
|
||||||
|
|
||||||
let status = match self.cmd_block.as_deref_mut() {
|
|
||||||
Some(x) => x,
|
|
||||||
None => &mut CmdBlock::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Arm `OnDrop` after the buffer, so it will be dropped first
|
|
||||||
let regs = T::regs();
|
|
||||||
let on_drop = OnDrop::new(|| Self::on_drop());
|
|
||||||
|
|
||||||
let transfer = Self::prepare_datapath_read(
|
|
||||||
&self.config,
|
|
||||||
#[cfg(sdmmc_v1)]
|
|
||||||
&mut self.dma,
|
|
||||||
status.as_mut(),
|
|
||||||
64,
|
|
||||||
6,
|
|
||||||
);
|
|
||||||
InterruptHandler::<T>::data_interrupts(true);
|
|
||||||
Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6
|
|
||||||
|
|
||||||
let res = poll_fn(|cx| {
|
|
||||||
T::state().register(cx.waker());
|
|
||||||
let status = regs.star().read();
|
|
||||||
|
|
||||||
if status.dcrcfail() {
|
|
||||||
return Poll::Ready(Err(Error::Crc));
|
|
||||||
}
|
|
||||||
if status.dtimeout() {
|
|
||||||
return Poll::Ready(Err(Error::Timeout));
|
|
||||||
}
|
|
||||||
#[cfg(sdmmc_v1)]
|
|
||||||
if status.stbiterr() {
|
|
||||||
return Poll::Ready(Err(Error::StBitErr));
|
|
||||||
}
|
|
||||||
if status.dataend() {
|
|
||||||
return Poll::Ready(Ok(()));
|
|
||||||
}
|
|
||||||
Poll::Pending
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
Self::clear_interrupt_flags();
|
|
||||||
|
|
||||||
// Host is allowed to use the new functions at least 8
|
|
||||||
// clocks after the end of the switch command
|
|
||||||
// transaction. We know the current clock period is < 80ns,
|
|
||||||
// so a total delay of 640ns is required here
|
|
||||||
for _ in 0..300 {
|
|
||||||
cortex_m::asm::nop();
|
|
||||||
}
|
|
||||||
|
|
||||||
match res {
|
|
||||||
Ok(_) => {
|
|
||||||
on_drop.defuse();
|
|
||||||
Self::stop_datapath();
|
|
||||||
drop(transfer);
|
|
||||||
|
|
||||||
// Function Selection of Function Group 1
|
|
||||||
let selection = (u32::from_be(status[4]) >> 24) & 0xF;
|
|
||||||
|
|
||||||
match selection {
|
|
||||||
0 => Ok(Signalling::SDR12),
|
|
||||||
1 => Ok(Signalling::SDR25),
|
|
||||||
2 => Ok(Signalling::SDR50),
|
|
||||||
3 => Ok(Signalling::SDR104),
|
|
||||||
4 => Ok(Signalling::DDR50),
|
|
||||||
_ => Err(Error::UnsupportedCardType),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Query the card status (CMD13, returns R1)
|
/// Query the card status (CMD13, returns R1)
|
||||||
fn read_status(&self, card: &Card) -> Result<CardStatus<SD>, Error> {
|
fn read_status<Ext>(&self, card: &SdmmcPeripheral) -> Result<CardStatus<Ext>, Error>
|
||||||
|
where
|
||||||
|
CardStatus<Ext>: From<u32>,
|
||||||
|
{
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
let rca = card.rca;
|
let rca = card.get_address();
|
||||||
|
|
||||||
Self::cmd(common_cmd::card_status(rca, false), false)?; // CMD13
|
Self::cmd(common_cmd::card_status(rca, false), false)?; // CMD13
|
||||||
|
|
||||||
@ -764,78 +739,13 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
Ok(r1.into())
|
Ok(r1.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the SD Status (ACMD13)
|
|
||||||
async fn read_sd_status(&mut self) -> Result<(), Error> {
|
|
||||||
let card = self.card.as_mut().ok_or(Error::NoCard)?;
|
|
||||||
let rca = card.rca;
|
|
||||||
|
|
||||||
let cmd_block = match self.cmd_block.as_deref_mut() {
|
|
||||||
Some(x) => x,
|
|
||||||
None => &mut CmdBlock::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16
|
|
||||||
Self::cmd(common_cmd::app_cmd(rca), false)?; // APP
|
|
||||||
|
|
||||||
let status = cmd_block;
|
|
||||||
|
|
||||||
// Arm `OnDrop` after the buffer, so it will be dropped first
|
|
||||||
let regs = T::regs();
|
|
||||||
let on_drop = OnDrop::new(|| Self::on_drop());
|
|
||||||
|
|
||||||
let transfer = Self::prepare_datapath_read(
|
|
||||||
&self.config,
|
|
||||||
#[cfg(sdmmc_v1)]
|
|
||||||
&mut self.dma,
|
|
||||||
status.as_mut(),
|
|
||||||
64,
|
|
||||||
6,
|
|
||||||
);
|
|
||||||
InterruptHandler::<T>::data_interrupts(true);
|
|
||||||
Self::cmd(sd_cmd::sd_status(), true)?;
|
|
||||||
|
|
||||||
let res = poll_fn(|cx| {
|
|
||||||
T::state().register(cx.waker());
|
|
||||||
let status = regs.star().read();
|
|
||||||
|
|
||||||
if status.dcrcfail() {
|
|
||||||
return Poll::Ready(Err(Error::Crc));
|
|
||||||
}
|
|
||||||
if status.dtimeout() {
|
|
||||||
return Poll::Ready(Err(Error::Timeout));
|
|
||||||
}
|
|
||||||
#[cfg(sdmmc_v1)]
|
|
||||||
if status.stbiterr() {
|
|
||||||
return Poll::Ready(Err(Error::StBitErr));
|
|
||||||
}
|
|
||||||
if status.dataend() {
|
|
||||||
return Poll::Ready(Ok(()));
|
|
||||||
}
|
|
||||||
Poll::Pending
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
Self::clear_interrupt_flags();
|
|
||||||
|
|
||||||
if res.is_ok() {
|
|
||||||
on_drop.defuse();
|
|
||||||
Self::stop_datapath();
|
|
||||||
drop(transfer);
|
|
||||||
|
|
||||||
for byte in status.iter_mut() {
|
|
||||||
*byte = u32::from_be(*byte);
|
|
||||||
}
|
|
||||||
self.card.as_mut().unwrap().status = status.0.into();
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select one card and place it into the _Tranfer State_
|
/// Select one card and place it into the _Tranfer State_
|
||||||
///
|
///
|
||||||
/// If `None` is specifed for `card`, all cards are put back into
|
/// If `None` is specifed for `card`, all cards are put back into
|
||||||
/// _Stand-by State_
|
/// _Stand-by State_
|
||||||
fn select_card(&self, card: Option<&Card>) -> Result<(), Error> {
|
fn select_card(&self, rca: Option<u16>) -> Result<(), Error> {
|
||||||
// Determine Relative Card Address (RCA) of given card
|
// Determine Relative Card Address (RCA) of given card
|
||||||
let rca = card.map(|c| c.rca).unwrap_or(0);
|
let rca = rca.unwrap_or(0);
|
||||||
|
|
||||||
let r = Self::cmd(common_cmd::select_card(rca), false);
|
let r = Self::cmd(common_cmd::select_card(rca), false);
|
||||||
match (r, rca) {
|
match (r, rca) {
|
||||||
@ -878,67 +788,6 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> {
|
|
||||||
// Read the 64-bit SCR register
|
|
||||||
Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16
|
|
||||||
Self::cmd(common_cmd::app_cmd(card.rca), false)?;
|
|
||||||
|
|
||||||
let cmd_block = match self.cmd_block.as_deref_mut() {
|
|
||||||
Some(x) => x,
|
|
||||||
None => &mut CmdBlock::new(),
|
|
||||||
};
|
|
||||||
let scr = &mut cmd_block.0[..2];
|
|
||||||
|
|
||||||
// Arm `OnDrop` after the buffer, so it will be dropped first
|
|
||||||
let regs = T::regs();
|
|
||||||
let on_drop = OnDrop::new(|| Self::on_drop());
|
|
||||||
|
|
||||||
let transfer = Self::prepare_datapath_read(
|
|
||||||
&self.config,
|
|
||||||
#[cfg(sdmmc_v1)]
|
|
||||||
&mut self.dma,
|
|
||||||
scr,
|
|
||||||
8,
|
|
||||||
3,
|
|
||||||
);
|
|
||||||
InterruptHandler::<T>::data_interrupts(true);
|
|
||||||
Self::cmd(sd_cmd::send_scr(), true)?;
|
|
||||||
|
|
||||||
let res = poll_fn(|cx| {
|
|
||||||
T::state().register(cx.waker());
|
|
||||||
let status = regs.star().read();
|
|
||||||
|
|
||||||
if status.dcrcfail() {
|
|
||||||
return Poll::Ready(Err(Error::Crc));
|
|
||||||
}
|
|
||||||
if status.dtimeout() {
|
|
||||||
return Poll::Ready(Err(Error::Timeout));
|
|
||||||
}
|
|
||||||
#[cfg(sdmmc_v1)]
|
|
||||||
if status.stbiterr() {
|
|
||||||
return Poll::Ready(Err(Error::StBitErr));
|
|
||||||
}
|
|
||||||
if status.dataend() {
|
|
||||||
return Poll::Ready(Ok(()));
|
|
||||||
}
|
|
||||||
Poll::Pending
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
Self::clear_interrupt_flags();
|
|
||||||
|
|
||||||
if res.is_ok() {
|
|
||||||
on_drop.defuse();
|
|
||||||
Self::stop_datapath();
|
|
||||||
drop(transfer);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let scr_bytes = &*(&scr as *const _ as *const [u8; 8]);
|
|
||||||
card.scr = SCR(u64::from_be_bytes(*scr_bytes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send command to card
|
/// Send command to card
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn cmd<R: Resp>(cmd: Cmd<R>, data: bool) -> Result<(), Error> {
|
fn cmd<R: Resp>(cmd: Cmd<R>, data: bool) -> Result<(), Error> {
|
||||||
@ -1024,6 +873,170 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
Self::stop_datapath();
|
Self::stop_datapath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a data block.
|
||||||
|
#[inline]
|
||||||
|
pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> {
|
||||||
|
let card_capacity = self.card()?.get_capacity();
|
||||||
|
|
||||||
|
// NOTE(unsafe) DataBlock uses align 4
|
||||||
|
let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) };
|
||||||
|
|
||||||
|
// Always read 1 block of 512 bytes
|
||||||
|
// SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
|
||||||
|
let address = match card_capacity {
|
||||||
|
CardCapacity::StandardCapacity => block_idx * 512,
|
||||||
|
_ => block_idx,
|
||||||
|
};
|
||||||
|
Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16
|
||||||
|
|
||||||
|
let regs = T::regs();
|
||||||
|
let on_drop = OnDrop::new(|| Self::on_drop());
|
||||||
|
|
||||||
|
let transfer = Self::prepare_datapath_read(
|
||||||
|
&self.config,
|
||||||
|
#[cfg(sdmmc_v1)]
|
||||||
|
&mut self.dma,
|
||||||
|
buffer,
|
||||||
|
512,
|
||||||
|
9,
|
||||||
|
);
|
||||||
|
InterruptHandler::<T>::data_interrupts(true);
|
||||||
|
Self::cmd(common_cmd::read_single_block(address), true)?;
|
||||||
|
|
||||||
|
let res = poll_fn(|cx| {
|
||||||
|
T::state().register(cx.waker());
|
||||||
|
let status = regs.star().read();
|
||||||
|
|
||||||
|
if status.dcrcfail() {
|
||||||
|
return Poll::Ready(Err(Error::Crc));
|
||||||
|
}
|
||||||
|
if status.dtimeout() {
|
||||||
|
return Poll::Ready(Err(Error::Timeout));
|
||||||
|
}
|
||||||
|
#[cfg(sdmmc_v1)]
|
||||||
|
if status.stbiterr() {
|
||||||
|
return Poll::Ready(Err(Error::StBitErr));
|
||||||
|
}
|
||||||
|
if status.dataend() {
|
||||||
|
return Poll::Ready(Ok(()));
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
Self::clear_interrupt_flags();
|
||||||
|
|
||||||
|
if res.is_ok() {
|
||||||
|
on_drop.defuse();
|
||||||
|
Self::stop_datapath();
|
||||||
|
drop(transfer);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a data block.
|
||||||
|
pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
|
||||||
|
let card = self.card.as_mut().ok_or(Error::NoCard)?;
|
||||||
|
|
||||||
|
// NOTE(unsafe) DataBlock uses align 4
|
||||||
|
let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) };
|
||||||
|
|
||||||
|
// Always read 1 block of 512 bytes
|
||||||
|
// SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
|
||||||
|
let address = match card.get_capacity() {
|
||||||
|
CardCapacity::StandardCapacity => block_idx * 512,
|
||||||
|
_ => block_idx,
|
||||||
|
};
|
||||||
|
Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16
|
||||||
|
|
||||||
|
let regs = T::regs();
|
||||||
|
let on_drop = OnDrop::new(|| Self::on_drop());
|
||||||
|
|
||||||
|
// sdmmc_v1 uses different cmd/dma order than v2, but only for writes
|
||||||
|
#[cfg(sdmmc_v1)]
|
||||||
|
Self::cmd(common_cmd::write_single_block(address), true)?;
|
||||||
|
|
||||||
|
let transfer = self.prepare_datapath_write(buffer, 512, 9);
|
||||||
|
InterruptHandler::<T>::data_interrupts(true);
|
||||||
|
|
||||||
|
#[cfg(sdmmc_v2)]
|
||||||
|
Self::cmd(common_cmd::write_single_block(address), true)?;
|
||||||
|
|
||||||
|
let res = poll_fn(|cx| {
|
||||||
|
T::state().register(cx.waker());
|
||||||
|
let status = regs.star().read();
|
||||||
|
|
||||||
|
if status.dcrcfail() {
|
||||||
|
return Poll::Ready(Err(Error::Crc));
|
||||||
|
}
|
||||||
|
if status.dtimeout() {
|
||||||
|
return Poll::Ready(Err(Error::Timeout));
|
||||||
|
}
|
||||||
|
#[cfg(sdmmc_v1)]
|
||||||
|
if status.stbiterr() {
|
||||||
|
return Poll::Ready(Err(Error::StBitErr));
|
||||||
|
}
|
||||||
|
if status.dataend() {
|
||||||
|
return Poll::Ready(Ok(()));
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
Self::clear_interrupt_flags();
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(_) => {
|
||||||
|
on_drop.defuse();
|
||||||
|
Self::stop_datapath();
|
||||||
|
drop(transfer);
|
||||||
|
|
||||||
|
// TODO: Make this configurable
|
||||||
|
let mut timeout: u32 = 0x00FF_FFFF;
|
||||||
|
|
||||||
|
let card = self.card.as_ref().unwrap();
|
||||||
|
while timeout > 0 {
|
||||||
|
let ready_for_data = match card {
|
||||||
|
SdmmcPeripheral::Emmc(_) => self.read_status::<EMMC>(card)?.ready_for_data(),
|
||||||
|
SdmmcPeripheral::SdCard(_) => self.read_status::<SD>(card)?.ready_for_data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if ready_for_data {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
timeout -= 1;
|
||||||
|
}
|
||||||
|
Err(Error::SoftwareTimeout)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the initialized card
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns Error::NoCard if [`init_card`](#method.init_card)
|
||||||
|
/// has not previously succeeded
|
||||||
|
#[inline]
|
||||||
|
pub fn card(&self) -> Result<&SdmmcPeripheral, Error> {
|
||||||
|
self.card.as_ref().ok_or(Error::NoCard)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current SDMMC bus clock
|
||||||
|
pub fn clock(&self) -> Hertz {
|
||||||
|
self.clock
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a specific cmd buffer rather than using the default stack allocated one.
|
||||||
|
/// This is required if stack RAM cannot be used with DMA and usually manifests
|
||||||
|
/// itself as an indefinite wait on a dma transfer because the dma peripheral
|
||||||
|
/// cannot access the memory.
|
||||||
|
pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) {
|
||||||
|
self.cmd_block = Some(cmd_block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SD only
|
||||||
|
impl<'d, T: Instance> Sdmmc<'d, T> {
|
||||||
/// Initializes card (if present) and sets the bus at the specified frequency.
|
/// Initializes card (if present) and sets the bus at the specified frequency.
|
||||||
pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> {
|
pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> {
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
@ -1114,7 +1127,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3);
|
let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3);
|
||||||
card.csd = csd.into();
|
card.csd = csd.into();
|
||||||
|
|
||||||
self.select_card(Some(&card))?;
|
self.select_card(Some(card.rca))?;
|
||||||
|
|
||||||
self.get_scr(&mut card).await?;
|
self.get_scr(&mut card).await?;
|
||||||
|
|
||||||
@ -1148,7 +1161,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
self.clkcr_set_clkdiv(25_000_000, width)?;
|
self.clkcr_set_clkdiv(25_000_000, width)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.card = Some(card);
|
self.card = Some(SdmmcPeripheral::SdCard(card));
|
||||||
|
|
||||||
// Read status
|
// Read status
|
||||||
self.read_sd_status().await?;
|
self.read_sd_status().await?;
|
||||||
@ -1161,7 +1174,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
// Set final clock frequency
|
// Set final clock frequency
|
||||||
self.clkcr_set_clkdiv(freq.0, width)?;
|
self.clkcr_set_clkdiv(freq.0, width)?;
|
||||||
|
|
||||||
if self.read_status(&card)?.state() != CurrentState::Transfer {
|
if self.read_status::<SD>(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer {
|
||||||
return Err(Error::SignalingSwitchFailed);
|
return Err(Error::SignalingSwitchFailed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1173,22 +1186,34 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a data block.
|
/// Switch mode using CMD6.
|
||||||
#[inline]
|
///
|
||||||
pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> {
|
/// Attempt to set a new signalling mode. The selected
|
||||||
let card_capacity = self.card()?.card_type;
|
/// signalling mode is returned. Expects the current clock
|
||||||
|
/// frequency to be > 12.5MHz.
|
||||||
|
///
|
||||||
|
/// SD only.
|
||||||
|
async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result<Signalling, Error> {
|
||||||
|
let _ = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card();
|
||||||
|
// NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not
|
||||||
|
// necessary"
|
||||||
|
|
||||||
// NOTE(unsafe) DataBlock uses align 4
|
let set_function = 0x8000_0000
|
||||||
let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) };
|
| match signalling {
|
||||||
|
// See PLSS v7_10 Table 4-11
|
||||||
|
Signalling::DDR50 => 0xFF_FF04,
|
||||||
|
Signalling::SDR104 => 0xFF_1F03,
|
||||||
|
Signalling::SDR50 => 0xFF_1F02,
|
||||||
|
Signalling::SDR25 => 0xFF_FF01,
|
||||||
|
Signalling::SDR12 => 0xFF_FF00,
|
||||||
|
};
|
||||||
|
|
||||||
// Always read 1 block of 512 bytes
|
let status = match self.cmd_block.as_deref_mut() {
|
||||||
// SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
|
Some(x) => x,
|
||||||
let address = match card_capacity {
|
None => &mut CmdBlock::new(),
|
||||||
CardCapacity::StandardCapacity => block_idx * 512,
|
|
||||||
_ => block_idx,
|
|
||||||
};
|
};
|
||||||
Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16
|
|
||||||
|
|
||||||
|
// Arm `OnDrop` after the buffer, so it will be dropped first
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
let on_drop = OnDrop::new(|| Self::on_drop());
|
let on_drop = OnDrop::new(|| Self::on_drop());
|
||||||
|
|
||||||
@ -1196,12 +1221,93 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
&self.config,
|
&self.config,
|
||||||
#[cfg(sdmmc_v1)]
|
#[cfg(sdmmc_v1)]
|
||||||
&mut self.dma,
|
&mut self.dma,
|
||||||
buffer,
|
status.as_mut(),
|
||||||
512,
|
64,
|
||||||
9,
|
6,
|
||||||
);
|
);
|
||||||
InterruptHandler::<T>::data_interrupts(true);
|
InterruptHandler::<T>::data_interrupts(true);
|
||||||
Self::cmd(common_cmd::read_single_block(address), true)?;
|
Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6
|
||||||
|
|
||||||
|
let res = poll_fn(|cx| {
|
||||||
|
T::state().register(cx.waker());
|
||||||
|
let status = regs.star().read();
|
||||||
|
|
||||||
|
if status.dcrcfail() {
|
||||||
|
return Poll::Ready(Err(Error::Crc));
|
||||||
|
}
|
||||||
|
if status.dtimeout() {
|
||||||
|
return Poll::Ready(Err(Error::Timeout));
|
||||||
|
}
|
||||||
|
#[cfg(sdmmc_v1)]
|
||||||
|
if status.stbiterr() {
|
||||||
|
return Poll::Ready(Err(Error::StBitErr));
|
||||||
|
}
|
||||||
|
if status.dataend() {
|
||||||
|
return Poll::Ready(Ok(()));
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
Self::clear_interrupt_flags();
|
||||||
|
|
||||||
|
// Host is allowed to use the new functions at least 8
|
||||||
|
// clocks after the end of the switch command
|
||||||
|
// transaction. We know the current clock period is < 80ns,
|
||||||
|
// so a total delay of 640ns is required here
|
||||||
|
for _ in 0..300 {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(_) => {
|
||||||
|
on_drop.defuse();
|
||||||
|
Self::stop_datapath();
|
||||||
|
drop(transfer);
|
||||||
|
|
||||||
|
// Function Selection of Function Group 1
|
||||||
|
let selection = (u32::from_be(status[4]) >> 24) & 0xF;
|
||||||
|
|
||||||
|
match selection {
|
||||||
|
0 => Ok(Signalling::SDR12),
|
||||||
|
1 => Ok(Signalling::SDR25),
|
||||||
|
2 => Ok(Signalling::SDR50),
|
||||||
|
3 => Ok(Signalling::SDR104),
|
||||||
|
4 => Ok(Signalling::DDR50),
|
||||||
|
_ => Err(Error::UnsupportedCardType),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the SCR register.
|
||||||
|
///
|
||||||
|
/// SD only.
|
||||||
|
async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> {
|
||||||
|
// Read the 64-bit SCR register
|
||||||
|
Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16
|
||||||
|
Self::cmd(common_cmd::app_cmd(card.rca), false)?;
|
||||||
|
|
||||||
|
let cmd_block = match self.cmd_block.as_deref_mut() {
|
||||||
|
Some(x) => x,
|
||||||
|
None => &mut CmdBlock::new(),
|
||||||
|
};
|
||||||
|
let scr = &mut cmd_block.0[..2];
|
||||||
|
|
||||||
|
// Arm `OnDrop` after the buffer, so it will be dropped first
|
||||||
|
let regs = T::regs();
|
||||||
|
let on_drop = OnDrop::new(|| Self::on_drop());
|
||||||
|
|
||||||
|
let transfer = Self::prepare_datapath_read(
|
||||||
|
&self.config,
|
||||||
|
#[cfg(sdmmc_v1)]
|
||||||
|
&mut self.dma,
|
||||||
|
scr,
|
||||||
|
8,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
InterruptHandler::<T>::data_interrupts(true);
|
||||||
|
Self::cmd(sd_cmd::send_scr(), true)?;
|
||||||
|
|
||||||
let res = poll_fn(|cx| {
|
let res = poll_fn(|cx| {
|
||||||
T::state().register(cx.waker());
|
T::state().register(cx.waker());
|
||||||
@ -1229,37 +1335,46 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
on_drop.defuse();
|
on_drop.defuse();
|
||||||
Self::stop_datapath();
|
Self::stop_datapath();
|
||||||
drop(transfer);
|
drop(transfer);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let scr_bytes = &*(&scr as *const _ as *const [u8; 8]);
|
||||||
|
card.scr = SCR(u64::from_be_bytes(*scr_bytes));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a data block.
|
/// Reads the SD Status (ACMD13)
|
||||||
pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {
|
///
|
||||||
let card = self.card.as_mut().ok_or(Error::NoCard)?;
|
/// SD only.
|
||||||
|
async fn read_sd_status(&mut self) -> Result<(), Error> {
|
||||||
|
let card = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card();
|
||||||
|
let rca = card.rca;
|
||||||
|
|
||||||
// NOTE(unsafe) DataBlock uses align 4
|
let cmd_block = match self.cmd_block.as_deref_mut() {
|
||||||
let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) };
|
Some(x) => x,
|
||||||
|
None => &mut CmdBlock::new(),
|
||||||
// Always read 1 block of 512 bytes
|
|
||||||
// SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes
|
|
||||||
let address = match card.card_type {
|
|
||||||
CardCapacity::StandardCapacity => block_idx * 512,
|
|
||||||
_ => block_idx,
|
|
||||||
};
|
};
|
||||||
Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16
|
|
||||||
|
|
||||||
|
Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16
|
||||||
|
Self::cmd(common_cmd::app_cmd(rca), false)?; // APP
|
||||||
|
|
||||||
|
let status = cmd_block;
|
||||||
|
|
||||||
|
// Arm `OnDrop` after the buffer, so it will be dropped first
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
let on_drop = OnDrop::new(|| Self::on_drop());
|
let on_drop = OnDrop::new(|| Self::on_drop());
|
||||||
|
|
||||||
// sdmmc_v1 uses different cmd/dma order than v2, but only for writes
|
let transfer = Self::prepare_datapath_read(
|
||||||
#[cfg(sdmmc_v1)]
|
&self.config,
|
||||||
Self::cmd(common_cmd::write_single_block(address), true)?;
|
#[cfg(sdmmc_v1)]
|
||||||
|
&mut self.dma,
|
||||||
let transfer = self.prepare_datapath_write(buffer, 512, 9);
|
status.as_mut(),
|
||||||
|
64,
|
||||||
|
6,
|
||||||
|
);
|
||||||
InterruptHandler::<T>::data_interrupts(true);
|
InterruptHandler::<T>::data_interrupts(true);
|
||||||
|
Self::cmd(sd_cmd::sd_status(), true)?;
|
||||||
#[cfg(sdmmc_v2)]
|
|
||||||
Self::cmd(common_cmd::write_single_block(address), true)?;
|
|
||||||
|
|
||||||
let res = poll_fn(|cx| {
|
let res = poll_fn(|cx| {
|
||||||
T::state().register(cx.waker());
|
T::state().register(cx.waker());
|
||||||
@ -1283,52 +1398,17 @@ impl<'d, T: Instance> Sdmmc<'d, T> {
|
|||||||
.await;
|
.await;
|
||||||
Self::clear_interrupt_flags();
|
Self::clear_interrupt_flags();
|
||||||
|
|
||||||
match res {
|
if res.is_ok() {
|
||||||
Ok(_) => {
|
on_drop.defuse();
|
||||||
on_drop.defuse();
|
Self::stop_datapath();
|
||||||
Self::stop_datapath();
|
drop(transfer);
|
||||||
drop(transfer);
|
|
||||||
|
|
||||||
// TODO: Make this configurable
|
for byte in status.iter_mut() {
|
||||||
let mut timeout: u32 = 0x00FF_FFFF;
|
*byte = u32::from_be(*byte);
|
||||||
|
|
||||||
let card = self.card.as_ref().unwrap();
|
|
||||||
while timeout > 0 {
|
|
||||||
let status = self.read_status(card)?;
|
|
||||||
|
|
||||||
if status.ready_for_data() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
timeout -= 1;
|
|
||||||
}
|
|
||||||
Err(Error::SoftwareTimeout)
|
|
||||||
}
|
}
|
||||||
Err(e) => Err(e),
|
card.status = status.0.into();
|
||||||
}
|
}
|
||||||
}
|
res
|
||||||
|
|
||||||
/// Get a reference to the initialized card
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns Error::NoCard if [`init_card`](#method.init_card)
|
|
||||||
/// has not previously succeeded
|
|
||||||
#[inline]
|
|
||||||
pub fn card(&self) -> Result<&Card, Error> {
|
|
||||||
self.card.as_ref().ok_or(Error::NoCard)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current SDMMC bus clock
|
|
||||||
pub fn clock(&self) -> Hertz {
|
|
||||||
self.clock
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a specific cmd buffer rather than using the default stack allocated one.
|
|
||||||
/// This is required if stack RAM cannot be used with DMA and usually manifests
|
|
||||||
/// itself as an indefinite wait on a dma transfer because the dma peripheral
|
|
||||||
/// cannot access the memory.
|
|
||||||
pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) {
|
|
||||||
self.cmd_block = Some(cmd_block)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user