use crate::emmc::clock_divider;
use crate::mailbox::{ClockId, Mailbox, PowerDeviceId, EXPANDER_WL_ON};
use crate::pac::{EMMC, GPIO};
use crate::timer::Timer;
const GPIO_CLK: u32 = 34;
const GPIO_CMD: u32 = 35;
const GPIO_DAT: [u32; 4] = [36, 37, 38, 39];
const GPIO_ALT_FUNCTION: u8 = 0b111;
const SD_SLOT_ALT_FUNCTION: u8 = 0b100;
const WL_ON_SETTLE_MS: u32 = 150;
const SETUP_CLOCK_HZ: u32 = 400_000;
const TRANSFER_CLOCK_HZ: u32 = 25_000_000;
const DATA_TIMEOUT_MAX: u8 = 0b1110;
const CMD_GO_IDLE: u32 = 0x0000_0000;
const CMD_SEND_REL_ADDR: u32 = 0x0302_0000;
const CMD_IO_SEND_OP_COND: u32 = 0x0502_0000;
const CMD_CARD_SELECT: u32 = 0x0703_0000;
const CMD_IO_RW_DIRECT: u32 = 0x3402_0000;
const CMD_IO_RW_EXTENDED_READ: u32 = 0x3522_0010;
const CMD_IO_RW_EXTENDED_WRITE: u32 = 0x3522_0000;
const INT_ERROR_MASK: u32 = 0x017e_8000;
const INT_CMD_DONE: u32 = 0x0000_0001;
const INT_DATA_DONE: u32 = 0x0000_0002;
const INT_WRITE_RDY: u32 = 0x0000_0010;
const INT_READ_RDY: u32 = 0x0000_0020;
const FN0: u32 = 0;
const FN1: u32 = 1;
const CCCR_IO_ENABLE: u32 = 0x02;
const CCCR_IO_READY: u32 = 0x03;
const CCCR_BUS_IFACE: u32 = 0x07;
const BUS_WIDTH_MASK: u8 = 0x03;
const BUS_WIDTH_4BIT: u8 = 0x02;
const OCR_VOLTAGE_WINDOW: u32 = 0x00ff_8000;
const IO_OP_COND_READY: u32 = 0x8000_0000;
const FUNC1_SBADDR_LOW: u32 = 0x1_000a;
const FUNC1_SBADDR_MID: u32 = 0x1_000b;
const FUNC1_SBADDR_HIGH: u32 = 0x1_000c;
const SB_WINDOW_OFFSET_MASK: u32 = 0x0000_7fff;
const SB_WINDOW_BASE_MASK: u32 = 0xffff_8000;
const SB_ACCESS_32BIT: u32 = 0x0000_8000;
const CHIPCOMMON_BASE: u32 = 0x1800_0000;
const CHIP_ID_MASK: u32 = 0x0000_ffff;
pub const BCM43438_CHIP_ID: u32 = 0xa9a6;
pub const BCM43455_CHIP_ID: u32 = 0x4345;
const CHIPCOMMON_EROM_PTR: u32 = CHIPCOMMON_BASE + 0xfc;
const CORE_ARM_CM3: u16 = 0x82a;
const CORE_ARM_CR4: u16 = 0x83e;
const CORE_SOCRAM: u16 = 0x80e;
const CORE_SDIO: u16 = 0x829;
const CORE_D11: u16 = 0x812;
const EROM_DESC_COMPONENT: u32 = 0x1;
const EROM_DESC_ADDRESS: u32 = 0x5;
const EROM_DESC_EOT: u32 = 0xf;
const EROM_ADDR_TYPE_MASK: u32 = 0xc0;
const WRAP_IOCTRL: u32 = 0x408;
const WRAP_RESETCTRL: u32 = 0x800;
const WRAP_CLK: u32 = 0x1;
const WRAP_FGC: u32 = 0x2;
const WRAP_RESET: u32 = 0x1;
const D11_PHY_CLOCK_EN: u32 = 0x4;
const D11_PHY_RESET: u32 = 0x8;
const FUNC1_CHIPCLKCSR: u32 = 0x1_000e;
const FUNC1_SDIOPULLUP: u32 = 0x1_000f;
const CLKCSR_ALP_AVAIL_REQ: u8 = 0x08;
const CLKCSR_FORCE_ALP: u8 = 0x01;
const CLKCSR_FORCE_HW_CLKREQ_OFF: u8 = 0x20;
const CLKCSR_ALP_AVAIL: u8 = 0x40;
const CLKCSR_HT_AVAIL_REQ: u8 = 0x10;
const CLKCSR_FORCE_HT: u8 = 0x02;
const CLKCSR_HT_AVAIL: u8 = 0x80;
const FN2: u32 = 2;
const SDIO_CORE_SB_MBOX_DATA: u32 = 0x48;
const SDPCM_PROT_VERSION: u32 = 4;
const FIRMWARE_CHUNK_BYTES: usize = 512;
const MAX_TRANSFER_WORDS: usize = 16;
const NVRAM_MAX_BYTES: usize = 2048;
const SOCRAM_COREINFO: u32 = 0x00;
const SOCRAM_BANKIDX: u32 = 0x10;
const SOCRAM_BANKINFO: u32 = 0x40;
const SOCRAM_BANKPDA: u32 = 0x44;
const SOCRAM_BANKINFO_SIZE_MASK: u32 = 0x7f;
const SOCRAM_BANK_SIZE_UNIT: u32 = 8192;
const SOCRAM_COREINFO_BANKS_MASK: u32 = 0xf0;
const SOCRAM_REMAP_BANK: u32 = 3;
const ARMCR4_CAP: u32 = 0x04;
const ARMCR4_BANKIDX: u32 = 0x40;
const ARMCR4_BANKINFO: u32 = 0x44;
const ARMCR4_CAP_BANKS_A_MASK: u32 = 0x0000_000f;
const ARMCR4_CAP_BANKS_B_MASK: u32 = 0x0000_00f0;
const ARMCR4_CAP_BANKS_B_SHIFT: u32 = 4;
const ARMCR4_BANKINFO_SIZE_MASK: u32 = 0x0000_003f;
const ARMCR4_BANK_SIZE_UNIT: u32 = 8192;
const ARMCR4_IOCTL_CPUHALT: u32 = 0x20;
const ARMCR4_RESET_VECTOR_ADDR: u32 = 0;
const BCM43455_RAM_BASE: u32 = 0x0019_8000;
const SDIO_CORE_INTSTATUS: u32 = 0x20;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
Timeout,
CardError {
interrupt: u32,
command: u32,
},
ControllerPowerOnFailed,
ClockQueryFailed,
OpCondTimeout,
Function1NotReady,
CoreNotFound,
UnsupportedChip {
chip_id: u32,
},
SocramNotUp,
FirmwareNotReady,
DataStalled {
stage: u8,
interrupt: u32,
status: u32,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ChipCores {
pub cpu: Cpu,
pub d11_wrapper: u32,
pub sdio_base: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Cpu {
CortexM3 {
arm_wrapper: u32,
socram_base: u32,
socram_wrapper: u32,
},
Cr4 {
arm_base: u32,
arm_wrapper: u32,
ram_base: u32,
},
}
pub struct Sdio {
emmc: EMMC,
rca: u32,
window_base: u32,
sdio_core_base: u32,
}
impl Sdio {
pub fn init(
gpio: &GPIO,
emmc: EMMC,
mailbox: &mut Mailbox,
timer: &Timer,
) -> Result<Self, Error> {
if !matches!(
mailbox.set_power_state(PowerDeviceId::SdCard, true),
Ok(true)
) {
return Err(Error::ControllerPowerOnFailed);
}
let _ = mailbox.set_expander_gpio(EXPANDER_WL_ON, true);
timer.delay_ms(WL_ON_SETTLE_MS);
route_gpio_to_emmc(gpio);
let base_clock_hz = mailbox
.clock_rate_hz(ClockId::Emmc)
.map_err(|_| Error::ClockQueryFailed)?;
emmc.control0().reset();
emmc.control1().modify(|_, w| w.srst_hc().set_bit());
wait_for(timer, 100_000, || {
!emmc.control1().read().srst_hc().bit_is_set()
})?;
emmc.control1().modify(|_, w| unsafe {
w.clk_intlen()
.set_bit()
.data_tounit()
.bits(DATA_TIMEOUT_MAX)
});
timer.delay_ms(10);
set_clock(&emmc, base_clock_hz, SETUP_CLOCK_HZ, timer)?;
emmc.irpt_mask().write(|w| unsafe { w.bits(0xffff_ffff) });
let mut sdio = Self {
emmc,
rca: 0,
window_base: 0,
sdio_core_base: 0,
};
sdio.enumerate(timer)?;
sdio.enable_function1(timer)?;
sdio.set_bus_width_4bit(timer)?;
set_clock(&sdio.emmc, base_clock_hz, TRANSFER_CLOCK_HZ, timer)?;
Ok(sdio)
}
fn enumerate(&mut self, timer: &Timer) -> Result<(), Error> {
self.command(CMD_GO_IDLE, 0, timer)?;
self.command(CMD_IO_SEND_OP_COND, 0, timer)?;
let start = timer.now_micros();
loop {
let response = self.command(CMD_IO_SEND_OP_COND, OCR_VOLTAGE_WINDOW, timer)?;
if response & IO_OP_COND_READY != 0 {
break;
}
if timer.now_micros() - start > 1_000_000 {
return Err(Error::OpCondTimeout);
}
timer.delay_ms(10);
}
self.rca = self.command(CMD_SEND_REL_ADDR, 0, timer)? & 0xffff_0000;
self.command(CMD_CARD_SELECT, self.rca, timer)?;
Ok(())
}
fn enable_function1(&self, timer: &Timer) -> Result<(), Error> {
let function1_bit = 1u8 << FN1;
self.cmd52_write(FN0, CCCR_IO_ENABLE, function1_bit, timer)?;
let start = timer.now_micros();
loop {
let ready = self.cmd52_read(FN0, CCCR_IO_READY, timer)?;
if ready & function1_bit != 0 {
return Ok(());
}
if timer.now_micros() - start > 1_000_000 {
return Err(Error::Function1NotReady);
}
timer.delay_ms(10);
}
}
fn set_bus_width_4bit(&self, timer: &Timer) -> Result<(), Error> {
let ctrl = self.cmd52_read(FN0, CCCR_BUS_IFACE, timer)?;
let ctrl = (ctrl & !BUS_WIDTH_MASK) | BUS_WIDTH_4BIT;
self.cmd52_write(FN0, CCCR_BUS_IFACE, ctrl, timer)?;
self.emmc
.control0()
.modify(|_, w| w.hctl_dwidth().set_bit());
Ok(())
}
pub fn chip_id(&mut self, timer: &Timer) -> Result<u32, Error> {
let id = self.backplane_read32(CHIPCOMMON_BASE, timer)?;
Ok(id & CHIP_ID_MASK)
}
pub fn scan_cores(&mut self, timer: &Timer) -> Result<ChipCores, Error> {
let mut erom = self.backplane_read32(CHIPCOMMON_EROM_PTR, timer)? & 0xffff_f000;
let mut arm_wrapper = 0;
let mut cr4_base = 0;
let mut cr4_wrapper = 0;
let mut d11_wrapper = 0;
let mut socram_base = 0;
let mut socram_wrapper = 0;
let mut sdio_base = 0;
let mut core_id = 0u16;
let mut got_base = false;
let mut got_wrapper = false;
for _ in 0..512 {
let desc = self.backplane_read32(erom, timer)?;
erom += 4;
match desc & 0xf {
EROM_DESC_EOT => break,
EROM_DESC_COMPONENT => {
core_id = ((desc >> 8) & 0xfff) as u16;
let _cib = self.backplane_read32(erom, timer)?;
erom += 4;
got_base = false;
got_wrapper = false;
}
EROM_DESC_ADDRESS => {
let addr = desc & 0xffff_f000;
let addr_type = desc & EROM_ADDR_TYPE_MASK;
if addr_type == 0 && !got_base {
got_base = true;
match core_id {
CORE_SOCRAM => socram_base = addr,
CORE_SDIO => sdio_base = addr,
CORE_ARM_CR4 => cr4_base = addr,
_ => {}
}
} else if (addr_type == 0x80 || addr_type == 0xc0) && !got_wrapper {
got_wrapper = true;
match core_id {
CORE_ARM_CM3 => arm_wrapper = addr,
CORE_ARM_CR4 => cr4_wrapper = addr,
CORE_D11 => d11_wrapper = addr,
CORE_SOCRAM => socram_wrapper = addr,
_ => {}
}
}
if desc & 0x8 != 0 {
self.backplane_read32(erom, timer)?;
erom += 4;
}
if desc & 0x30 == 0x30 {
let size_desc = self.backplane_read32(erom, timer)?;
erom += 4;
if size_desc & 0x8 != 0 {
self.backplane_read32(erom, timer)?;
erom += 4;
}
}
}
_ => {}
}
}
let cpu = if cr4_wrapper != 0 && cr4_base != 0 {
Cpu::Cr4 {
arm_base: cr4_base,
arm_wrapper: cr4_wrapper,
ram_base: match self.chip_id(timer)? {
BCM43455_CHIP_ID => BCM43455_RAM_BASE,
chip_id => return Err(Error::UnsupportedChip { chip_id }),
},
}
} else if arm_wrapper != 0 && socram_base != 0 && socram_wrapper != 0 {
Cpu::CortexM3 {
arm_wrapper,
socram_base,
socram_wrapper,
}
} else {
return Err(Error::CoreNotFound);
};
if d11_wrapper == 0 || sdio_base == 0 {
return Err(Error::CoreNotFound);
}
Ok(ChipCores {
cpu,
d11_wrapper,
sdio_base,
})
}
pub fn prepare_download(&mut self, timer: &Timer) -> Result<ChipCores, Error> {
let cores = self.scan_cores(timer)?;
self.force_alp_clock(timer)?;
match cores.cpu {
Cpu::CortexM3 { arm_wrapper, .. } => {
self.core_disable(arm_wrapper, 0, 0, timer)?;
}
Cpu::Cr4 { arm_wrapper, .. } => {
let halt =
self.backplane_read32(arm_wrapper + WRAP_IOCTRL, timer)? & ARMCR4_IOCTL_CPUHALT;
self.core_reset(
arm_wrapper,
halt,
ARMCR4_IOCTL_CPUHALT,
ARMCR4_IOCTL_CPUHALT,
timer,
)?;
}
}
self.core_reset(
cores.d11_wrapper,
D11_PHY_RESET | D11_PHY_CLOCK_EN,
D11_PHY_CLOCK_EN,
D11_PHY_CLOCK_EN,
timer,
)?;
if let Cpu::CortexM3 {
socram_base,
socram_wrapper,
..
} = cores.cpu
{
self.core_reset(socram_wrapper, 0, 0, 0, timer)?;
self.backplane_write32(socram_base + SOCRAM_BANKIDX, SOCRAM_REMAP_BANK, timer)?;
self.backplane_write32(socram_base + SOCRAM_BANKPDA, 0, timer)?;
}
Ok(cores)
}
pub fn ram_size(&mut self, cores: &ChipCores, timer: &Timer) -> Result<u32, Error> {
let (base, banks, idx_reg, info_reg, size_mask, unit) = match cores.cpu {
Cpu::CortexM3 { socram_base, .. } => {
let coreinfo = self.backplane_read32(socram_base + SOCRAM_COREINFO, timer)?;
(
socram_base,
(coreinfo & SOCRAM_COREINFO_BANKS_MASK) >> 4,
SOCRAM_BANKIDX,
SOCRAM_BANKINFO,
SOCRAM_BANKINFO_SIZE_MASK,
SOCRAM_BANK_SIZE_UNIT,
)
}
Cpu::Cr4 { arm_base, .. } => {
let cap = self.backplane_read32(arm_base + ARMCR4_CAP, timer)?;
let banks_a = cap & ARMCR4_CAP_BANKS_A_MASK;
let banks_b = (cap & ARMCR4_CAP_BANKS_B_MASK) >> ARMCR4_CAP_BANKS_B_SHIFT;
(
arm_base,
banks_a + banks_b,
ARMCR4_BANKIDX,
ARMCR4_BANKINFO,
ARMCR4_BANKINFO_SIZE_MASK,
ARMCR4_BANK_SIZE_UNIT,
)
}
};
let mut total = 0;
for bank in 0..banks {
self.backplane_write32(base + idx_reg, bank, timer)?;
let bankinfo = self.backplane_read32(base + info_reg, timer)?;
total += ((bankinfo & size_mask) + 1) * unit;
}
Ok(total)
}
pub fn load_firmware(
&mut self,
firmware: &[u8],
nvram: &[u8],
timer: &Timer,
) -> Result<(), Error> {
let cores = self.prepare_download(timer)?;
self.sdio_core_base = cores.sdio_base;
let ram_size = self.ram_size(&cores, timer)?;
let ram_base = match cores.cpu {
Cpu::CortexM3 { .. } => 0,
Cpu::Cr4 { ram_base, .. } => ram_base,
};
self.download_to_ram(ram_base, firmware, timer)?;
self.write_nvram(ram_base, ram_size, nvram, timer)?;
match cores.cpu {
Cpu::CortexM3 {
arm_wrapper,
socram_wrapper,
..
} => {
if !self.core_is_up(socram_wrapper, timer)? {
return Err(Error::SocramNotUp);
}
self.core_reset(arm_wrapper, 0, 0, 0, timer)?;
}
Cpu::Cr4 { arm_wrapper, .. } => {
let mut vector = [0u8; 4];
let n = firmware.len().min(4);
vector[..n].copy_from_slice(&firmware[..n]);
self.backplane_write32(
ARMCR4_RESET_VECTOR_ADDR,
u32::from_le_bytes(vector),
timer,
)?;
self.backplane_write32(cores.sdio_base + SDIO_CORE_INTSTATUS, 0xffff_ffff, timer)?;
self.core_reset(arm_wrapper, ARMCR4_IOCTL_CPUHALT, 0, 0, timer)?;
}
}
self.force_ht_clock(timer)?;
self.backplane_write32(
cores.sdio_base + SDIO_CORE_SB_MBOX_DATA,
SDPCM_PROT_VERSION << 16,
timer,
)?;
self.wait_function2_ready(timer)
}
fn download_to_ram(&mut self, base: u32, bytes: &[u8], timer: &Timer) -> Result<(), Error> {
let mut addr = base;
for chunk in bytes.chunks(FIRMWARE_CHUNK_BYTES) {
let mut words = [0u32; FIRMWARE_CHUNK_BYTES / 4];
let word_count = chunk.len().div_ceil(4);
for (i, word) in words[..word_count].iter_mut().enumerate() {
let start = i * 4;
let end = (start + 4).min(chunk.len());
let mut b = [0u8; 4];
b[..end - start].copy_from_slice(&chunk[start..end]);
*word = u32::from_le_bytes(b);
}
self.backplane_write(addr, &words[..word_count], timer)?;
addr += chunk.len() as u32;
}
Ok(())
}
fn write_nvram(
&mut self,
ram_base: u32,
ram_size: u32,
nvram: &[u8],
timer: &Timer,
) -> Result<(), Error> {
let mut buf = [0u8; NVRAM_MAX_BYTES];
let len = condense_nvram(nvram, &mut buf);
let ram_top = ram_base + ram_size;
let vars_addr = ram_top - len as u32 - 4;
self.download_to_ram(vars_addr, &buf[..len], timer)?;
let word_count = (len / 4) as u32;
let token = (!word_count << 16) | (word_count & 0xffff);
self.backplane_write32(ram_top - 4, token, timer)?;
Ok(())
}
fn core_is_up(&mut self, wrap: u32, timer: &Timer) -> Result<bool, Error> {
let ioctrl = self.backplane_read32(wrap + WRAP_IOCTRL, timer)?;
let resetctrl = self.backplane_read32(wrap + WRAP_RESETCTRL, timer)?;
Ok(resetctrl & WRAP_RESET == 0 && ioctrl & (WRAP_FGC | WRAP_CLK) == WRAP_CLK)
}
fn force_ht_clock(&self, timer: &Timer) -> Result<(), Error> {
self.cmd52_write(FN1, FUNC1_CHIPCLKCSR, CLKCSR_HT_AVAIL_REQ, timer)?;
let start = timer.now_micros();
loop {
let csr = self.cmd52_read(FN1, FUNC1_CHIPCLKCSR, timer)?;
if csr & CLKCSR_HT_AVAIL != 0 {
break;
}
if timer.now_micros() - start > 5_000_000 {
return Err(Error::Timeout);
}
timer.delay_ms(1);
}
self.cmd52_write(
FN1,
FUNC1_CHIPCLKCSR,
CLKCSR_HT_AVAIL_REQ | CLKCSR_FORCE_HT,
timer,
)?;
Ok(())
}
fn wait_function2_ready(&self, timer: &Timer) -> Result<(), Error> {
let function2_bit = 1u8 << FN2;
let enable = self.cmd52_read(FN0, CCCR_IO_ENABLE, timer)? | function2_bit;
self.cmd52_write(FN0, CCCR_IO_ENABLE, enable, timer)?;
let start = timer.now_micros();
loop {
let ready = self.cmd52_read(FN0, CCCR_IO_READY, timer)?;
if ready & function2_bit != 0 {
return Ok(());
}
if timer.now_micros() - start > 4_000_000 {
return Err(Error::FirmwareNotReady);
}
timer.delay_ms(10);
}
}
fn force_alp_clock(&self, timer: &Timer) -> Result<(), Error> {
self.cmd52_write(
FN1,
FUNC1_CHIPCLKCSR,
CLKCSR_FORCE_HW_CLKREQ_OFF | CLKCSR_ALP_AVAIL_REQ,
timer,
)?;
let start = timer.now_micros();
loop {
let csr = self.cmd52_read(FN1, FUNC1_CHIPCLKCSR, timer)?;
if csr & CLKCSR_ALP_AVAIL != 0 {
break;
}
if timer.now_micros() - start > 1_000_000 {
return Err(Error::Timeout);
}
timer.delay_ms(1);
}
self.cmd52_write(
FN1,
FUNC1_CHIPCLKCSR,
CLKCSR_FORCE_HW_CLKREQ_OFF | CLKCSR_FORCE_ALP,
timer,
)?;
timer.delay_us(65);
self.cmd52_write(FN1, FUNC1_SDIOPULLUP, 0, timer)?;
Ok(())
}
pub fn backplane_write32(&mut self, addr: u32, value: u32, timer: &Timer) -> Result<(), Error> {
self.backplane_write(addr, &[value], timer)
}
pub fn backplane_write(&mut self, addr: u32, buf: &[u32], timer: &Timer) -> Result<(), Error> {
let mut addr = addr;
for chunk in buf.chunks(MAX_TRANSFER_WORDS) {
self.write_block(addr, chunk, timer)?;
addr += (chunk.len() * 4) as u32;
}
Ok(())
}
fn write_block(&mut self, addr: u32, buf: &[u32], timer: &Timer) -> Result<(), Error> {
const ATTEMPTS: u32 = 4;
for attempt in 0..ATTEMPTS {
match self.backplane_write_attempt(addr, buf, timer) {
Ok(()) => return Ok(()),
Err(Error::CardError { .. }) if attempt + 1 < ATTEMPTS => {
self.reset_data_circuit(timer);
}
Err(e) => return Err(e),
}
}
Err(Error::Timeout)
}
fn backplane_write_attempt(
&mut self,
addr: u32,
buf: &[u32],
timer: &Timer,
) -> Result<(), Error> {
debug_assert!(buf.len() <= MAX_TRANSFER_WORDS);
self.ensure_window(addr, timer)?;
let byte_count = (buf.len() * 4) as u32;
self.wait_data_ready(timer)?;
self.emmc
.blksizecnt()
.write(|w| unsafe { w.blksize().bits(byte_count as u16).blkcnt().bits(1) });
wait_for(timer, 100_000, || {
self.emmc.status().read().cmd_inhibit().bit_is_clear()
})?;
let stale = self.emmc.interrupt().read().bits();
self.emmc.interrupt().write(|w| unsafe { w.bits(stale) });
let offset = (addr & SB_WINDOW_OFFSET_MASK) | SB_ACCESS_32BIT;
let arg = cmd53_arg(true, FN1, false, true, offset, byte_count & 0x1ff);
self.emmc.arg1().write(|w| unsafe { w.bits(arg) });
self.emmc
.cmdtm()
.write(|w| unsafe { w.bits(CMD_IO_RW_EXTENDED_WRITE) });
self.wait_data_int(INT_CMD_DONE, 0, timer)?;
self.wait_data_int(INT_WRITE_RDY, 1, timer)?;
for &word in buf {
self.emmc.data().write(|w| unsafe { w.bits(word) });
}
self.wait_data_int(INT_DATA_DONE, 2, timer)?;
Ok(())
}
fn wait_data_int(&self, mask: u32, stage: u8, timer: &Timer) -> Result<(), Error> {
let start = timer.now_micros();
loop {
let interrupt = self.emmc.interrupt().read().bits();
if interrupt & INT_ERROR_MASK != 0 {
self.emmc
.interrupt()
.write(|w| unsafe { w.bits(interrupt & INT_ERROR_MASK) });
return Err(Error::CardError {
interrupt,
command: stage as u32,
});
}
if interrupt & mask != 0 {
self.emmc.interrupt().write(|w| unsafe { w.bits(mask) });
return Ok(());
}
if timer.now_micros() - start > 1_000_000 {
let status = self.emmc.status().read().bits();
return Err(Error::DataStalled {
stage,
interrupt,
status,
});
}
}
}
fn reset_data_circuit(&self, timer: &Timer) {
self.emmc.control1().modify(|_, w| w.srst_data().set_bit());
let _ = wait_for(timer, 100_000, || {
!self.emmc.control1().read().srst_data().bit_is_set()
});
let stale = self.emmc.interrupt().read().bits();
self.emmc.interrupt().write(|w| unsafe { w.bits(stale) });
}
pub fn sdio_core_base(&self) -> u32 {
self.sdio_core_base
}
pub fn f2_write(&mut self, bytes: &[u8], timer: &Timer) -> Result<(), Error> {
debug_assert!(bytes.len().is_multiple_of(4));
for chunk in bytes.chunks(MAX_TRANSFER_WORDS * 4) {
let mut words = [0u32; MAX_TRANSFER_WORDS];
let word_count = chunk.len() / 4;
for (i, word) in words[..word_count].iter_mut().enumerate() {
*word = u32::from_le_bytes([
chunk[i * 4],
chunk[i * 4 + 1],
chunk[i * 4 + 2],
chunk[i * 4 + 3],
]);
}
self.f2_write_chunk(&words[..word_count], timer)?;
}
Ok(())
}
pub fn f2_read(&mut self, bytes: &mut [u8], timer: &Timer) -> Result<(), Error> {
debug_assert!(bytes.len().is_multiple_of(4));
for chunk in bytes.chunks_mut(MAX_TRANSFER_WORDS * 4) {
let mut words = [0u32; MAX_TRANSFER_WORDS];
let word_count = chunk.len() / 4;
self.f2_read_chunk(&mut words[..word_count], timer)?;
for (i, word) in words[..word_count].iter().enumerate() {
chunk[i * 4..i * 4 + 4].copy_from_slice(&word.to_le_bytes());
}
}
Ok(())
}
fn f2_write_chunk(&mut self, words: &[u32], timer: &Timer) -> Result<(), Error> {
let byte_count = (words.len() * 4) as u32;
self.wait_data_ready(timer)?;
self.emmc
.blksizecnt()
.write(|w| unsafe { w.blksize().bits(byte_count as u16).blkcnt().bits(1) });
wait_for(timer, 100_000, || {
self.emmc.status().read().cmd_inhibit().bit_is_clear()
})?;
let stale = self.emmc.interrupt().read().bits();
self.emmc.interrupt().write(|w| unsafe { w.bits(stale) });
let arg = cmd53_arg(true, FN2, false, false, 0, byte_count & 0x1ff);
self.emmc.arg1().write(|w| unsafe { w.bits(arg) });
self.emmc
.cmdtm()
.write(|w| unsafe { w.bits(CMD_IO_RW_EXTENDED_WRITE) });
self.wait_data_int(INT_CMD_DONE, 0, timer)?;
self.wait_data_int(INT_WRITE_RDY, 1, timer)?;
for &word in words {
self.emmc.data().write(|w| unsafe { w.bits(word) });
}
self.wait_data_int(INT_DATA_DONE, 2, timer)?;
Ok(())
}
fn f2_read_chunk(&mut self, out: &mut [u32], timer: &Timer) -> Result<(), Error> {
let byte_count = (out.len() * 4) as u32;
self.wait_data_ready(timer)?;
self.emmc
.blksizecnt()
.write(|w| unsafe { w.blksize().bits(byte_count as u16).blkcnt().bits(1) });
wait_for(timer, 100_000, || {
self.emmc.status().read().cmd_inhibit().bit_is_clear()
})?;
let stale = self.emmc.interrupt().read().bits();
self.emmc.interrupt().write(|w| unsafe { w.bits(stale) });
let arg = cmd53_arg(false, FN2, false, false, 0, byte_count & 0x1ff);
self.emmc.arg1().write(|w| unsafe { w.bits(arg) });
self.emmc
.cmdtm()
.write(|w| unsafe { w.bits(CMD_IO_RW_EXTENDED_READ) });
self.wait_data_int(INT_CMD_DONE, 0, timer)?;
self.wait_data_int(INT_READ_RDY, 1, timer)?;
for word in out {
*word = self.emmc.data().read().bits();
}
Ok(())
}
fn core_disable(
&mut self,
wrap: u32,
pre_ioctrl: u32,
reset_ioctrl: u32,
timer: &Timer,
) -> Result<(), Error> {
if self.backplane_read32(wrap + WRAP_RESETCTRL, timer)? & WRAP_RESET != 0 {
self.backplane_write32(
wrap + WRAP_IOCTRL,
reset_ioctrl | WRAP_FGC | WRAP_CLK,
timer,
)?;
self.backplane_read32(wrap + WRAP_IOCTRL, timer)?;
return Ok(());
}
self.backplane_write32(wrap + WRAP_IOCTRL, pre_ioctrl | WRAP_FGC | WRAP_CLK, timer)?;
self.backplane_read32(wrap + WRAP_IOCTRL, timer)?;
self.backplane_write32(wrap + WRAP_RESETCTRL, WRAP_RESET, timer)?;
timer.delay_us(10);
let start = timer.now_micros();
while self.backplane_read32(wrap + WRAP_RESETCTRL, timer)? & WRAP_RESET == 0 {
if timer.now_micros() - start > 100_000 {
return Err(Error::Timeout);
}
}
self.backplane_write32(
wrap + WRAP_IOCTRL,
reset_ioctrl | WRAP_FGC | WRAP_CLK,
timer,
)?;
self.backplane_read32(wrap + WRAP_IOCTRL, timer)?;
Ok(())
}
fn core_reset(
&mut self,
wrap: u32,
pre_ioctrl: u32,
reset_ioctrl: u32,
post_ioctrl: u32,
timer: &Timer,
) -> Result<(), Error> {
self.core_disable(wrap, pre_ioctrl, reset_ioctrl, timer)?;
let start = timer.now_micros();
loop {
self.backplane_write32(wrap + WRAP_RESETCTRL, 0, timer)?;
if self.backplane_read32(wrap + WRAP_RESETCTRL, timer)? & WRAP_RESET == 0 {
break;
}
if timer.now_micros() - start > 100_000 {
return Err(Error::Timeout);
}
timer.delay_us(50);
}
self.backplane_write32(wrap + WRAP_IOCTRL, post_ioctrl | WRAP_CLK, timer)?;
self.backplane_read32(wrap + WRAP_IOCTRL, timer)?;
Ok(())
}
pub fn backplane_read32(&mut self, addr: u32, timer: &Timer) -> Result<u32, Error> {
self.ensure_window(addr, timer)?;
let offset = (addr & SB_WINDOW_OFFSET_MASK) | SB_ACCESS_32BIT;
let mut value = 0u32;
for i in 0..4 {
let byte = self.cmd52_read(FN1, offset + i, timer)?;
value |= (byte as u32) << (8 * i);
}
Ok(value)
}
pub fn backplane_read(
&mut self,
addr: u32,
buf: &mut [u32],
timer: &Timer,
) -> Result<(), Error> {
let mut addr = addr;
for chunk in buf.chunks_mut(MAX_TRANSFER_WORDS) {
self.read_block(addr, chunk, timer)?;
addr += (chunk.len() * 4) as u32;
}
Ok(())
}
fn read_block(&mut self, addr: u32, buf: &mut [u32], timer: &Timer) -> Result<(), Error> {
debug_assert!(buf.len() <= MAX_TRANSFER_WORDS);
self.ensure_window(addr, timer)?;
let byte_count = (buf.len() * 4) as u32;
self.wait_data_ready(timer)?;
self.emmc
.blksizecnt()
.write(|w| unsafe { w.blksize().bits(byte_count as u16).blkcnt().bits(1) });
let offset = (addr & SB_WINDOW_OFFSET_MASK) | SB_ACCESS_32BIT;
let arg = cmd53_arg(false, FN1, false, true, offset, byte_count & 0x1ff);
self.command(CMD_IO_RW_EXTENDED_READ, arg, timer)?;
self.wait_interrupt(INT_READ_RDY, CMD_IO_RW_EXTENDED_READ, timer)?;
for word in buf {
*word = self.emmc.data().read().bits();
}
Ok(())
}
fn ensure_window(&mut self, addr: u32, timer: &Timer) -> Result<(), Error> {
let base = addr & SB_WINDOW_BASE_MASK;
if base != self.window_base || self.window_base == 0 {
self.set_backplane_window(base, timer)?;
self.window_base = base;
}
Ok(())
}
fn wait_data_ready(&self, timer: &Timer) -> Result<(), Error> {
wait_for(timer, 100_000, || {
self.emmc.status().read().dat_inhibit().bit_is_clear()
})
}
fn set_backplane_window(&self, base: u32, timer: &Timer) -> Result<(), Error> {
self.cmd52_write(FN1, FUNC1_SBADDR_LOW, (base >> 8) as u8, timer)?;
self.cmd52_write(FN1, FUNC1_SBADDR_MID, (base >> 16) as u8, timer)?;
self.cmd52_write(FN1, FUNC1_SBADDR_HIGH, (base >> 24) as u8, timer)?;
Ok(())
}
pub fn cmd52_read(&self, func: u32, addr: u32, timer: &Timer) -> Result<u8, Error> {
let response = self.command(CMD_IO_RW_DIRECT, cmd52_arg(false, func, addr, 0), timer)?;
Ok(response as u8)
}
pub fn cmd52_write(&self, func: u32, addr: u32, data: u8, timer: &Timer) -> Result<(), Error> {
self.command(CMD_IO_RW_DIRECT, cmd52_arg(true, func, addr, data), timer)?;
Ok(())
}
fn command(&self, code: u32, arg: u32, timer: &Timer) -> Result<u32, Error> {
wait_for(timer, 100_000, || {
self.emmc.status().read().cmd_inhibit().bit_is_clear()
})?;
let stale = self.emmc.interrupt().read().bits();
self.emmc.interrupt().write(|w| unsafe { w.bits(stale) });
self.emmc.arg1().write(|w| unsafe { w.bits(arg) });
self.emmc.cmdtm().write(|w| unsafe { w.bits(code) });
self.wait_interrupt(INT_CMD_DONE, code, timer)?;
Ok(self.emmc.resp0().read().bits())
}
fn wait_interrupt(&self, mask: u32, command: u32, timer: &Timer) -> Result<u32, Error> {
let full_mask = mask | INT_ERROR_MASK;
let start = timer.now_micros();
let interrupt = loop {
let interrupt = self.emmc.interrupt().read().bits();
if interrupt & full_mask != 0 {
break interrupt;
}
if timer.now_micros() - start > 1_000_000 {
return Err(Error::Timeout);
}
};
self.emmc
.interrupt()
.write(|w| unsafe { w.bits(interrupt) });
if interrupt & INT_ERROR_MASK != 0 {
return Err(Error::CardError { interrupt, command });
}
Ok(interrupt)
}
}
fn cmd52_arg(write: bool, func: u32, addr: u32, data: u8) -> u32 {
((write as u32) << 31) | ((func & 0x7) << 28) | ((addr & 0x1_ffff) << 9) | (data as u32)
}
fn condense_nvram(raw: &[u8], out: &mut [u8]) -> usize {
let mut n = 0;
let mut line_start = 0;
let mut in_comment = false;
for &byte in raw {
if n + 6 >= out.len() {
break;
}
match byte {
b'\r' => {}
b'\n' => {
in_comment = false;
if n > line_start {
out[n] = 0;
n += 1;
line_start = n;
}
}
b'#' => in_comment = true,
_ if in_comment => {}
_ => {
out[n] = byte;
n += 1;
}
}
}
if n > line_start {
out[n] = 0;
n += 1;
}
out[n] = 0;
n += 1;
while !n.is_multiple_of(4) {
out[n] = 0;
n += 1;
}
n
}
fn cmd53_arg(
write: bool,
func: u32,
block_mode: bool,
incrementing: bool,
addr: u32,
count: u32,
) -> u32 {
((write as u32) << 31)
| ((func & 0x7) << 28)
| ((block_mode as u32) << 27)
| ((incrementing as u32) << 26)
| ((addr & 0x1_ffff) << 9)
| (count & 0x1ff)
}
fn route_gpio_to_emmc(gpio: &GPIO) {
gpio.gpfsel4().modify(|_, w| {
w.fsel48()
.bits(SD_SLOT_ALT_FUNCTION)
.fsel49()
.bits(SD_SLOT_ALT_FUNCTION)
});
gpio.gpfsel5().modify(|_, w| {
w.fsel50()
.bits(SD_SLOT_ALT_FUNCTION)
.fsel51()
.bits(SD_SLOT_ALT_FUNCTION)
.fsel52()
.bits(SD_SLOT_ALT_FUNCTION)
.fsel53()
.bits(SD_SLOT_ALT_FUNCTION)
});
gpio.gpfsel3().modify(|_, w| {
w.fsel34()
.bits(GPIO_ALT_FUNCTION)
.fsel35()
.bits(GPIO_ALT_FUNCTION)
.fsel36()
.bits(GPIO_ALT_FUNCTION)
.fsel37()
.bits(GPIO_ALT_FUNCTION)
.fsel38()
.bits(GPIO_ALT_FUNCTION)
.fsel39()
.bits(GPIO_ALT_FUNCTION)
});
let pull_up_mask = (1 << (GPIO_CMD - 32))
| GPIO_DAT
.iter()
.fold(0, |mask, pin| mask | (1 << (pin - 32)));
let clk_mask = 1 << (GPIO_CLK - 32);
crate::gpio::set_pull_bank(gpio, 1, pull_up_mask, crate::gpio::Pull::Up);
crate::gpio::set_pull_bank(gpio, 1, clk_mask, crate::gpio::Pull::None);
}
fn set_clock(emmc: &EMMC, base_hz: u32, target_hz: u32, timer: &Timer) -> Result<(), Error> {
wait_for(timer, 100_000, || {
let status = emmc.status().read();
status.cmd_inhibit().bit_is_clear() && status.dat_inhibit().bit_is_clear()
})?;
emmc.control1().modify(|_, w| w.clk_en().clear_bit());
timer.delay_ms(10);
let (freq8, freq_ms2) = clock_divider(base_hz, target_hz);
emmc.control1()
.modify(|_, w| unsafe { w.clk_freq8().bits(freq8).clk_freq_ms2().bits(freq_ms2) });
timer.delay_ms(10);
emmc.control1().modify(|_, w| w.clk_en().set_bit());
timer.delay_ms(10);
wait_for(timer, 100_000, || {
emmc.control1().read().clk_stable().bit_is_set()
})
}
fn wait_for(
timer: &Timer,
timeout_us: u64,
mut condition: impl FnMut() -> bool,
) -> Result<(), Error> {
let start = timer.now_micros();
loop {
if condition() {
return Ok(());
}
if timer.now_micros() - start > timeout_us {
return Err(Error::Timeout);
}
}
}