use crate::gpio::alt::sdio as alt;
use crate::pac::{self, SDIO};
use crate::rcc::{Clocks, Enable, Reset};
#[allow(unused_imports)]
use fugit::HertzU32 as Hertz;
pub use sdio_host::{
common_cmd::{self, ResponseLen},
emmc::{CardCapacity, CardStatus, CurrentState, CID, CSD, EMMC, OCR, RCA},
emmc_cmd,
sd::{SDStatus, CIC, SCR, SD},
sd_cmd, Cmd,
};
pub trait Pins {
const BUSWIDTH: Buswidth;
type SdPins;
fn convert(self) -> Self::SdPins;
}
impl<CLK, CMD, D0, D1, D2, D3, D4, D5, D6, D7> Pins for (CLK, CMD, D0, D1, D2, D3, D4, D5, D6, D7)
where
CLK: Into<alt::Ck>,
CMD: Into<alt::Cmd>,
D0: Into<alt::D0>,
D1: Into<alt::D1>,
D2: Into<alt::D2>,
D3: Into<alt::D3>,
D4: Into<alt::D4>,
D5: Into<alt::D5>,
D6: Into<alt::D6>,
D7: Into<alt::D7>,
{
const BUSWIDTH: Buswidth = Buswidth::Buswidth8;
type SdPins = (
alt::Ck,
alt::Cmd,
alt::D0,
alt::D1,
alt::D2,
alt::D3,
alt::D4,
alt::D5,
alt::D6,
alt::D7,
);
fn convert(self) -> Self::SdPins {
(
self.0.into(),
self.1.into(),
self.2.into(),
self.3.into(),
self.4.into(),
self.5.into(),
self.6.into(),
self.7.into(),
self.8.into(),
self.9.into(),
)
}
}
impl<CLK, CMD, D0, D1, D2, D3> Pins for (CLK, CMD, D0, D1, D2, D3)
where
CLK: Into<alt::Ck>,
CMD: Into<alt::Cmd>,
D0: Into<alt::D0>,
D1: Into<alt::D1>,
D2: Into<alt::D2>,
D3: Into<alt::D3>,
{
const BUSWIDTH: Buswidth = Buswidth::Buswidth4;
type SdPins = (alt::Ck, alt::Cmd, alt::D0, alt::D1, alt::D2, alt::D3);
fn convert(self) -> Self::SdPins {
(
self.0.into(),
self.1.into(),
self.2.into(),
self.3.into(),
self.4.into(),
self.5.into(),
)
}
}
impl<CLK, CMD, D0> Pins for (CLK, CMD, D0)
where
CLK: Into<alt::Ck>,
CMD: Into<alt::Cmd>,
D0: Into<alt::D0>,
{
const BUSWIDTH: Buswidth = Buswidth::Buswidth1;
type SdPins = (alt::Ck, alt::Cmd, alt::D0);
fn convert(self) -> Self::SdPins {
(self.0.into(), self.1.into(), self.2.into())
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum Buswidth {
Buswidth1 = 0,
Buswidth4 = 1,
Buswidth8 = 2,
}
pub enum ClockFreq {
F24Mhz = 0,
F16Mhz = 1,
F12Mhz = 2,
F8Mhz = 8,
F4Mhz = 10,
F1Mhz = 46,
F400Khz = 118,
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Error {
Timeout,
SoftwareTimeout,
Crc,
UnsupportedCardVersion,
UnsupportedCardType,
UnsupportedVoltage,
DataCrcFail,
RxOverFlow,
TxUnderErr,
NoCard,
}
#[derive(Debug, Copy, Clone)]
pub enum AddressMode {
Byte,
Block512,
}
pub struct Sdio<P: SdioPeripheral> {
sdio: SDIO,
bw: Buswidth,
card: Option<P>,
clock: Hertz,
}
pub struct SdCard {
pub capacity: CardCapacity,
pub ocr: OCR<SD>,
pub rca: RCA<SD>, pub cid: CID<SD>,
pub csd: CSD<SD>,
pub scr: SCR,
}
pub struct Emmc {
pub ocr: OCR<EMMC>,
pub rca: RCA<EMMC>, pub cid: CID<EMMC>,
pub csd: CSD<EMMC>,
}
impl<P: SdioPeripheral> Sdio<P> {
pub fn new<PINS: Pins>(sdio: SDIO, pins: PINS, clocks: &Clocks) -> Self {
unsafe {
SDIO::enable_unchecked();
SDIO::reset_unchecked();
}
sdio.clkcr.write(|w| {
w.widbus()
.bus_width1()
.clken()
.enabled()
.clkdiv()
.bits(ClockFreq::F400Khz as u8)
.pwrsav()
.disabled()
.bypass()
.disabled()
.negedge()
.rising()
.hwfc_en()
.disabled()
});
let _pins = pins.convert();
let mut host = Self {
sdio,
bw: PINS::BUSWIDTH,
card: None,
clock: clocks.sysclk(),
};
host.power_card(false);
host
}
fn power_card(&mut self, on: bool) {
use crate::pac::sdio::power::PWRCTRL_A;
self.sdio.power.modify(|_, w| {
w.pwrctrl().variant(if on {
PWRCTRL_A::PowerOn
} else {
PWRCTRL_A::PowerOff
})
});
cortex_m::asm::delay(2 * (self.clock.raw() / 1000));
}
pub fn card(&self) -> Result<&P, Error> {
self.card.as_ref().ok_or(Error::NoCard)
}
pub fn read_block(&mut self, blockaddr: u32, block: &mut [u8; 512]) -> Result<(), Error> {
let card = self.card()?;
let blockaddr = match card.get_address_mode() {
AddressMode::Byte => blockaddr * 512,
AddressMode::Block512 => blockaddr,
};
self.cmd(common_cmd::set_block_length(512))?;
self.start_datapath_transfer(512, 9, true);
self.cmd(common_cmd::read_single_block(blockaddr))?;
let mut i = 0;
let status = loop {
let sta = self.sdio.sta.read();
if sta.rxact().bit_is_clear() {
break sta;
}
if sta.rxfifohf().bit() {
for _ in 0..8 {
let bytes = self.sdio.fifo.read().bits().to_le_bytes();
block[i..i + 4].copy_from_slice(&bytes);
i += 4;
}
}
if i == block.len() {
break sta;
}
};
status_to_error(status)?;
while !self.card_ready()? {}
Ok(())
}
pub fn write_block(&mut self, blockaddr: u32, block: &[u8; 512]) -> Result<(), Error> {
let card = self.card()?;
let blockaddr = match card.get_address_mode() {
AddressMode::Byte => blockaddr * 512,
AddressMode::Block512 => blockaddr,
};
self.cmd(common_cmd::set_block_length(512))?;
self.start_datapath_transfer(512, 9, false);
self.cmd(common_cmd::write_single_block(blockaddr))?;
let mut i = 0;
let status = loop {
let sta = self.sdio.sta.read();
if sta.txact().bit_is_clear() {
break sta;
}
if sta.txfifohe().bit() {
for _ in 0..8 {
let mut wb = [0u8; 4];
wb.copy_from_slice(&block[i..i + 4]);
let word = u32::from_le_bytes(wb);
self.sdio.fifo.write(|w| w.bits(word));
i += 4;
}
}
if i == block.len() {
break sta;
}
};
status_to_error(status)?;
loop {
let sta = self.sdio.sta.read();
if !sta.txact().bit_is_set() {
break;
}
}
while !self.card_ready()? {}
Ok(())
}
fn start_datapath_transfer(&self, length_bytes: u32, block_size: u8, card_to_controller: bool) {
use crate::pac::sdio::dctrl::DTDIR_A;
assert!(block_size <= 14);
loop {
let status = self.sdio.sta.read();
if status.cmdact().bit_is_clear()
&& status.rxact().bit_is_clear()
&& status.txact().bit_is_clear()
{
break;
}
}
let dtdir = if card_to_controller {
DTDIR_A::CardToController
} else {
DTDIR_A::ControllerToCard
};
self.sdio.dtimer.write(|w| w.datatime().bits(0xFFFF_FFFF));
self.sdio.dlen.write(|w| w.datalength().bits(length_bytes));
self.sdio.dctrl.write(|w| {
w.dblocksize()
.bits(block_size) .dtdir()
.variant(dtdir)
.dten()
.enabled() });
}
fn read_status(&mut self) -> Result<CardStatus<P>, Error> {
let card = self.card()?;
self.cmd(common_cmd::card_status(card.get_address(), false))?;
let r1 = self.sdio.resp1.read().bits();
Ok(CardStatus::from(r1))
}
fn card_ready(&mut self) -> Result<bool, Error> {
Ok(self.read_status()?.state() == CurrentState::Transfer)
}
fn select_card(&self, rca: u16) -> Result<(), Error> {
let r = self.cmd(common_cmd::select_card(rca));
match (r, rca) {
(Err(Error::Timeout), 0) => Ok(()),
_ => r,
}
}
fn app_cmd<R: common_cmd::Resp>(&self, acmd: Cmd<R>) -> Result<(), Error> {
let rca = self.card().map(|card| card.get_address()).unwrap_or(0);
self.cmd(common_cmd::app_cmd(rca))?;
self.cmd(acmd)
}
fn cmd<R: common_cmd::Resp>(&self, cmd: Cmd<R>) -> Result<(), Error> {
use crate::pac::sdio::cmd::WAITRESP_A;
while self.sdio.sta.read().cmdact().bit_is_set() {}
clear_all_interrupts(&self.sdio.icr);
self.sdio.arg.write(|w| w.cmdarg().bits(cmd.arg));
let waitresp = match cmd.response_len() {
ResponseLen::Zero => WAITRESP_A::NoResponse,
ResponseLen::R48 => WAITRESP_A::ShortResponse,
ResponseLen::R136 => WAITRESP_A::LongResponse,
};
self.sdio.cmd.write(|w| {
w.waitresp()
.variant(waitresp)
.cmdindex()
.bits(cmd.cmd)
.waitint()
.disabled()
.cpsmen()
.enabled()
});
let mut timeout: u32 = 0xFFFF_FFFF;
let status = if cmd.response_len() == ResponseLen::Zero {
loop {
let sta = self.sdio.sta.read();
if sta.cmdact().bit_is_clear()
&& (sta.ctimeout().bit_is_set() || sta.cmdsent().bit_is_set())
{
break sta;
}
if timeout == 0 {
return Err(Error::SoftwareTimeout);
}
timeout -= 1;
}
} else {
loop {
let sta = self.sdio.sta.read();
if sta.cmdact().bit_is_clear()
&& (sta.ctimeout().bit()
|| sta.cmdrend().bit_is_set()
|| sta.ccrcfail().bit_is_set())
{
break sta;
}
if timeout == 0 {
return Err(Error::SoftwareTimeout);
}
timeout -= 1;
}
};
status_to_error(status)
}
}
impl Sdio<SdCard> {
pub fn init(&mut self, freq: ClockFreq) -> Result<(), Error> {
self.power_card(true);
self.sdio.clkcr.modify(|_, w| w.clken().enabled());
self.cmd(common_cmd::idle())?;
self.cmd(sd_cmd::send_if_cond(1, 0xAA))?;
let cic = CIC::from(self.sdio.resp1.read().bits());
if cic.pattern() != 0xAA {
return Err(Error::UnsupportedCardVersion);
}
if cic.voltage_accepted() & 1 == 0 {
return Err(Error::UnsupportedVoltage);
}
let ocr = loop {
let voltage_window = 1 << 5;
match self.app_cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window)) {
Ok(_) => (),
Err(Error::Crc) => (),
Err(err) => return Err(err),
}
let ocr = OCR::from(self.sdio.resp1.read().bits());
if ocr.is_busy() {
continue;
}
break ocr;
};
let capacity = if ocr.high_capacity() {
CardCapacity::HighCapacity
} else {
CardCapacity::StandardCapacity
};
self.cmd(common_cmd::all_send_cid())?;
let mut cid = [0; 4];
cid[3] = self.sdio.resp1.read().bits();
cid[2] = self.sdio.resp2.read().bits();
cid[1] = self.sdio.resp3.read().bits();
cid[0] = self.sdio.resp4.read().bits();
let cid = CID::from(cid);
self.cmd(sd_cmd::send_relative_address())?;
let rca = RCA::from(self.sdio.resp1.read().bits());
let card_addr = rca.address();
self.cmd(common_cmd::send_csd(card_addr))?;
let mut csd = [0; 4];
csd[3] = self.sdio.resp1.read().bits();
csd[2] = self.sdio.resp2.read().bits();
csd[1] = self.sdio.resp3.read().bits();
csd[0] = self.sdio.resp4.read().bits();
let csd = CSD::from(csd);
self.select_card(card_addr)?;
let scr = self.get_scr(card_addr)?;
let card = SdCard {
capacity,
ocr,
rca,
cid,
csd,
scr,
};
self.card.replace(card);
while !self.card_ready()? {}
self.set_bus(self.bw, freq)?;
Ok(())
}
pub fn read_sd_status(&mut self) -> Result<SDStatus, Error> {
let _card = self.card()?;
self.cmd(common_cmd::set_block_length(64))?;
self.start_datapath_transfer(64, 6, true);
self.app_cmd(sd_cmd::sd_status())?;
let mut status = [0u32; 16];
let mut idx = 0;
let s = loop {
let sta = self.sdio.sta.read();
if sta.rxact().bit_is_clear() {
break sta;
}
if sta.rxfifohf().bit() {
for _ in 0..8 {
status[15 - idx] = self.sdio.fifo.read().bits().swap_bytes();
idx += 1;
}
}
if idx == status.len() {
break sta;
}
};
status_to_error(s)?;
Ok(SDStatus::from(status))
}
fn get_scr(&self, rca: u16) -> Result<SCR, Error> {
self.cmd(common_cmd::set_block_length(8))?;
self.start_datapath_transfer(8, 3, true);
self.cmd(common_cmd::app_cmd(rca))?;
self.cmd(sd_cmd::send_scr())?;
let mut buf = [0; 2];
let mut i = 0;
let status = loop {
let sta = self.sdio.sta.read();
if sta.rxact().bit_is_clear() {
break sta;
}
if sta.rxdavl().bit() {
buf[1 - i] = self.sdio.fifo.read().bits().swap_bytes();
i += 1;
}
if i == 2 {
break sta;
}
};
status_to_error(status)?;
Ok(SCR::from(buf))
}
fn set_bus(&self, width: Buswidth, freq: ClockFreq) -> Result<(), Error> {
use crate::pac::sdio::clkcr::WIDBUS_A;
let card_widebus = self.card()?.supports_widebus();
let width = match width {
Buswidth::Buswidth4 if card_widebus => WIDBUS_A::BusWidth4,
_ => WIDBUS_A::BusWidth1,
};
self.app_cmd(sd_cmd::set_bus_width(width == WIDBUS_A::BusWidth4))?;
self.sdio.clkcr.modify(|_, w| {
w.clkdiv()
.bits(freq as u8)
.widbus()
.variant(width)
.clken()
.enabled()
});
Ok(())
}
}
impl Sdio<Emmc> {
pub fn init(&mut self, freq: ClockFreq) -> Result<(), Error> {
let card_addr: RCA<EMMC> = RCA::from(1u16);
self.power_card(true);
self.sdio.clkcr.modify(|_, w| w.clken().enabled());
self.cmd(common_cmd::idle())?;
let ocr = loop {
match self.cmd(emmc_cmd::send_op_cond(0b01000000111111111000000000000000)) {
Ok(_) => (),
Err(Error::Crc) => (),
Err(err) => return Err(err),
};
let ocr = OCR::<EMMC>::from(self.sdio.resp1.read().bits());
if !ocr.is_busy() {
break ocr;
}
};
self.cmd(common_cmd::all_send_cid())?;
let mut cid = [0; 4];
cid[3] = self.sdio.resp1.read().bits();
cid[2] = self.sdio.resp2.read().bits();
cid[1] = self.sdio.resp3.read().bits();
cid[0] = self.sdio.resp4.read().bits();
let cid = CID::<EMMC>::from(cid);
self.cmd(emmc_cmd::assign_relative_address(card_addr.address()))?;
self.cmd(common_cmd::send_csd(card_addr.address()))?;
let mut csd = [0; 4];
csd[3] = self.sdio.resp1.read().bits();
csd[2] = self.sdio.resp2.read().bits();
csd[1] = self.sdio.resp3.read().bits();
csd[0] = self.sdio.resp4.read().bits();
let csd = CSD::<EMMC>::from(csd);
self.select_card(card_addr.address())?;
let card = Emmc {
ocr,
rca: card_addr,
cid,
csd,
};
self.card.replace(card);
while !self.card_ready()? {}
self.set_bus(self.bw, freq)?;
Ok(())
}
pub fn set_bus(&mut self, width: Buswidth, freq: ClockFreq) -> Result<(), Error> {
use crate::pac::sdio::clkcr::WIDBUS_A;
self.cmd(emmc_cmd::modify_ext_csd(
emmc_cmd::AccessMode::WriteByte,
183,
width as u8,
))?;
let width = match width {
Buswidth::Buswidth1 => WIDBUS_A::BusWidth1,
Buswidth::Buswidth4 => WIDBUS_A::BusWidth4,
Buswidth::Buswidth8 => WIDBUS_A::BusWidth8,
};
while !self.card_ready()? {}
self.sdio.clkcr.modify(|_, w| {
w.clkdiv()
.bits(freq as u8)
.widbus()
.variant(width)
.clken()
.enabled()
});
Ok(())
}
}
fn status_to_error(sta: pac::sdio::sta::R) -> Result<(), Error> {
if sta.ctimeout().bit_is_set() {
return Err(Error::Timeout);
} else if sta.ccrcfail().bit() {
return Err(Error::Crc);
} else if sta.dcrcfail().bit() {
return Err(Error::DataCrcFail);
} else if sta.rxoverr().bit() {
return Err(Error::RxOverFlow);
} else if sta.dtimeout().bit() {
return Err(Error::Timeout);
} else if sta.txunderr().bit() {
return Err(Error::TxUnderErr);
}
Ok(())
}
fn clear_all_interrupts(icr: &pac::sdio::ICR) {
icr.modify(|_, w| {
w.ccrcfailc()
.set_bit()
.ctimeoutc()
.set_bit()
.ceataendc()
.set_bit()
.cmdrendc()
.set_bit()
.cmdsentc()
.set_bit()
.dataendc()
.set_bit()
.dbckendc()
.set_bit()
.dcrcfailc()
.set_bit()
.dtimeoutc()
.set_bit()
.sdioitc()
.set_bit()
.stbiterrc()
.set_bit()
.rxoverrc()
.set_bit()
.txunderrc()
.set_bit()
});
}
impl SdCard {
pub fn block_count(&self) -> u64 {
self.csd.block_count()
}
fn supports_widebus(&self) -> bool {
self.scr.bus_width_four()
}
}
impl SdioPeripheral for SdCard {
fn get_address(&self) -> u16 {
self.rca.address()
}
fn get_address_mode(&self) -> AddressMode {
match self.capacity {
CardCapacity::StandardCapacity => AddressMode::Byte,
CardCapacity::HighCapacity => AddressMode::Block512,
_ => AddressMode::Block512,
}
}
}
impl SdioPeripheral for Emmc {
fn get_address(&self) -> u16 {
self.rca.address()
}
fn get_address_mode(&self) -> AddressMode {
AddressMode::Block512
}
}
pub trait SdioPeripheral {
fn get_address(&self) -> u16;
fn get_address_mode(&self) -> AddressMode;
}