use core::{fmt, mem, slice};
use aligned::{A4, Aligned};
use embedded_hal_async::delay::DelayNs;
use crate::{
BlockCommand, BlockReadCommand, BlockWriteCommand, BusAdapter, BusWidth, ByteCommand,
ByteReadCommand, ByteWriteCommand, Command, ControlCommand, INIT_FREQ, MmcBus, MmcError, R4,
R5,
sd::{self, OCR, RCA},
};
#[derive(Clone, Copy, Default)]
pub struct SDIO;
pub struct Cmd5 {
pub switch_to_1_8v_request: bool,
pub voltage_window: u16,
}
impl Command for Cmd5 {
const INDEX: u8 = 5;
type Resp<'a> = R4;
fn arg(&self) -> u32 {
u32::from(self.switch_to_1_8v_request) << 24 | u32::from(self.voltage_window & 0x1FF) << 15
}
}
impl ControlCommand for Cmd5 {}
pub fn io_send_op_cond(switch_to_1_8v_request: bool, voltage_window: u16) -> Cmd5 {
Cmd5 {
switch_to_1_8v_request,
voltage_window,
}
}
pub struct Cmd52 {
pub write: bool,
pub function: u8,
pub raw: bool,
pub addr: u32,
pub data: u8,
}
impl Command for Cmd52 {
const INDEX: u8 = 52;
type Resp<'a> = R5;
fn arg(&self) -> u32 {
let rw = (self.write as u32) << 31;
let fn_num = (self.function as u32 & 0x7) << 28;
let raw = (self.raw as u32) << 27;
let addr = (self.addr & 0x1FFFF) << 9;
let data = if self.write { self.data as u32 } else { 0 };
rw | fn_num | raw | addr | data
}
}
impl ControlCommand for Cmd52 {}
pub struct Cmd53ByteRead<'a> {
pub function: u8,
pub increment: bool,
pub addr: u32, pub buf: &'a mut Aligned<A4, [u8]>,
}
impl<'a> Command for Cmd53ByteRead<'a> {
const INDEX: u8 = 53;
type Resp<'b>
= R5
where
Self: 'b;
fn arg(&self) -> u32 {
let rw = 0u32 << 31;
let fnn = (self.function as u32 & 0x7) << 28;
let blk = 0u32 << 27; let op = (self.increment as u32) << 26;
let addr = (self.addr & 0x1FFFF) << 9;
let cnt = (self.buf.len() as u32) & 0x1FF;
rw | fnn | blk | op | addr | cnt
}
}
impl<'a> ByteCommand for Cmd53ByteRead<'a> {
fn byte_count(&self) -> usize {
self.buf.len()
}
}
impl<'a> ByteReadCommand for Cmd53ByteRead<'a> {
fn buf(&mut self) -> &mut Aligned<A4, [u8]> {
&mut *self.buf
}
}
pub struct Cmd53ByteWrite<'a> {
pub function: u8,
pub increment: bool,
pub addr: u32, pub buf: &'a Aligned<A4, [u8]>,
}
impl<'a> Command for Cmd53ByteWrite<'a> {
const INDEX: u8 = 53;
type Resp<'b>
= R5
where
Self: 'b;
fn arg(&self) -> u32 {
let rw = 1u32 << 31;
let fnn = (self.function as u32 & 0x7) << 28;
let blk = 0u32 << 27; let op = (self.increment as u32) << 26;
let addr = (self.addr & 0x1FFFF) << 9;
let cnt = (self.buf.len() as u32) & 0x1FF;
rw | fnn | blk | op | addr | cnt
}
}
impl<'a> ByteCommand for Cmd53ByteWrite<'a> {
fn byte_count(&self) -> usize {
self.buf.len()
}
}
impl<'a> ByteWriteCommand for Cmd53ByteWrite<'a> {
fn buf(&self) -> &Aligned<A4, [u8]> {
&*self.buf
}
}
pub struct Cmd53BlockRead<'a, const BLOCK_SIZE: usize> {
pub function: u8,
pub increment: bool,
pub addr: u32, pub buf: &'a mut [Aligned<A4, [u8; BLOCK_SIZE]>],
}
impl<'a, const BLOCK_SIZE: usize> Command for Cmd53BlockRead<'a, BLOCK_SIZE> {
const INDEX: u8 = 53;
type Resp<'b>
= R5
where
Self: 'b;
fn arg(&self) -> u32 {
let rw = 0u32 << 31;
let fnn = (self.function as u32 & 0x7) << 28;
let blk = 1u32 << 27; let op = (self.increment as u32) << 26;
let addr = (self.addr & 0x1FFFF) << 9;
let cnt = (self.buf.len() as u32) & 0x1FF;
rw | fnn | blk | op | addr | cnt
}
}
impl<'a, const BLOCK_SIZE: usize> BlockCommand for Cmd53BlockRead<'a, BLOCK_SIZE> {
fn block_size(&self) -> u16 {
BLOCK_SIZE as u16
}
fn block_count(&self) -> u32 {
self.buf.len() as u32
}
}
impl<'a, const BLOCK_SIZE: usize> BlockReadCommand for Cmd53BlockRead<'a, BLOCK_SIZE> {
fn buf(&mut self) -> &mut Aligned<A4, [u8]> {
unsafe {
mem::transmute(slice::from_raw_parts_mut(
self.buf.as_mut_ptr() as *mut _,
size_of_val(self.buf),
))
}
}
}
pub struct Cmd53BlockWrite<'a, const BLOCK_SIZE: usize> {
pub function: u8,
pub increment: bool,
pub addr: u32, pub buf: &'a [Aligned<A4, [u8; BLOCK_SIZE]>],
}
impl<'a, const BLOCK_SIZE: usize> Command for Cmd53BlockWrite<'a, BLOCK_SIZE> {
const INDEX: u8 = 53;
type Resp<'b>
= R5
where
Self: 'b;
fn arg(&self) -> u32 {
let rw = 1u32 << 31;
let fnn = (self.function as u32 & 0x7) << 28;
let blk = 1u32 << 27; let op = (self.increment as u32) << 26;
let addr = (self.addr & 0x1FFFF) << 9;
let cnt = (self.buf.len() as u32) & 0x1FF;
rw | fnn | blk | op | addr | cnt
}
}
impl<'a, const BLOCK_SIZE: usize> BlockCommand for Cmd53BlockWrite<'a, BLOCK_SIZE> {
fn block_size(&self) -> u16 {
BLOCK_SIZE as u16
}
fn block_count(&self) -> u32 {
self.buf.len() as u32
}
}
impl<'a, const BLOCK_SIZE: usize> BlockWriteCommand for Cmd53BlockWrite<'a, BLOCK_SIZE> {
fn buf(&self) -> &Aligned<A4, [u8]> {
unsafe {
mem::transmute(slice::from_raw_parts(
self.buf.as_ptr() as *const _,
size_of_val(self.buf),
))
}
}
}
impl OCR<SDIO> {
pub fn num_io_functions(&self) -> u8 {
((self.0 >> 28) & 0x7) as u8
}
}
impl fmt::Debug for OCR<SDIO> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OCR: Operation Conditions Register")
.field("IO functions", &self.num_io_functions())
.finish()
}
}
pub const CCCR_REV: u32 = 0x00;
pub const CCCR_SD_REV: u32 = 0x01;
pub const CCCR_IO_ENABLE: u32 = 0x02;
pub const CCCR_IO_READY: u32 = 0x03;
pub const CCCR_INT_ENABLE: u32 = 0x04;
pub const CCCR_INT_PENDING: u32 = 0x05;
pub const CCCR_IO_ABORT: u32 = 0x06;
pub const CCCR_BUS_CTRL: u32 = 0x07;
pub const CCCR_CARD_CAP: u32 = 0x08;
pub const CCCR_CIS_PTR: u32 = 0x09;
pub const CCCR_BUS_SUSPEND: u32 = 0x0C;
pub const CCCR_FUNC_SEL: u32 = 0x0D;
pub const CCCR_EXEC_FLAGS: u32 = 0x0E;
pub const CCCR_READY_FLAGS: u32 = 0x0F;
pub const CCCR_FN0_BLKSZ_LO: u32 = 0x10;
pub const CCCR_FN0_BLKSZ_HI: u32 = 0x11;
pub const CCCR_POWER_CTRL: u32 = 0x12;
pub const CCCR_HIGH_SPEED: u32 = 0x13;
pub const BUS_CTRL_WIDTH_MASK: u8 = 0x03;
pub const BUS_CTRL_WIDTH_1BIT: u8 = 0x00;
pub const BUS_CTRL_WIDTH_4BIT: u8 = 0x02;
pub const BUS_CTRL_WIDTH_8BIT: u8 = 0x03;
pub const BUS_CTRL_ECSI: u8 = 0x20;
pub const BUS_CTRL_SCSI: u8 = 0x40;
pub const CARD_CAP_SDC: u8 = 0x01;
pub const CARD_CAP_SMB: u8 = 0x02;
pub const CARD_CAP_SRW: u8 = 0x04;
pub const CARD_CAP_SBS: u8 = 0x08;
pub const CARD_CAP_S4MI: u8 = 0x10;
pub const CARD_CAP_E4MI: u8 = 0x20;
pub const CARD_CAP_LSC: u8 = 0x40;
pub const CARD_CAP_4BLS: u8 = 0x80;
pub const HIGH_SPEED_SHS: u8 = 0x01;
pub const HIGH_SPEED_EHS: u8 = 0x02;
pub const FBR_FUNCTION_IF: u32 = 0x00;
pub const FBR_EXT_IF: u32 = 0x01;
pub const FBR_POWER_SEL: u32 = 0x02;
pub const FBR_CIS_PTR: u32 = 0x09;
pub const FBR_CSA_PTR: u32 = 0x0C;
pub const FBR_CSA_DATA: u32 = 0x0F;
pub const FBR_BLKSZ_LO: u32 = 0x10;
pub const FBR_BLKSZ_HI: u32 = 0x11;
#[inline]
pub const fn fbr_base(function: u8) -> u32 {
0x100 + (function as u32) * 0x100
}
#[inline]
pub const fn fbr_std_func_if(function: u8) -> u32 {
fbr_base(function) + 0x00
}
#[inline]
pub const fn fbr_func_enable(function: u8) -> u32 {
fbr_base(function) + 0x02
}
#[inline]
pub const fn fbr_block_size_low(function: u8) -> u32 {
fbr_base(function) + 0x10
}
#[inline]
pub const fn fbr_block_size_high(function: u8) -> u32 {
fbr_base(function) + 0x11
}
pub struct SdioResponse {
pub data: u8,
pub flags: u8,
}
impl From<R5> for SdioResponse {
fn from(resp: R5) -> Self {
SdioResponse {
data: resp.data,
flags: resp.flags,
}
}
}
pub struct SdioCard<B: MmcBus, D: DelayNs> {
bus: BusAdapter<B, D>,
ocr: OCR<SDIO>,
}
impl<B: MmcBus, D: DelayNs> SdioCard<B, D> {
pub async fn new_sdio(bus: B, freq: u32, delay: D) -> Result<Self, MmcError> {
let mut s = Self {
bus: BusAdapter { bus, delay, rca: 0 },
ocr: OCR::default(),
};
s.acquire(freq).await?;
Ok(s)
}
async fn acquire(&mut self, freq: u32) -> Result<(), MmcError> {
let freq = freq.clamp(0, self.bus.bus.supports_frequency());
self.bus.bus.init_idle(INIT_FREQ).await?;
let mut i = 0;
self.ocr = loop {
match self
.bus
.send_command(io_send_op_cond(false, 0x0), false)
.await
{
Ok(r) => break Ok(r),
Err(MmcError::Timeout) => {}
Err(e) => break Err(e),
}
if i > 750 {
return Err(MmcError::Timeout);
}
self.bus.delay.delay_ms(1).await;
i += 1;
}?
.into();
self.bus.rca = RCA::<SDIO>::from(
self.bus
.send_command(sd::send_relative_address(), false)
.await?,
)
.address();
self.bus.select_card(Some(self.bus.rca)).await?;
let cap = self.cmd52_read(0, CCCR_CARD_CAP).await?;
let host_width = self.bus.bus.supports_bus_width();
let card_supports_4bit = cap & CARD_CAP_4BLS != 0 || cap & CARD_CAP_LSC == 0;
let (bus_width_reg, bus_width) = if matches!(host_width, BusWidth::W4) && card_supports_4bit
{
(BUS_CTRL_WIDTH_4BIT, BusWidth::W4)
} else {
(BUS_CTRL_WIDTH_1BIT, BusWidth::W1)
};
let bus_ctrl = self.cmd52_read(0, CCCR_BUS_CTRL).await?;
self.cmd52_write(
0,
CCCR_BUS_CTRL,
(bus_ctrl & !BUS_CTRL_WIDTH_MASK) | bus_width_reg,
)
.await?;
self.bus
.bus
.set_bus(bus_width, freq.clamp(0, 25_000_000))
.await?;
let hs_reg = self.cmd52_read(0, CCCR_HIGH_SPEED).await?;
if freq > 25_000_000 && hs_reg & HIGH_SPEED_SHS != 0 {
self.cmd52_write(0, CCCR_HIGH_SPEED, hs_reg | HIGH_SPEED_EHS)
.await?;
self.bus.bus.set_bus(bus_width, freq).await?;
}
Ok(())
}
pub async fn enable_functions(
&mut self,
func_mask: u8,
delay: &mut impl DelayNs,
) -> Result<(), MmcError> {
if func_mask & 0xFE == 0 {
return Ok(());
}
let current = self.cmd52_read(0, CCCR_IO_ENABLE).await?;
self.cmd52_write(0, CCCR_IO_ENABLE, current | func_mask)
.await?;
loop {
let ready = self.cmd52_read(0, CCCR_IO_READY).await?;
if ready & func_mask == func_mask {
return Ok(());
}
delay.delay_ms(2).await;
}
}
pub async fn disable_functions(&mut self, func_mask: u8) -> Result<(), MmcError> {
let current = self.cmd52_read(0, CCCR_IO_ENABLE).await?;
self.cmd52_write(0, CCCR_IO_ENABLE, current & !func_mask)
.await?;
Ok(())
}
pub async fn enable_interrupts(&mut self, func_mask: u8) -> Result<(), MmcError> {
self.cmd52_write(0, CCCR_INT_ENABLE, func_mask | 0x01).await
}
pub async fn set_block_size(&mut self, func: u8, size: u16) -> Result<(), MmcError> {
let base = fbr_base(func);
self.cmd52_write(0, base + FBR_BLKSZ_LO, (size & 0xFF) as u8)
.await?;
self.cmd52_write(0, base + FBR_BLKSZ_HI, (size >> 8) as u8)
.await?;
Ok(())
}
pub async fn cmd52_read(&mut self, func: u8, addr: u32) -> Result<u8, MmcError> {
let resp = self
.bus
.send_command(
Cmd52 {
write: false,
function: func,
raw: false,
addr,
data: 0,
},
false,
)
.await?;
if resp.is_error() {
return Err(MmcError::Other);
}
Ok(resp.data)
}
pub async fn cmd52_write(&mut self, func: u8, addr: u32, data: u8) -> Result<(), MmcError> {
let resp = self
.bus
.send_command(
Cmd52 {
write: true,
function: func,
raw: false,
addr,
data,
},
false,
)
.await?;
if resp.is_error() {
return Err(MmcError::Other);
}
Ok(())
}
pub async fn cmd52_write_read(
&mut self,
func: u8,
addr: u32,
data: u8,
) -> Result<u8, MmcError> {
let resp = self
.bus
.send_command(
Cmd52 {
write: true,
function: func,
raw: true,
addr,
data,
},
false,
)
.await?;
if resp.is_error() {
return Err(MmcError::Other);
}
Ok(resp.data)
}
pub async fn cmd53_read_blocks<const BLOCK_SIZE: usize>(
&mut self,
function: u8,
increment: bool,
addr: u32, buf: &mut [Aligned<A4, [u8; BLOCK_SIZE]>],
) -> Result<SdioResponse, MmcError> {
self.bus
.bus
.read_blocks(Cmd53BlockRead {
function,
increment,
addr,
buf,
})
.await
.map(|r| r.into())
}
pub async fn cmd53_read_bytes(
&mut self,
function: u8,
increment: bool,
addr: u32, buf: &mut Aligned<A4, [u8]>,
) -> Result<SdioResponse, MmcError> {
self.bus
.bus
.read_bytes(Cmd53ByteRead {
function,
increment,
addr,
buf,
})
.await
.map(|r| r.into())
}
pub async fn cmd53_write_blocks<const BLOCK_SIZE: usize>(
&mut self,
function: u8,
increment: bool,
addr: u32, buf: &mut [Aligned<A4, [u8; BLOCK_SIZE]>],
) -> Result<SdioResponse, MmcError> {
self.bus
.bus
.write_blocks(Cmd53BlockWrite {
function,
increment,
addr,
buf,
})
.await
.map(|r| r.into())
}
pub async fn cmd53_write_bytes(
&mut self,
function: u8,
increment: bool,
addr: u32, buf: &mut Aligned<A4, [u8]>,
) -> Result<SdioResponse, MmcError> {
self.bus
.bus
.write_bytes(Cmd53ByteWrite {
function,
increment,
addr,
buf,
})
.await
.map(|r| r.into())
}
}