mod consts;
pub use consts::{BLOCK_SIZE, CmdError, CommandType, PowerLevel, ResponseType};
pub use consts::{SD_DRIVER_BASE, SdmmcRegisters, TOP_BASE};
use consts::*;
use core::cell::Cell;
use crate::utils::{delay, delay_long, delay_short};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use crate::pinmux::{
DriveStrength, FMUX_SD0_CLK, FMUX_SD0_CMD, FMUX_SD0_D0, FMUX_SD0_D1, FMUX_SD0_D2, FMUX_SD0_D3,
IoConfig, Pinmux,
};
extern crate alloc;
#[derive(Debug, Clone, Copy, Default)]
pub struct SdCardInfo {
pub rca: u32,
pub csd_raw: [u32; 4],
pub csd_structure: u8,
pub capacity_bytes: u64,
}
pub struct Sdmmc {
regs: &'static SdmmcRegisters,
top_regs: &'static TopRegisters,
pinmux: Option<Pinmux>,
card_info: Cell<SdCardInfo>,
}
impl Sdmmc {
pub unsafe fn new(sd_base: usize, top_base: usize) -> Self {
unsafe {
Self {
regs: &*(sd_base as *const SdmmcRegisters),
top_regs: &*(top_base as *const TopRegisters),
pinmux: None,
card_info: Cell::new(SdCardInfo::default()),
}
}
}
pub fn set_pinmux(&mut self, pinmux: Pinmux) {
self.pinmux = Some(pinmux);
}
pub fn is_card_inserted(&self) -> bool {
self.regs.present_state.is_set(PRESENT_STATE::CARD_INSERTED)
}
pub fn read_block_single(&self, block_id: u32, buffer: &mut [u8]) -> Result<(), CmdError> {
assert_eq!(buffer.len(), 512);
self.cmd_transfer(CommandType::CMD(17), block_id, 1, false)?;
self.read_buff(buffer)?;
let res = self.wait_for_xfer_done();
self.regs
.norm_and_err_int_sts
.set(self.regs.norm_and_err_int_sts.get());
res
}
pub fn read_block(&self, block_id: u32, data: &mut [u8]) -> Result<(), CmdError> {
let blk_cnt = data.len() / BLOCK_SIZE;
log::debug!("reading {} blocks", blk_cnt);
self.cmd_transfer(CommandType::CMD(18), block_id, blk_cnt as _, false)?;
log::debug!(
"blk cnt: {}",
self.regs.blk_size_and_cnt.read(BLK_SIZE_AND_CNT::BLK_CNT)
);
self.read_buff(data)?;
let res = self.wait_for_xfer_done();
self.regs
.norm_and_err_int_sts
.set(self.regs.norm_and_err_int_sts.get());
res
}
pub fn write_block(&self, block_id: u32, data: &[u8]) -> Result<(), CmdError> {
self.cmd_transfer(CommandType::CMD(24), block_id, 1, false)?;
self.write_buff(data)?;
let res = self.wait_for_xfer_done();
self.regs
.norm_and_err_int_sts
.set(self.regs.norm_and_err_int_sts.get());
res
}
pub fn reset_config(&self) {
self.power_config(PowerLevel::Close);
self.regs.clk_ctl.modify(
CLK_CTL::SW_RST_DAT::CLEAR + CLK_CTL::SW_RST_CMD::CLEAR + CLK_CTL::SW_RST_ALL::CLEAR,
);
delay(0x1000);
self.power_config(PowerLevel::V33);
self.regs
.host_ctl1_pwr_bg_wup
.modify(HOST_CTL1_PWR_BG_WUP::DAT_XFER_WIDTH::Width4Bit);
}
pub fn wait_for_cmd_done(&self) -> Result<(), CmdError> {
loop {
let sts = self.regs.norm_and_err_int_sts.extract();
if sts.is_set(NORM_AND_ERR_INT_STS::ERR_INT) {
self.regs
.norm_and_err_int_sts
.write(NORM_AND_ERR_INT_STS::ERR_INT::SET);
return Err(CmdError::IntError);
}
if sts.is_set(NORM_AND_ERR_INT_STS::CMD_CMPL) {
self.regs
.norm_and_err_int_sts
.write(NORM_AND_ERR_INT_STS::CMD_CMPL::SET);
return Ok(());
}
delay(1);
}
}
pub fn wait_for_xfer_done(&self) -> Result<(), CmdError> {
loop {
let sts = self.regs.norm_and_err_int_sts.extract();
if sts.is_set(NORM_AND_ERR_INT_STS::XFER_CMPL) {
self.regs
.norm_and_err_int_sts
.write(NORM_AND_ERR_INT_STS::XFER_CMPL::SET);
return Ok(());
}
if sts.is_set(NORM_AND_ERR_INT_STS::ERR_INT) {
self.regs
.norm_and_err_int_sts
.write(NORM_AND_ERR_INT_STS::ERR_INT::SET);
return Err(CmdError::IntError);
}
delay(1);
}
}
pub fn cmd_transfer(
&self,
cmd: CommandType,
arg: u32,
blk_cnt: u32,
dma: bool,
) -> Result<(), CmdError> {
while self.regs.present_state.any_matching_bits_set(
PRESENT_STATE::CMD_INHIBIT::SET + PRESENT_STATE::CMD_INHIBIT_DAT::SET,
) {
core::hint::spin_loop();
}
let mut xfer_mode = XFER_MODE_AND_CMD::CMD_IDX.val(cmd.num() as u32);
match cmd {
CommandType::CMD(17) | CommandType::ACMD(51) => {
xfer_mode +=
XFER_MODE_AND_CMD::DATA_PRESENT::SET + XFER_MODE_AND_CMD::DAT_XFER_DIR::Read;
}
CommandType::CMD(18) => {
self.regs.blk_size_and_cnt.write(
BLK_SIZE_AND_CNT::XFER_BLK_SIZE.val(BLOCK_SIZE as u32)
+ BLK_SIZE_AND_CNT::BLK_CNT.val(blk_cnt),
);
xfer_mode += XFER_MODE_AND_CMD::DATA_PRESENT::SET
+ XFER_MODE_AND_CMD::DAT_XFER_DIR::Read
+ XFER_MODE_AND_CMD::BLK_CNT_EN::SET
+ XFER_MODE_AND_CMD::AUTO_CMD_EN::AutoCmd12
+ XFER_MODE_AND_CMD::MULTI_BLK_SEL::SET;
}
CommandType::CMD(24) => {
xfer_mode +=
XFER_MODE_AND_CMD::DATA_PRESENT::SET + XFER_MODE_AND_CMD::DAT_XFER_DIR::Write;
}
_ => {}
}
log::debug!(
"blk cnt: {}",
self.regs.blk_size_and_cnt.read(BLK_SIZE_AND_CNT::BLK_CNT)
);
if dma {
xfer_mode += XFER_MODE_AND_CMD::DMA_EN::SET;
}
match cmd {
CommandType::CMD(7)
| CommandType::CMD(8)
| CommandType::CMD(16)
| CommandType::CMD(17)
| CommandType::CMD(18)
| CommandType::CMD(24)
| CommandType::ACMD(6)
| CommandType::ACMD(42)
| CommandType::ACMD(51) => {
xfer_mode += XFER_MODE_AND_CMD::RESP_TYPE::Response48
+ XFER_MODE_AND_CMD::CMD_CRC_CHK_EN::SET
+ XFER_MODE_AND_CMD::CMD_IDX_CHK_EN::SET;
}
CommandType::CMD(2) | CommandType::CMD(9) => {
xfer_mode += XFER_MODE_AND_CMD::RESP_TYPE::Response136
+ XFER_MODE_AND_CMD::CMD_CRC_CHK_EN::SET;
}
CommandType::ACMD(41) | CommandType::CMD(58) => {
xfer_mode += XFER_MODE_AND_CMD::RESP_TYPE::Response48;
}
CommandType::CMD(3) => {
xfer_mode += XFER_MODE_AND_CMD::RESP_TYPE::Response48Busy
+ XFER_MODE_AND_CMD::CMD_CRC_CHK_EN::SET
+ XFER_MODE_AND_CMD::CMD_IDX_CHK_EN::SET;
}
_ => {}
}
self.regs.clk_ctl.modify(CLK_CTL::TOUT_CNT::TMCLK2p27);
self.regs.norm_and_err_int_sts.set(0xF3FFFFFF);
log::debug!("read cmd transfer: {:#x?}", self.regs.norm_and_err_int_sts_en.get());
self.regs.argument1.set(arg);
self.regs.xfer_mode_and_cmd.write(xfer_mode);
self.wait_for_cmd_done()?;
let resp0 = self.regs.response0.get();
let resp1 = self.regs.response1.get();
let resp2 = self.regs.response2.get();
let resp3 = self.regs.response3.get();
log::trace!(
"resp0: {:#x} resp1: {:#x} resp2: {:#x} resp3: {:#x}",
resp0,
resp1,
resp2,
resp3
);
Ok(())
}
pub fn get_response0(&self) -> u32 {
self.regs.response0.get()
}
pub fn card_info(&self) -> SdCardInfo {
self.card_info.get()
}
pub fn card_capacity_bytes(&self) -> u64 {
self.card_info.get().capacity_bytes
}
pub fn card_capacity_blocks(&self) -> u64 {
self.card_info.get().capacity_bytes / BLOCK_SIZE as u64
}
pub fn refresh_csd(&self) -> Result<SdCardInfo, CmdError> {
let rca = self.card_info.get().rca;
self.cmd_transfer(CommandType::CMD(9), rca, 0, false)?;
let csd_raw = [
self.regs.response0.get(),
self.regs.response1.get(),
self.regs.response2.get(),
self.regs.response3.get(),
];
let info = parse_sd_card_info(rca, csd_raw);
self.card_info.set(info);
Ok(info)
}
pub fn pad_settings(&self) {
self.top_regs
.sd_pwrsw_ctrl
.write(TOP_SD_PWRSW_CTRL::PWRSW_CTRL.val(0x9));
if let Some(ref pinmux) = self.pinmux {
pinmux.set_sd0_clk_func(FMUX_SD0_CLK::FSEL::Value::SDIO0_CLK);
pinmux.set_sd0_cmd_func(FMUX_SD0_CMD::FSEL::Value::SDIO0_CMD);
pinmux.set_sd0_d0_func(FMUX_SD0_D0::FSEL::Value::SDIO0_D0);
pinmux.set_sd0_d1_func(FMUX_SD0_D1::FSEL::Value::SDIO0_D1);
pinmux.set_sd0_d2_func(FMUX_SD0_D2::FSEL::Value::SDIO0_D2);
pinmux.set_sd0_d3_func(FMUX_SD0_D3::FSEL::Value::SDIO0_D3);
let ioblk_g10 = pinmux.ioblk_g10();
ioblk_g10
.sd0_clk
.set_drive_strength(DriveStrength::Level2 as u8);
ioblk_g10
.sd0_cmd
.set_drive_strength(DriveStrength::Level2 as u8);
ioblk_g10
.sd0_d0
.set_drive_strength(DriveStrength::Level2 as u8);
ioblk_g10
.sd0_d1
.set_drive_strength(DriveStrength::Level2 as u8);
ioblk_g10
.sd0_d2
.set_drive_strength(DriveStrength::Level2 as u8);
ioblk_g10
.sd0_d3
.set_drive_strength(DriveStrength::Level2 as u8);
ioblk_g10.sd0_cmd.set_pull_up(true);
ioblk_g10.sd0_d0.set_pull_up(true);
ioblk_g10.sd0_d1.set_pull_up(true);
ioblk_g10.sd0_d2.set_pull_up(true);
ioblk_g10.sd0_d3.set_pull_up(true);
}
}
pub fn power_config(&self, level: PowerLevel) {
match level {
PowerLevel::V33 => {
self.regs.host_ctl1_pwr_bg_wup.modify(
HOST_CTL1_PWR_BG_WUP::SD_BUS_VOL_SEL::V33
+ HOST_CTL1_PWR_BG_WUP::SD_BUS_PWR::SET,
);
self.top_regs
.sd_pwrsw_ctrl
.write(TOP_SD_PWRSW_CTRL::PWRSW_CTRL.val(0x9));
}
PowerLevel::V30 => {
self.regs.host_ctl1_pwr_bg_wup.modify(
HOST_CTL1_PWR_BG_WUP::SD_BUS_VOL_SEL::V30
+ HOST_CTL1_PWR_BG_WUP::SD_BUS_PWR::SET,
);
self.top_regs
.sd_pwrsw_ctrl
.write(TOP_SD_PWRSW_CTRL::PWRSW_CTRL.val(0x9));
}
PowerLevel::V18 => {
self.regs.host_ctl1_pwr_bg_wup.modify(
HOST_CTL1_PWR_BG_WUP::SD_BUS_VOL_SEL::V18
+ HOST_CTL1_PWR_BG_WUP::SD_BUS_PWR::SET,
);
self.top_regs
.sd_pwrsw_ctrl
.write(TOP_SD_PWRSW_CTRL::PWRSW_CTRL.val(0xd));
if let Some(ref pinmux) = self.pinmux {
pinmux
.ioblk_g10()
.sd0_clk
.set_drive_strength(DriveStrength::Level7 as u8);
}
}
PowerLevel::Close => {
self.regs
.host_ctl1_pwr_bg_wup
.modify(HOST_CTL1_PWR_BG_WUP::SD_BUS_PWR::CLEAR);
}
}
delay_long();
}
pub fn set_clock(&self, divider: u8) {
self.regs.clk_ctl.modify(CLK_CTL::SD_CLK_EN::CLEAR);
self.regs
.clk_ctl
.modify(CLK_CTL::FREQ_SEL.val(divider as u32));
self.regs.clk_ctl.modify(CLK_CTL::INT_CLK_EN::SET);
loop {
if self.regs.clk_ctl.is_set(CLK_CTL::INT_CLK_STABLE) {
break;
}
delay_short();
}
self.regs.clk_ctl.modify(CLK_CTL::SD_CLK_EN::SET);
delay_long();
}
pub fn close_clock(&self) {
if !self.regs.present_state.is_set(PRESENT_STATE::CMD_INHIBIT)
&& !self
.regs
.present_state
.is_set(PRESENT_STATE::DAT_LINE_ACTIVE)
{
self.regs.clk_ctl.modify(CLK_CTL::SD_CLK_EN::CLEAR);
}
delay(0x100_0000);
}
pub fn clk_en(&self, en: bool) {
if en {
self.regs.clk_ctl.modify(CLK_CTL::SD_CLK_EN::SET);
} else {
self.regs.clk_ctl.modify(CLK_CTL::SD_CLK_EN::CLEAR);
}
}
fn read_buff(&self, data: &mut [u8]) -> Result<(), CmdError> {
assert!(data.len().is_multiple_of(BLOCK_SIZE));
for block_buf in data.chunks_exact_mut(BLOCK_SIZE) {
loop {
let sts = self.regs.norm_and_err_int_sts.extract();
if sts.is_set(NORM_AND_ERR_INT_STS::BUF_RRDY) {
self.regs
.norm_and_err_int_sts
.write(NORM_AND_ERR_INT_STS::BUF_RRDY::SET);
break;
}
if sts.is_set(NORM_AND_ERR_INT_STS::ERR_INT) {
self.regs
.norm_and_err_int_sts
.write(NORM_AND_ERR_INT_STS::ERR_INT::SET);
return Err(CmdError::IntError);
}
delay(1);
}
for chunk in block_buf.chunks_exact_mut(4) {
let value = self.regs.buf_data_port.get();
chunk.copy_from_slice(&value.to_le_bytes());
delay(1);
}
}
Ok(())
}
fn write_buff(&self, data: &[u8]) -> Result<(), CmdError> {
assert!(data.len() == BLOCK_SIZE);
loop {
let sts = self.regs.norm_and_err_int_sts.extract();
if sts.is_set(NORM_AND_ERR_INT_STS::BUF_WRDY) {
self.regs
.norm_and_err_int_sts
.write(NORM_AND_ERR_INT_STS::BUF_WRDY::SET);
break;
}
if sts.is_set(NORM_AND_ERR_INT_STS::ERR_INT) {
self.regs
.norm_and_err_int_sts
.write(NORM_AND_ERR_INT_STS::ERR_INT::SET);
return Err(CmdError::IntError);
}
delay(1);
}
for chunk in data.chunks_exact(4) {
let value = u32::from_le_bytes(chunk.try_into().unwrap());
self.regs.buf_data_port.set(value);
delay(1);
}
Ok(())
}
pub fn init(&self) -> Result<(), CmdError> {
if !self.is_card_inserted() {
log::warn!("SD card not inserted");
return Ok(());
}
self.reset_config();
self.power_config(PowerLevel::V18);
self.set_clock(4);
self.cmd_transfer(CommandType::CMD(0), 0, 0, false)?;
self.cmd_transfer(CommandType::CMD(8), 0x1aa, 0, false)?;
loop {
self.cmd_transfer(CommandType::CMD(55), 0, 0, false)?;
self.cmd_transfer(
CommandType::ACMD(41),
0x4000_0000 | 0x0030_0000 | (0x1FF << 15),
0,
false,
)?;
if self.get_response0() >> 31 == 1 {
break;
}
delay(0x100_0000);
}
self.cmd_transfer(CommandType::CMD(2), 0, 0, false)?;
self.cmd_transfer(CommandType::CMD(3), 0, 0, false)?;
let rca = self.get_response0() & 0xffff0000;
self.cmd_transfer(CommandType::CMD(9), rca, 0, false)?;
let csd_raw = [
self.regs.response0.get(),
self.regs.response1.get(),
self.regs.response2.get(),
self.regs.response3.get(),
];
let info = parse_sd_card_info(rca, csd_raw);
log::debug!(
"sdmmc CSD raw: r0={:#010x} r1={:#010x} r2={:#010x} r3={:#010x}",
csd_raw[0],
csd_raw[1],
csd_raw[2],
csd_raw[3],
);
log::debug!(
"sdmmc CSD struct=v{}.0 capacity={} bytes",
info.csd_structure as u32 + 1,
info.capacity_bytes
);
self.card_info.set(info);
self.cmd_transfer(CommandType::CMD(7), rca, 0, false)?;
self.cmd_transfer(CommandType::CMD(55), rca, 0, false)?;
self.cmd_transfer(CommandType::ACMD(6), 2, 0, false)?;
self.regs
.host_ctl1_pwr_bg_wup
.modify(HOST_CTL1_PWR_BG_WUP::DAT_XFER_WIDTH::Width4Bit);
log::debug!("sdmmc initialize done!");
self.clk_en(false);
Ok(())
}
}
pub unsafe fn init(
sd_base: usize,
top_base: usize,
fmux_base: usize,
ioblk_base: usize,
ioblk_grtc_base: usize,
) -> Result<Sdmmc, CmdError> {
let mut sdmmc = unsafe { Sdmmc::new(sd_base, top_base) };
sdmmc.set_pinmux(unsafe { Pinmux::new(fmux_base, ioblk_base, ioblk_grtc_base) });
sdmmc.init()?;
Ok(sdmmc)
}
pub fn parse_sd_card_info(rca: u32, csd_raw: [u32; 4]) -> SdCardInfo {
let r0 = csd_raw[0];
let r1 = csd_raw[1];
let r2 = csd_raw[2];
let r3 = csd_raw[3];
let csd_structure = ((r3 >> 22) & 0x3) as u8;
let capacity_bytes = match csd_structure {
0 => {
let read_bl_len = (r2 >> 8) & 0xF;
let c_size = ((r2 & 0x3) << 10) | ((r1 >> 22) & 0x3FF);
let c_size_mult = (r1 >> 7) & 0x7;
let mult = 1u64 << (c_size_mult + 2);
let blocknr = (c_size as u64 + 1) * mult;
let block_len = 1u64 << read_bl_len;
blocknr * block_len
}
1 => {
let c_size = (r1 >> 8) & 0x3F_FFFF;
(c_size as u64 + 1) * 512 * 1024
}
2 => {
let c_size = ((r2 & 0xF) << 24) | ((r1 >> 8) & 0xFF_FFFF);
(c_size as u64 + 1) * 512 * 1024
}
_ => 0,
};
let _ = r0;
SdCardInfo {
rca,
csd_raw,
csd_structure,
capacity_bytes,
}
}