use core::cell::RefCell;
use core::future::poll_fn;
use core::task::{Poll, Waker};
use critical_section::Mutex;
use super::{
checked_block_count, wait_for, Block, Error, Sd, CMD_READ_MULTI, CMD_READ_SINGLE,
CMD_WRITE_MULTI, CMD_WRITE_SINGLE, DATA_REG_BUS_ADDRESS, DMA_DREQ_EMMC, INT_DATA_DONE,
INT_ERROR_MASK, INT_READ_RDY, INT_WRITE_RDY,
};
use crate::dma::Channel;
use crate::timer::Timer;
const CMD_STOP_TRANSMISSION: u32 = 0x0cc3_0000;
const TM_MULTI_BLOCK: u32 = 0x0000_0020;
const ABORT_BUDGET_US: u64 = 10_000;
const DMA_DRAIN_BUDGET_US: u64 = 10_000;
const WAIT_BACKSTOP_US: u64 = 1_000_000;
static WAKER: Mutex<RefCell<Option<Waker>>> = Mutex::new(RefCell::new(None));
pub fn on_irq() {
let emmc = unsafe { Sd::steal_emmc() };
if emmc.irpt_en().read().bits() == 0 {
return;
}
emmc.irpt_en().write(|w| unsafe { w.bits(0) });
critical_section::with(|cs| {
if let Some(waker) = WAKER.borrow_ref_mut(cs).take() {
waker.wake();
}
});
}
struct Abort<'a> {
sd: &'a Sd,
timer: &'a Timer,
command: u32,
armed: bool,
}
impl<'a> Abort<'a> {
fn new(sd: &'a Sd, timer: &'a Timer, command: u32) -> Self {
Self {
sd,
timer,
command,
armed: true,
}
}
fn disarm(&mut self) {
self.armed = false;
}
}
impl Drop for Abort<'_> {
fn drop(&mut self) {
self.sd.end_wait();
if self.armed {
self.sd.abort_transfer(self.command, self.timer);
}
}
}
impl Sd {
pub async fn read_block_async(
&mut self,
block_index: u32,
buf: &mut Block,
timer: &Timer,
) -> Result<(), Error> {
self.read_blocks_pio_async(block_index, 1, core::iter::once(buf), timer)
.await
}
pub async fn read_blocks_async(
&mut self,
block_index: u32,
blocks: &mut [Block],
timer: &Timer,
) -> Result<(), Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
self.read_blocks_pio_async(block_index, count, blocks.iter_mut(), timer)
.await
}
pub async fn read_blocks_dma_async(
&mut self,
block_index: u32,
blocks: &mut [Block],
channel: &mut Channel,
timer: &Timer,
) -> Result<(), Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
let cmd = if count == 1 {
CMD_READ_SINGLE
} else {
CMD_READ_MULTI
};
self.start_transfer(cmd, block_index, count, timer)?;
let mut abort = Abort::new(self, timer, cmd);
let transfer = channel
.start_from_peripheral(
blocks.as_flattened_mut(),
DMA_DREQ_EMMC,
DATA_REG_BUS_ADDRESS,
)
.map_err(Error::Dma)?;
self.wait_interrupt_async(INT_DATA_DONE, cmd, timer).await?;
wait_for(timer, DMA_DRAIN_BUDGET_US, || transfer.is_complete())?;
if transfer.is_error() {
return Err(Error::Dma(crate::dma::Error::Transfer));
}
drop(transfer);
abort.disarm();
Ok(())
}
pub async fn write_block_async(
&mut self,
block_index: u32,
buf: &Block,
timer: &Timer,
) -> Result<(), Error> {
self.write_blocks_pio_async(block_index, 1, core::iter::once(buf), timer)
.await
}
pub async fn write_blocks_async(
&mut self,
block_index: u32,
blocks: &[Block],
timer: &Timer,
) -> Result<(), Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
self.write_blocks_pio_async(block_index, count, blocks.iter(), timer)
.await
}
pub async fn write_blocks_dma_async(
&mut self,
block_index: u32,
blocks: &[Block],
channel: &mut Channel,
timer: &Timer,
) -> Result<(), Error> {
if blocks.is_empty() {
return Ok(());
}
let count = checked_block_count(blocks.len())?;
let cmd = if count == 1 {
CMD_WRITE_SINGLE
} else {
CMD_WRITE_MULTI
};
self.start_transfer(cmd, block_index, count, timer)?;
let mut abort = Abort::new(self, timer, cmd);
let transfer = channel
.start_to_peripheral(blocks.as_flattened(), DMA_DREQ_EMMC, DATA_REG_BUS_ADDRESS)
.map_err(Error::Dma)?;
self.wait_interrupt_async(INT_DATA_DONE, cmd, timer).await?;
wait_for(timer, DMA_DRAIN_BUDGET_US, || transfer.is_complete())?;
if transfer.is_error() {
return Err(Error::Dma(crate::dma::Error::Transfer));
}
drop(transfer);
abort.disarm();
Ok(())
}
async fn read_blocks_pio_async<'a>(
&self,
block_index: u32,
count: u16,
blocks: impl Iterator<Item = &'a mut Block>,
timer: &Timer,
) -> Result<(), Error> {
let cmd = if count == 1 {
CMD_READ_SINGLE
} else {
CMD_READ_MULTI
};
self.start_transfer(cmd, block_index, count, timer)?;
let mut abort = Abort::new(self, timer, cmd);
for block in blocks {
self.wait_interrupt_async(INT_READ_RDY, cmd, timer).await?;
for chunk in block.as_chunks_mut::<4>().0 {
*chunk = self.emmc.data().read().bits().to_le_bytes();
}
}
if count > 1 {
self.wait_interrupt_async(INT_DATA_DONE, cmd, timer).await?;
}
abort.disarm();
Ok(())
}
async fn write_blocks_pio_async<'a>(
&self,
block_index: u32,
count: u16,
blocks: impl Iterator<Item = &'a Block>,
timer: &Timer,
) -> Result<(), Error> {
let cmd = if count == 1 {
CMD_WRITE_SINGLE
} else {
CMD_WRITE_MULTI
};
self.start_transfer(cmd, block_index, count, timer)?;
let mut abort = Abort::new(self, timer, cmd);
for block in blocks {
self.wait_interrupt_async(INT_WRITE_RDY, cmd, timer).await?;
for &chunk in block.as_chunks::<4>().0 {
self.emmc
.data()
.write(|w| unsafe { w.bits(u32::from_le_bytes(chunk)) });
}
}
self.wait_interrupt_async(INT_DATA_DONE, cmd, timer).await?;
abort.disarm();
Ok(())
}
async fn wait_interrupt_async(
&self,
mask: u32,
command: u32,
timer: &Timer,
) -> Result<u32, Error> {
let start = timer.now_micros();
poll_fn(|cx| {
if let Some(result) = self.poll_interrupt(mask, command) {
self.end_wait();
return Poll::Ready(result);
}
critical_section::with(|cs| {
WAKER.borrow_ref_mut(cs).replace(cx.waker().clone());
});
self.set_irq_enable(mask | INT_ERROR_MASK);
if let Some(result) = self.poll_interrupt(mask, command) {
self.end_wait();
return Poll::Ready(result);
}
if timer.now_micros() - start > WAIT_BACKSTOP_US {
self.end_wait();
return Poll::Ready(Err(Error::WaitTimeout {
waiting_for: mask,
interrupt: self.emmc.interrupt().read().bits(),
status: self.emmc.status().read().bits(),
command,
}));
}
Poll::Pending
})
.await
}
fn end_wait(&self) {
self.set_irq_enable(0);
critical_section::with(|cs| {
WAKER.borrow_ref_mut(cs).take();
});
}
fn set_irq_enable(&self, bits: u32) {
self.emmc.irpt_en().write(|w| unsafe { w.bits(bits) });
}
fn abort_transfer(&self, command: u32, timer: &Timer) {
if command & TM_MULTI_BLOCK != 0 && self.emmc.status().read().dat_inhibit().bit_is_set() {
let _ = self.command(CMD_STOP_TRANSMISSION, 0, timer);
}
self.emmc
.control1()
.modify(|_, w| w.srst_cmd().set_bit().srst_data().set_bit());
let _ = wait_for(timer, ABORT_BUDGET_US, || {
let control1 = self.emmc.control1().read();
!control1.srst_cmd().bit_is_set() && !control1.srst_data().bit_is_set()
});
self.emmc
.irpt_mask()
.write(|w| unsafe { w.bits(0xffff_ffff) });
let latched = self.emmc.interrupt().read().bits();
self.emmc.interrupt().write(|w| unsafe { w.bits(latched) });
}
}