feat: rpi3 added again with simple emmc read support
This commit is contained in:
@@ -40,6 +40,14 @@ QEMU_OPTS+=-kernel $(O)/kernel.bin \
|
||||
-display none \
|
||||
-net none
|
||||
endif
|
||||
ifeq ($(MACH),rpi3)
|
||||
QEMU_OPTS+=-kernel $(O)/kernel.bin \
|
||||
-initrd $(O)/initrd.img \
|
||||
-M raspi3b \
|
||||
-serial chardev:serial1 \
|
||||
-display none \
|
||||
-net none
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(QEMU_DINT),1)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"arch": "aarch64",
|
||||
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
||||
"disable-redzone": true,
|
||||
"executables": true,
|
||||
"features": "+strict-align,+neon,+fp-armv8",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"llvm-target": "aarch64-unknown-none",
|
||||
"max-atomic-width": 128,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "static",
|
||||
"target-pointer-width": "64",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [ "-Tetc/aarch64-rpi3.ld" ]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
ENTRY(_entry);
|
||||
|
||||
KERNEL_OFFSET = 0xFFFFFF8000000000;
|
||||
BASE_OFFSET = 0x80000;
|
||||
|
||||
SECTIONS {
|
||||
. = BASE_OFFSET;
|
||||
|
||||
.text.lower : {
|
||||
*(.text._entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_OFFSET;
|
||||
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
.text : AT(. - KERNEL_OFFSET) {
|
||||
*(.text._entry_upper)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_OFFSET) {
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : AT(. - KERNEL_OFFSET) {
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_OFFSET);
|
||||
PROVIDE(__bss_start = .);
|
||||
.bss : AT(. - KERNEL_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_OFFSET);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
PROVIDE(__kernel_end_phys = . - KERNEL_OFFSET);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
use error::Errno;
|
||||
|
||||
pub trait BlockDevice {
|
||||
fn read(&self, pos: usize, buf: &mut [u8]) -> Result<(), Errno>;
|
||||
fn write(&self, pos: usize, buf: &[u8]) -> Result<(), Errno>;
|
||||
// TODO ioctl and stuff
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod block;
|
||||
pub use block::BlockDevice;
|
||||
pub mod fs;
|
||||
pub use fs::Filesystem;
|
||||
pub mod stat;
|
||||
|
||||
+1
-1
@@ -28,4 +28,4 @@ pl031 = []
|
||||
|
||||
mach_qemu = ["pl011", "pl031"]
|
||||
mach_orangepi3 = []
|
||||
mach_rpi3 = []
|
||||
mach_rpi3 = ["pl011"]
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
use crate::arch::machine::{self, Bcm283xMailbox};
|
||||
use crate::dev::sd::{
|
||||
SdCardIdentification, SdCardStatus, SdCommand, SdCommandNumber, SdCommandTransfer,
|
||||
SdHostController, SdResponse, SdResponseType,
|
||||
};
|
||||
use crate::dev::Device;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
use vfs::BlockDevice;
|
||||
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::register_structs;
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => ARG2: ReadWrite<u32>),
|
||||
(0x04 => BLKSIZECNT: ReadWrite<u32>),
|
||||
(0x08 => ARG1: ReadWrite<u32>),
|
||||
(0x0C => CMDTM: ReadWrite<u32>),
|
||||
(0x10 => RESP0: ReadOnly<u32>),
|
||||
(0x14 => RESP1: ReadOnly<u32>),
|
||||
(0x18 => RESP2: ReadOnly<u32>),
|
||||
(0x1C => RESP3: ReadOnly<u32>),
|
||||
(0x20 => DATA: ReadWrite<u32>),
|
||||
(0x24 => STATUS: ReadOnly<u32>),
|
||||
(0x28 => CONTROL0: ReadWrite<u32>),
|
||||
(0x2C => CONTROL1: ReadWrite<u32>),
|
||||
(0x30 => INTERRUPT: ReadWrite<u32>),
|
||||
(0x34 => IRPT_MASK: ReadWrite<u32>),
|
||||
(0x38 => IRPT_EN: ReadWrite<u32>),
|
||||
(0x3C => CONTROL2: ReadWrite<u32>),
|
||||
(0x40 => _res0),
|
||||
(0x50 => FORCE_IRPT: ReadWrite<u32>),
|
||||
(0x54 => _res1),
|
||||
(0x70 => BOOT_TIMEOUT: ReadWrite<u32>),
|
||||
(0x74 => DBG_SEL: ReadWrite<u32>),
|
||||
(0x78 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct MmcInner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
status: SdCardStatus,
|
||||
}
|
||||
|
||||
pub struct MassMediaController {
|
||||
inner: InitOnce<IrqSafeSpinLock<MmcInner>>,
|
||||
base: usize,
|
||||
}
|
||||
|
||||
fn clock_divider(f_base: u32, f_target: u32) -> Result<u32, Errno> {
|
||||
let mut target_div;
|
||||
let mut div = 0;
|
||||
if f_target <= f_base {
|
||||
target_div = f_base / f_target;
|
||||
if f_base % f_target != 0 {
|
||||
target_div -= 1;
|
||||
}
|
||||
} else {
|
||||
target_div = 1;
|
||||
}
|
||||
for first_bit in (0..31).rev() {
|
||||
if target_div & (1 << first_bit) != 0 {
|
||||
div = first_bit;
|
||||
target_div &= !(1 << first_bit);
|
||||
if target_div != 0 {
|
||||
div += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if div == u32::MAX {
|
||||
div = 31;
|
||||
}
|
||||
if div >= 32 {
|
||||
div = 31;
|
||||
}
|
||||
if div != 0 {
|
||||
div = 1 << (div - 1)
|
||||
}
|
||||
if div >= 0x400 {
|
||||
div = 0x3FF;
|
||||
}
|
||||
|
||||
let f_sel = div & 0xFF;
|
||||
let upper_bits = (div >> 8) & 0x3;
|
||||
|
||||
Ok((f_sel << 8) | (upper_bits << 6))
|
||||
}
|
||||
|
||||
impl MmcInner {
|
||||
fn power_on(&mut self) -> Result<(), Errno> {
|
||||
machine::BCM_MBOX.set_power_state(
|
||||
Bcm283xMailbox::POWER_SD_CARD,
|
||||
Bcm283xMailbox::POWER_STATE_ON | Bcm283xMailbox::POWER_STATE_WAIT,
|
||||
)
|
||||
}
|
||||
|
||||
fn base_clock(&mut self) -> Result<u32, Errno> {
|
||||
machine::BCM_MBOX.clock_rate(Bcm283xMailbox::CLOCK_EMMC)
|
||||
}
|
||||
|
||||
fn send_cmd_inner(&mut self, cmd: &mut SdCommand) -> Result<SdResponse, Errno> {
|
||||
debugln!("send_cmd {:?}", cmd.number);
|
||||
|
||||
while self.regs.STATUS.get() & 1 != 0 {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
|
||||
let response_type = cmd.response_type();
|
||||
if response_type.is_busy() {
|
||||
// TODO check if this is an ABORT command
|
||||
|
||||
while self.regs.STATUS.get() & 2 != 0 {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
let (block_count, block_size, io_flags) = match &cmd.transfer {
|
||||
SdCommandTransfer::Write(buf, blk_size) => {
|
||||
let sz = *blk_size as usize;
|
||||
assert!(buf.len() % sz == 0);
|
||||
((buf.len() / sz) as u32, *blk_size, (1 << 21))
|
||||
}
|
||||
SdCommandTransfer::Read(buf, blk_size) => {
|
||||
let sz = *blk_size as usize;
|
||||
assert!(buf.len() % sz == 0);
|
||||
((buf.len() / sz) as u32, *blk_size, (1 << 21) | (1 << 4))
|
||||
}
|
||||
SdCommandTransfer::None => (0, 512, 0),
|
||||
};
|
||||
|
||||
let (size_136, size_flags) = match response_type {
|
||||
SdResponseType::R2 | SdResponseType::R4 => (true, 1 << 16),
|
||||
SdResponseType::R1b | SdResponseType::R5b => (false, 3 << 16),
|
||||
SdResponseType::None => (false, 0),
|
||||
_ => (false, 2 << 16),
|
||||
};
|
||||
|
||||
debugln!("size_flags: {:#x}, io_flags: {:#x}", size_flags, io_flags);
|
||||
self.regs
|
||||
.BLKSIZECNT
|
||||
.set((block_size as u32) | (block_count << 16));
|
||||
self.regs.ARG1.set(cmd.argument);
|
||||
self.regs
|
||||
.CMDTM
|
||||
.set((cmd.number() << 24) | size_flags | io_flags);
|
||||
|
||||
while self.regs.INTERRUPT.get() & 0x8001 == 0 {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
|
||||
let irq_status = self.regs.INTERRUPT.get();
|
||||
self.regs.INTERRUPT.set(0xFFFF0001);
|
||||
|
||||
if irq_status & 0xFFFF0001 != 1 {
|
||||
warnln!("SD error: irq_status={:#x}", irq_status);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
let response = if size_136 {
|
||||
SdResponse::Four([
|
||||
self.regs.RESP0.get(),
|
||||
self.regs.RESP1.get(),
|
||||
self.regs.RESP2.get(),
|
||||
self.regs.RESP3.get(),
|
||||
])
|
||||
} else {
|
||||
SdResponse::One(self.regs.RESP0.get())
|
||||
};
|
||||
|
||||
match &mut cmd.transfer {
|
||||
SdCommandTransfer::Write(_, _) => {
|
||||
todo!()
|
||||
}
|
||||
SdCommandTransfer::Read(buf, _) => {
|
||||
debugln!("Reading {} data blocks", block_count);
|
||||
for i in 0..block_count {
|
||||
while self.regs.INTERRUPT.get() & (0x8000 | (1 << 5)) == 0 {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
let irq_status = self.regs.INTERRUPT.get();
|
||||
self.regs.INTERRUPT.set(0xFFFF0000 | (1 << 5));
|
||||
|
||||
if irq_status & (0xFFFF0000 | (1 << 5)) != (1 << 5) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
assert!(block_size % 4 == 0);
|
||||
for j in (0..block_size).step_by(4) {
|
||||
let word = self.regs.DATA.get();
|
||||
let base = (i * block_size as u32) as usize + j as usize;
|
||||
buf[base + 0] = (word & 0xFF) as u8;
|
||||
buf[base + 1] = ((word >> 8) & 0xFF) as u8;
|
||||
buf[base + 2] = ((word >> 16) & 0xFF) as u8;
|
||||
buf[base + 3] = (word >> 24) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
SdCommandTransfer::None => {}
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
fn phys_reset(&mut self) -> Result<(), Errno> {
|
||||
self.status.phys_inserted = false;
|
||||
|
||||
let mut tmp = self.regs.CONTROL1.get();
|
||||
tmp |= 1 << 24;
|
||||
// Disable clock
|
||||
tmp &= !(1 << 2);
|
||||
tmp &= !(1 << 0);
|
||||
self.regs.CONTROL1.set(tmp);
|
||||
|
||||
debugln!("Checking for a card");
|
||||
let mut retry = 5;
|
||||
let mut status = 0;
|
||||
|
||||
while retry > 0 {
|
||||
status = self.regs.STATUS.get();
|
||||
if status & 1 << 16 != 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
for _ in 0..100000 {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
|
||||
retry -= 1;
|
||||
}
|
||||
|
||||
if retry == 0 {
|
||||
warnln!("No card inserted");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.status.phys_inserted = true;
|
||||
debugln!("A card is present: status={:#x}", status);
|
||||
|
||||
self.regs.CONTROL2.set(0);
|
||||
|
||||
let mut f_base = self.base_clock()?;
|
||||
if f_base == 0 {
|
||||
f_base = 100000000;
|
||||
}
|
||||
|
||||
let div = clock_divider(f_base, 400000)?;
|
||||
|
||||
debugln!("Switching to ID frequency");
|
||||
|
||||
tmp = self.regs.CONTROL1.get();
|
||||
tmp |= div;
|
||||
tmp &= !(0xF << 16);
|
||||
tmp |= 11 << 16;
|
||||
tmp |= 4;
|
||||
tmp |= 1;
|
||||
self.regs.CONTROL1.set(tmp);
|
||||
|
||||
while self.regs.CONTROL1.get() & 1 << 1 == 0 {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
|
||||
self.regs.IRPT_EN.set(0);
|
||||
self.regs.INTERRUPT.set(u32::MAX);
|
||||
self.regs.IRPT_MASK.set(u32::MAX);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_cmd(&mut self, cmd: &mut SdCommand) -> Result<SdResponse, Errno> {
|
||||
if cmd.is_acmd() {
|
||||
let arg = if let Some(rca) = self.status.address {
|
||||
(rca as u32) << 16
|
||||
} else {
|
||||
0
|
||||
};
|
||||
self.send_cmd_inner(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd55,
|
||||
argument: arg,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?;
|
||||
}
|
||||
self.send_cmd_inner(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDevice for MassMediaController {
|
||||
fn read(&self, pos: usize, data: &mut [u8]) -> Result<(), Errno> {
|
||||
// TODO check card status
|
||||
if data.len() % 512 != 0 || pos % 512 != 0 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
for i in 0..(data.len() / 512) {
|
||||
let s = i * 512;
|
||||
self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd17,
|
||||
argument: (pos / 512 + i) as u32,
|
||||
transfer: SdCommandTransfer::Read(&mut data[s..(s + 512)], 512),
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, _pos: usize, _data: &[u8]) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl SdHostController for MassMediaController {
|
||||
fn send_cmd(&self, cmd: &mut SdCommand) -> Result<SdResponse, Errno> {
|
||||
self.inner.get().lock().send_cmd(cmd)
|
||||
}
|
||||
|
||||
fn phys_reset(&self) -> Result<(), Errno> {
|
||||
let mut inner = self.inner.get().lock();
|
||||
inner.status.address = None;
|
||||
inner.status.id = None;
|
||||
inner.phys_reset()
|
||||
}
|
||||
|
||||
fn is_phys_inserted(&self) -> bool {
|
||||
self.inner.get().lock().status.phys_inserted
|
||||
}
|
||||
|
||||
fn set_transfer_block_size(&self, blk: usize) -> Result<(), Errno> {
|
||||
// TODO other block sizes?
|
||||
assert_eq!(blk, 512);
|
||||
let inner = self.inner.get().lock();
|
||||
let mut tmp = inner.regs.BLKSIZECNT.get();
|
||||
tmp &= !0xFFF;
|
||||
tmp |= 0x200;
|
||||
inner.regs.BLKSIZECNT.set(tmp);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_card_address(&self, rca: u16) -> Result<(), Errno> {
|
||||
self.inner.get().lock().status.address = Some(rca);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_card_identification(&self, id: SdCardIdentification) -> Result<(), Errno> {
|
||||
self.inner.get().lock().status.id = Some(id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_card_identification(&self) -> Result<(), Errno> {
|
||||
self.inner.get().lock().status.id = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for MassMediaController {
|
||||
fn name(&self) -> &'static str {
|
||||
"BCM283x External Mass Media Controller"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let mut inner = MmcInner {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.base, 1)?,
|
||||
status: SdCardStatus::invalid(),
|
||||
};
|
||||
inner.power_on()?;
|
||||
self.inner.init(IrqSafeSpinLock::new(inner));
|
||||
|
||||
self.reset_card()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MassMediaController {
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource, IrqContext},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use core::fmt;
|
||||
use cortex_a::registers::MPIDR_EL1;
|
||||
use error::Errno;
|
||||
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::register_structs;
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub(super) BcmRegs {
|
||||
(0x000 => _res0),
|
||||
(0x200 => PENDING_BASIC: ReadOnly<u32>),
|
||||
(0x204 => PENDING1: ReadOnly<u32>),
|
||||
(0x208 => PENDING2: ReadOnly<u32>),
|
||||
(0x20C => _res1),
|
||||
(0x210 => ENABLE1: ReadWrite<u32>),
|
||||
(0x214 => ENABLE2: ReadWrite<u32>),
|
||||
(0x218 => ENABLE_BASIC: ReadWrite<u32>),
|
||||
(0x21C => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub(super) Qa7Regs {
|
||||
(0x000 => _res0),
|
||||
(0x040 => CORE_IRQ_EN: [ReadWrite<u32>; 4]),
|
||||
(0x050 => _res1),
|
||||
(0x060 => CORE_IRQ_SRC: [ReadWrite<u32>; 4]),
|
||||
(0x070 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct IrqNumber(u32);
|
||||
|
||||
impl IrqNumber {
|
||||
pub const MAX: u32 = 64 + 32;
|
||||
|
||||
pub const fn bcm_irq(n: u32) -> Self {
|
||||
assert!(n < 64);
|
||||
Self(n + 32)
|
||||
}
|
||||
|
||||
pub const fn qa7_irq(n: u32) -> Self {
|
||||
assert!(n < 32);
|
||||
Self(n)
|
||||
}
|
||||
|
||||
pub const fn is_bcm_irq(self) -> bool {
|
||||
self.0 >= 32
|
||||
}
|
||||
|
||||
pub const fn number(self) -> u32 {
|
||||
if self.is_bcm_irq() {
|
||||
self.0 - 32
|
||||
} else {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn index(self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for IrqNumber {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}_irq{}",
|
||||
if self.is_bcm_irq() { "bcm" } else { "qa7" },
|
||||
self.number()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct BcmIrqchipInner {
|
||||
regs: DeviceMemoryIo<BcmRegs>,
|
||||
}
|
||||
struct Qa7IrqchipInner {
|
||||
regs: DeviceMemoryIo<Qa7Regs>,
|
||||
}
|
||||
|
||||
pub struct Bcm283xIrqchip {
|
||||
bcm_inner: InitOnce<IrqSafeSpinLock<BcmIrqchipInner>>,
|
||||
qa7_inner: InitOnce<IrqSafeSpinLock<Qa7IrqchipInner>>,
|
||||
table: IrqSafeSpinLock<[Option<&'static (dyn IntSource + Sync)>; IrqNumber::MAX as usize]>,
|
||||
}
|
||||
|
||||
impl BcmIrqchipInner {
|
||||
fn enable_irq(&self, n: u32) -> Result<(), Errno> {
|
||||
let (reg, bit) = if n < 32 {
|
||||
(&self.regs.ENABLE1, 1 << n)
|
||||
} else if n < 64 {
|
||||
(&self.regs.ENABLE2, 1 << (n - 32))
|
||||
} else {
|
||||
todo!();
|
||||
};
|
||||
reg.set(reg.get() | bit);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pending_irq(&self) -> Option<u32> {
|
||||
let status = self.regs.PENDING2.get();
|
||||
for bit in 0..32 {
|
||||
if status & (1 << bit) != 0 {
|
||||
return Some(bit + 32);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Qa7IrqchipInner {
|
||||
fn enable_irq(&self, n: u32) -> Result<(), Errno> {
|
||||
// TODO check this code in SMP setup
|
||||
let core_id = MPIDR_EL1.get() & 0x3;
|
||||
// Timer IRQ control
|
||||
let (reg, bit) = if n < 4 {
|
||||
(&self.regs.CORE_IRQ_EN[core_id as usize], 1 << n)
|
||||
} else {
|
||||
todo!()
|
||||
};
|
||||
reg.set(reg.get() | bit);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pending_irq(&self) -> Option<u32> {
|
||||
let core_id = MPIDR_EL1.get() & 0x3;
|
||||
let reg = &self.regs.CORE_IRQ_SRC[core_id as usize];
|
||||
let value = reg.get();
|
||||
for bit in 0..8 {
|
||||
if value & (1 << bit) != 0 {
|
||||
return Some(bit);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn clear_irq(&self) {
|
||||
let core_id = MPIDR_EL1.get() & 0x3;
|
||||
let reg = &self.regs.CORE_IRQ_SRC[core_id as usize];
|
||||
reg.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Bcm283xIrqchip {
|
||||
fn name(&self) -> &'static str {
|
||||
"BCM283x/QA7 Interrupt Controller"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.bcm_inner.init(IrqSafeSpinLock::new(BcmIrqchipInner {
|
||||
regs: DeviceMemoryIo::map("BCM283x Peripheral Interrupt Controller", 0x3F00B000, 1)?,
|
||||
}));
|
||||
self.qa7_inner.init(IrqSafeSpinLock::new(Qa7IrqchipInner {
|
||||
regs: DeviceMemoryIo::map("QA7 Core Interrupt Controller", 0x40000000, 1)?,
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntController for Bcm283xIrqchip {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
fn register_handler(
|
||||
&self,
|
||||
irq: IrqNumber,
|
||||
handler: &'static (dyn IntSource + Sync),
|
||||
) -> Result<(), Errno> {
|
||||
let mut table = self.table.lock();
|
||||
let irqi = irq.index();
|
||||
if table[irqi as usize].is_some() {
|
||||
return Err(Errno::AlreadyExists);
|
||||
}
|
||||
|
||||
debugln!("Bound {:?} to {:?}", irq, Device::name(handler));
|
||||
table[irqi as usize] = Some(handler);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enable_irq(&self, irq: IrqNumber) -> Result<(), Errno> {
|
||||
if irq.is_bcm_irq() {
|
||||
self.bcm_inner.get().lock().enable_irq(irq.number())
|
||||
} else {
|
||||
self.qa7_inner.get().lock().enable_irq(irq.number())
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_pending_irqs<'q>(&'q self, _ic: &IrqContext<'q>) {
|
||||
let qa7 = self.qa7_inner.get().lock();
|
||||
let bcm = self.bcm_inner.get().lock();
|
||||
|
||||
let irq_number = if let Some(irq) = qa7.pending_irq() {
|
||||
irq as usize
|
||||
} else if let Some(irq) = bcm.pending_irq() {
|
||||
irq as usize + 32
|
||||
} else {
|
||||
panic!("No IRQ pending");
|
||||
};
|
||||
|
||||
drop(bcm);
|
||||
|
||||
if irq_number < 32 {
|
||||
qa7.clear_irq();
|
||||
drop(qa7);
|
||||
}
|
||||
|
||||
{
|
||||
let table = self.table.lock();
|
||||
match table[irq_number] {
|
||||
None => panic!("No handler registered for irq{}", irq_number),
|
||||
Some(handler) => {
|
||||
drop(table);
|
||||
handler.handle_irq().expect("irq handler failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bcm283xIrqchip {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
bcm_inner: InitOnce::new(),
|
||||
qa7_inner: InitOnce::new(),
|
||||
table: IrqSafeSpinLock::new([None; IrqNumber::MAX as usize]),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
use crate::dev::Device;
|
||||
use crate::mem::{self, virt::DeviceMemoryIo};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use error::Errno;
|
||||
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::registers::{ReadOnly, WriteOnly};
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
STATUS [
|
||||
FULL OFFSET(31) NUMBITS(1) [],
|
||||
EMPTY OFFSET(30) NUMBITS(1) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => READ: ReadOnly<u32>),
|
||||
(0x04 => _res0),
|
||||
(0x18 => STATUS: ReadOnly<u32, STATUS::Register>),
|
||||
(0x1C => _res1),
|
||||
(0x20 => WRITE: WriteOnly<u32>),
|
||||
(0x24 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(16))]
|
||||
struct MboxBuffer([u32; 36]);
|
||||
|
||||
struct Inner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
buf: MboxBuffer,
|
||||
}
|
||||
|
||||
pub struct Bcm283xMailbox {
|
||||
inner: InitOnce<IrqSafeSpinLock<Inner>>,
|
||||
base: usize,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
const RESPONSE: u32 = 1 << 31;
|
||||
const REQUEST: u32 = 0;
|
||||
|
||||
const PROP_ARM_MEMORY: u32 = 0x10005;
|
||||
const PROP_SET_POWER_STATE: u32 = 0x28001;
|
||||
const PROP_GET_CLOCK_RATE: u32 = 0x30002;
|
||||
|
||||
fn call(&self, ch: u8) -> Result<(), Errno> {
|
||||
let ptr_virt = &self.buf as *const _ as usize;
|
||||
let ptr_phys = ptr_virt - mem::KERNEL_OFFSET;
|
||||
assert!(ptr_phys < 0x100000000);
|
||||
|
||||
while self.regs.STATUS.matches_all(STATUS::FULL::SET) {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
|
||||
let val = (ptr_phys as u32) | (ch as u32);
|
||||
self.regs.WRITE.set(val);
|
||||
|
||||
loop {
|
||||
while self.regs.STATUS.matches_all(STATUS::EMPTY::SET) {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
|
||||
if self.regs.READ.get() == val {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn memory_split(&mut self) -> Result<usize, Errno> {
|
||||
self.buf.0[0] = 8 * 4;
|
||||
self.buf.0[1] = Self::REQUEST;
|
||||
|
||||
self.buf.0[2] = Self::PROP_ARM_MEMORY;
|
||||
self.buf.0[3] = 8;
|
||||
self.buf.0[4] = 0;
|
||||
self.buf.0[5] = 0x12345678;
|
||||
self.buf.0[6] = 0x87654321;
|
||||
self.buf.0[7] = 0;
|
||||
|
||||
self.call(8)?;
|
||||
|
||||
if self.buf.0[1] != Self::RESPONSE {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
Ok(self.buf.0[6] as usize)
|
||||
}
|
||||
|
||||
fn set_power_state(&mut self, dev: u32, cmd: u32) -> Result<(), Errno> {
|
||||
self.buf.0[0] = 8 * 4;
|
||||
self.buf.0[1] = Self::REQUEST;
|
||||
|
||||
self.buf.0[2] = Self::PROP_SET_POWER_STATE;
|
||||
self.buf.0[3] = 8;
|
||||
self.buf.0[4] = 0;
|
||||
self.buf.0[5] = dev;
|
||||
self.buf.0[6] = cmd;
|
||||
self.buf.0[7] = 0;
|
||||
|
||||
self.call(8)?;
|
||||
|
||||
if self.buf.0[1] != Self::RESPONSE {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
if self.buf.0[6] & 1 << 1 != 0 {
|
||||
return Err(Errno::DoesNotExist);
|
||||
}
|
||||
|
||||
if self.buf.0[6] & 1 << 0 == 0 {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clock_rate(&mut self, clk: u32) -> Result<u32, Errno> {
|
||||
self.buf.0[0] = 8 * 4;
|
||||
self.buf.0[1] = Self::REQUEST;
|
||||
|
||||
self.buf.0[2] = Self::PROP_GET_CLOCK_RATE;
|
||||
self.buf.0[3] = 8;
|
||||
self.buf.0[4] = 0;
|
||||
self.buf.0[5] = clk;
|
||||
self.buf.0[6] = 0;
|
||||
self.buf.0[7] = 0;
|
||||
|
||||
self.call(8)?;
|
||||
|
||||
if self.buf.0[1] != Self::RESPONSE {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
Ok(self.buf.0[6])
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Bcm283xMailbox {
|
||||
fn name(&self) -> &'static str {
|
||||
"BCM283x Mailbox"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.inner.init(IrqSafeSpinLock::new(Inner {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.base, 1)?,
|
||||
buf: MboxBuffer([0; 36]),
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Bcm283xMailbox {
|
||||
pub const POWER_STATE_ON: u32 = 1 << 0;
|
||||
pub const POWER_STATE_WAIT: u32 = 1 << 1;
|
||||
pub const POWER_SD_CARD: u32 = 0;
|
||||
|
||||
pub const CLOCK_EMMC: u32 = 1;
|
||||
|
||||
pub fn memory_split(&self) -> Result<usize, Errno> {
|
||||
self.inner.get().lock().memory_split()
|
||||
}
|
||||
|
||||
pub fn set_power_state(&self, dev: u32, cmd: u32) -> Result<(), Errno> {
|
||||
self.inner.get().lock().set_power_state(dev, cmd)
|
||||
}
|
||||
|
||||
pub fn clock_rate(&self, clk: u32) -> Result<u32, Errno> {
|
||||
self.inner.get().lock().clock_rate(clk)
|
||||
}
|
||||
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use crate::arch::aarch64::timer::GenericTimer;
|
||||
use crate::dev::{Device, serial::{SerialDevice, pl011::Pl011}, irq::IntSource};
|
||||
use crate::mem::phys;
|
||||
use error::Errno;
|
||||
|
||||
pub mod irqchip;
|
||||
pub use irqchip::{IrqNumber, Bcm283xIrqchip};
|
||||
pub mod emmc;
|
||||
pub use emmc::MassMediaController;
|
||||
pub mod mailbox;
|
||||
pub use mailbox::Bcm283xMailbox;
|
||||
|
||||
const UART_BASE: usize = 0x3F201000;
|
||||
const EMMC_BASE: usize = 0x3F300000;
|
||||
const BCM_MBOX_BASE: usize = 0x3F00B880;
|
||||
const UART_IRQ: IrqNumber = IrqNumber::bcm_irq(57);
|
||||
const LOCAL_TIMER_IRQ: IrqNumber = IrqNumber::qa7_irq(1);
|
||||
|
||||
pub fn init_board_early() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
UART.enable()?;
|
||||
BCM_MBOX.enable()?;
|
||||
|
||||
let memory = BCM_MBOX.memory_split()?;
|
||||
infoln!("Memory split: {:#x}", memory);
|
||||
|
||||
phys::init_from_region(0, memory);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_board() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
IRQCHIP.enable()?;
|
||||
UART.init_irqs()?;
|
||||
|
||||
EMMC.enable()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn intc() -> &'static Bcm283xIrqchip {
|
||||
&IRQCHIP
|
||||
}
|
||||
|
||||
/// Returns primary console for this machine
|
||||
#[inline]
|
||||
pub fn console() -> &'static impl SerialDevice {
|
||||
&UART
|
||||
}
|
||||
|
||||
/// Returns the timer used as CPU-local periodic IRQ source
|
||||
#[inline]
|
||||
pub fn local_timer() -> &'static GenericTimer {
|
||||
&LOCAL_TIMER
|
||||
}
|
||||
|
||||
static IRQCHIP: Bcm283xIrqchip = Bcm283xIrqchip::new();
|
||||
static EMMC: MassMediaController = unsafe { MassMediaController::new(EMMC_BASE) };
|
||||
static UART: Pl011 = unsafe { Pl011::new(UART_BASE, UART_IRQ) };
|
||||
pub(self) static BCM_MBOX: Bcm283xMailbox = unsafe { Bcm283xMailbox::new(BCM_MBOX_BASE) };
|
||||
static LOCAL_TIMER: GenericTimer = GenericTimer::new(LOCAL_TIMER_IRQ);
|
||||
@@ -7,6 +7,8 @@ use error::Errno;
|
||||
pub mod fdt;
|
||||
pub mod gpio;
|
||||
pub mod irq;
|
||||
#[allow(missing_docs)]
|
||||
pub mod sd;
|
||||
pub mod pci;
|
||||
pub mod rtc;
|
||||
pub mod serial;
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
use crate::dev::Device;
|
||||
use vfs::BlockDevice;
|
||||
use error::Errno;
|
||||
|
||||
pub trait SdHostController: Device + BlockDevice {
|
||||
// Physical layer
|
||||
fn send_cmd(&self, cmd: &mut SdCommand) -> Result<SdResponse, Errno>;
|
||||
fn phys_reset(&self) -> Result<(), Errno>;
|
||||
fn is_phys_inserted(&self) -> bool;
|
||||
fn set_transfer_block_size(&self, blk: usize) -> Result<(), Errno>;
|
||||
|
||||
// Data layer
|
||||
fn set_card_address(&self, rca: u16) -> Result<(), Errno>;
|
||||
fn set_card_identification(&self, id: SdCardIdentification) -> Result<(), Errno>;
|
||||
fn reset_card_identification(&self) -> Result<(), Errno>;
|
||||
|
||||
fn reset_card_probe(&self) -> Result<(), Errno> {
|
||||
self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd0,
|
||||
argument: 0,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?;
|
||||
|
||||
if self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd8,
|
||||
argument: 0x1AA,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?.unwrap_one() != 0x1AA {
|
||||
warnln!("Card did not respond to CMD8");
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
// Set operating conditions
|
||||
for _ in 0..10 {
|
||||
let res = self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Acmd41,
|
||||
argument: 0x00FF8000,
|
||||
transfer: SdCommandTransfer::None
|
||||
})?.unwrap_one();
|
||||
|
||||
if res & (1 << 31) != 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for _ in 0..1000000 {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
warnln!("Card did not respond to Acmd41");
|
||||
Err(Errno::InvalidArgument)
|
||||
}
|
||||
|
||||
fn reset_card(&self) -> Result<(), Errno> {
|
||||
let mut buf = [0u8; 16];
|
||||
|
||||
self.reset_card_identification()?;
|
||||
|
||||
// Reset physical interface
|
||||
self.phys_reset()?;
|
||||
|
||||
// Bail out if card is not physically present
|
||||
if !self.is_phys_inserted() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Probe for card
|
||||
self.reset_card_probe()?;
|
||||
|
||||
// Perform init sequence
|
||||
let cmd2 = self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd2,
|
||||
argument: 0,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?.unwrap_four();
|
||||
|
||||
let cmd3 = self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd3,
|
||||
argument: 0,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?.unwrap_one();
|
||||
|
||||
let rca = (cmd3 >> 16) as u16;
|
||||
|
||||
if cmd3 & (1 << 14) != 0 {
|
||||
warnln!("Illegal command");
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
if cmd3 & (1 << 13) != 0 {
|
||||
warnln!("Card reported error");
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
if cmd3 & (1 << 8) == 0 {
|
||||
warnln!("Card is not ready for data mode");
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
let cmd9 = self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd9,
|
||||
argument: cmd3 & 0xFFFF0000,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?.unwrap_four();
|
||||
|
||||
debugln!("cmd9 = {:#x?}", cmd9);
|
||||
let csd_structure = (cmd9[3] >> 16) & 0x3;
|
||||
let capacity = match csd_structure {
|
||||
0 => {
|
||||
let c_size = ((cmd9[2] & 0x3) << 10) | ((cmd9[1] >> 22) & 0x3FF);
|
||||
let c_size_mult = (cmd9[1] >> 7) & 0x7;
|
||||
((c_size + 1) as u64) << (c_size_mult + 9 /* Block size is 512 */ + 2)
|
||||
},
|
||||
1 => todo!(),
|
||||
_ => {
|
||||
warnln!("Invalid CSD version: {}", csd_structure);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
};
|
||||
|
||||
let cmd7 = self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd7,
|
||||
argument: cmd3 & 0xFFFF0000,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?.unwrap_one();
|
||||
|
||||
let status = (cmd7 >> 9) & 0xF;
|
||||
if status != 3 && status != 4 {
|
||||
warnln!("Card reported invalid status: {}", status);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
// Set block size
|
||||
self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd16,
|
||||
argument: 512,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?;
|
||||
|
||||
self.set_card_address(rca)?;
|
||||
self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Acmd51,
|
||||
argument: 0,
|
||||
transfer: SdCommandTransfer::Read(&mut buf[..8], 8)
|
||||
})?;
|
||||
|
||||
let sd_spec = buf[0] & 0xF;
|
||||
|
||||
let version = match sd_spec {
|
||||
0 => SdCardVersion::Ver10,
|
||||
1 => SdCardVersion::Ver11,
|
||||
2 => {
|
||||
// FIXME check for 3.0/4.0
|
||||
SdCardVersion::Ver20
|
||||
},
|
||||
_ => panic!("Invalid version: {:#x}", sd_spec)
|
||||
};
|
||||
|
||||
let card_id = SdCardIdentification {
|
||||
id: cmd2,
|
||||
version,
|
||||
capacity
|
||||
};
|
||||
|
||||
self.set_card_identification(card_id)?;
|
||||
infoln!("Found a valid SD card of capacity {}B", capacity);
|
||||
|
||||
// TODO High speed support
|
||||
// if version >= SdCardVersion::Ver11 {
|
||||
// let mut buf = [0u8; 64];
|
||||
// self.send_cmd(&mut SdCommand {
|
||||
// number: SdCommandNumber::Cmd6,
|
||||
// argument: 0x00FFFFF0,
|
||||
// transfer: SdCommandTransfer::Read(&mut buf, 64)
|
||||
// })?;
|
||||
|
||||
// // Check HS support
|
||||
// let hs_support = buf[13] >> 1 != 0;
|
||||
// if hs_support {
|
||||
// debugln!("Switching to high speed mode");
|
||||
// self.send_cmd(&mut SdCommand {
|
||||
// number: SdCommandNumber::Cmd6,
|
||||
// argument: 0x80FFFFF1,
|
||||
// transfer: SdCommandTransfer::None
|
||||
// })?;
|
||||
|
||||
// todo!();
|
||||
// }
|
||||
// }
|
||||
|
||||
// let dbus_widths = buf[0] & 0xF;
|
||||
// TODO 4 bit mode support
|
||||
|
||||
// TODO switch clock rate
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum SdResponseType {
|
||||
None,
|
||||
R1,
|
||||
R1b,
|
||||
R2,
|
||||
R3,
|
||||
R4,
|
||||
R5,
|
||||
R5b,
|
||||
R6,
|
||||
R7,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||
pub enum SdCardVersion {
|
||||
Ver10 = 0x10,
|
||||
Ver11 = 0x11,
|
||||
Ver20 = 0x20,
|
||||
Ver30 = 0x30,
|
||||
Ver40 = 0x40,
|
||||
}
|
||||
|
||||
pub struct SdCardIdentification {
|
||||
pub id: [u32; 4],
|
||||
pub version: SdCardVersion,
|
||||
pub capacity: u64,
|
||||
}
|
||||
|
||||
pub struct SdCardStatus {
|
||||
pub phys_inserted: bool,
|
||||
pub address: Option<u16>,
|
||||
pub id: Option<SdCardIdentification>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum SdResponse {
|
||||
One(u32),
|
||||
Four([u32; 4]),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[repr(u32)]
|
||||
pub enum SdCommandNumber {
|
||||
Cmd0 = 0,
|
||||
Cmd2 = 2,
|
||||
Cmd3 = 3,
|
||||
Cmd6 = 6,
|
||||
Cmd7 = 7,
|
||||
Cmd8 = 8,
|
||||
Cmd9 = 9,
|
||||
Cmd16 = 16,
|
||||
Cmd17 = 17,
|
||||
Acmd41 = 41,
|
||||
Acmd51 = 51,
|
||||
Cmd55 = 55,
|
||||
}
|
||||
|
||||
pub enum SdCommandTransfer<'a> {
|
||||
None,
|
||||
Read(&'a mut [u8], u16),
|
||||
Write(&'a [u8], u16)
|
||||
}
|
||||
|
||||
pub struct SdCommand<'a> {
|
||||
pub number: SdCommandNumber,
|
||||
pub argument: u32,
|
||||
pub transfer: SdCommandTransfer<'a>,
|
||||
}
|
||||
|
||||
impl SdCardStatus {
|
||||
pub const fn invalid() -> Self {
|
||||
Self {
|
||||
phys_inserted: false,
|
||||
address: None,
|
||||
id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponseType {
|
||||
pub const fn is_busy(self) -> bool {
|
||||
match self {
|
||||
Self::R1b | Self::R5b => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponse {
|
||||
pub fn unwrap_one(&self) -> u32 {
|
||||
match self {
|
||||
&SdResponse::One(v) => v,
|
||||
_ => panic!("Unexpected response type")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_four(&self) -> [u32; 4] {
|
||||
match self {
|
||||
&SdResponse::Four(v) => v,
|
||||
_ => panic!("Unexpected response type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SdCommand<'_> {
|
||||
pub const fn response_type(&self) -> SdResponseType {
|
||||
match self.number {
|
||||
SdCommandNumber::Cmd0 => SdResponseType::None,
|
||||
SdCommandNumber::Cmd2 => SdResponseType::R2,
|
||||
SdCommandNumber::Cmd3 => SdResponseType::R6,
|
||||
SdCommandNumber::Cmd6 => SdResponseType::R1,
|
||||
SdCommandNumber::Cmd7 => SdResponseType::R1b,
|
||||
SdCommandNumber::Cmd8 => SdResponseType::R7,
|
||||
SdCommandNumber::Cmd9 => SdResponseType::R2,
|
||||
SdCommandNumber::Cmd16 => SdResponseType::R1,
|
||||
SdCommandNumber::Cmd17 => SdResponseType::R1,
|
||||
SdCommandNumber::Acmd41 => SdResponseType::R3,
|
||||
SdCommandNumber::Acmd51 => SdResponseType::R1,
|
||||
SdCommandNumber::Cmd55 => SdResponseType::R1,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_acmd(&self) -> bool {
|
||||
match self.number {
|
||||
SdCommandNumber::Acmd41 | SdCommandNumber::Acmd51 => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn number(&self) -> u32 {
|
||||
self.number as u32
|
||||
}
|
||||
}
|
||||
@@ -51,3 +51,4 @@ impl<T> InitOnce<T> {
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for InitOnce<T> {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user