Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 717bbda123 | |||
| aa86f377f2 | |||
| e8acfb5a40 | |||
| b5d704064d |
Generated
+40
@@ -258,6 +258,26 @@ version = "0.10.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield"
|
||||||
|
version = "0.19.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db1bcd90f88eabbf0cadbfb87a45bceeaebcd3b4bc9e43da379cd2ef0162590d"
|
||||||
|
dependencies = [
|
||||||
|
"bitfield-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield-macros"
|
||||||
|
version = "0.19.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3787a07661997bfc05dd3431e379c0188573f78857080cf682e1393ab8e4d64c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.98",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -2727,6 +2747,25 @@ dependencies = [
|
|||||||
"yggdrasil-abi",
|
"yggdrasil-abi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ygg_driver_mmc_generic_sdhci"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bitfield",
|
||||||
|
"bitflags 2.8.0",
|
||||||
|
"bytemuck",
|
||||||
|
"device-api",
|
||||||
|
"device-tree",
|
||||||
|
"futures-util",
|
||||||
|
"libk",
|
||||||
|
"libk-mm",
|
||||||
|
"libk-util",
|
||||||
|
"log",
|
||||||
|
"tock-registers",
|
||||||
|
"yggdrasil-abi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ygg_driver_net_core"
|
name = "ygg_driver_net_core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -3021,6 +3060,7 @@ dependencies = [
|
|||||||
"ygg_driver_bsp_riscv",
|
"ygg_driver_bsp_riscv",
|
||||||
"ygg_driver_fat32",
|
"ygg_driver_fat32",
|
||||||
"ygg_driver_input",
|
"ygg_driver_input",
|
||||||
|
"ygg_driver_mmc_generic_sdhci",
|
||||||
"ygg_driver_net_core",
|
"ygg_driver_net_core",
|
||||||
"ygg_driver_net_igbe",
|
"ygg_driver_net_igbe",
|
||||||
"ygg_driver_net_loopback",
|
"ygg_driver_net_loopback",
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ $ booti ${loadaddr} ${initrd_addr_r}:<SIZE-PRINTED-WHEN-LOADING-INITRD> ${fdt_ad
|
|||||||
###### a quick command for a development boot
|
###### a quick command for a development boot
|
||||||
###### (FIXME when initrd gets larger than 64MiB)
|
###### (FIXME when initrd gets larger than 64MiB)
|
||||||
|
|
||||||
env set ipaddr 13.0.0.2; env set fdt_addr_r 0x11000000; env set initrd_addr_r 0x04000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.tar; tftpboot ${loadaddr} 13.0.0.1:kernel.bin; load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:67108864 ${fdt_addr_r}
|
env set ipaddr 13.0.0.2; env set fdt_addr_r 0x11000000; env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.img; tftpboot ${loadaddr} 13.0.0.1:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
|
||||||
|
|
||||||
dhcp;
|
dhcp;
|
||||||
env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
|
env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ device-tree.workspace = true
|
|||||||
kernel-arch-aarch64.workspace = true
|
kernel-arch-aarch64.workspace = true
|
||||||
ygg_driver_bsp_arm.path = "driver/bsp/arm"
|
ygg_driver_bsp_arm.path = "driver/bsp/arm"
|
||||||
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
|
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
|
||||||
|
ygg_driver_mmc_generic_sdhci.path = "driver/mmc/generic-sdhci"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
||||||
device-tree.workspace = true
|
device-tree.workspace = true
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ impl BlockDevice for ScsiUnit {
|
|||||||
let mut transport = self.enclosure.transport.lock().await;
|
let mut transport = self.enclosure.transport.lock().await;
|
||||||
|
|
||||||
// TODO DmaSliceMut subslicing
|
// TODO DmaSliceMut subslicing
|
||||||
let (buffer, range) = buffer.into_parts();
|
let (buffer, range) = unsafe { buffer.into_parts() };
|
||||||
let mut offset = range.start;
|
let mut offset = range.start;
|
||||||
|
|
||||||
for i in (0..lba_count).step_by(self.max_lba_per_request) {
|
for i in (0..lba_count).step_by(self.max_lba_per_request) {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "ygg_driver_mmc_generic_sdhci"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
device-tree.workspace = true
|
||||||
|
device-api.workspace = true
|
||||||
|
yggdrasil-abi.workspace = true
|
||||||
|
libk.workspace = true
|
||||||
|
libk-util.workspace = true
|
||||||
|
libk-mm.workspace = true
|
||||||
|
|
||||||
|
log.workspace = true
|
||||||
|
tock-registers.workspace = true
|
||||||
|
bitflags.workspace = true
|
||||||
|
bytemuck.workspace = true
|
||||||
|
async-trait.workspace = true
|
||||||
|
futures-util.workspace = true
|
||||||
|
bitfield = "0.19.1"
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
use core::{mem::MaybeUninit, time::Duration};
|
||||||
|
|
||||||
|
use alloc::{boxed::Box, sync::Arc};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use device_api::device::{Device, DeviceInitContext};
|
||||||
|
use libk::{
|
||||||
|
device::block::BlockDevice,
|
||||||
|
dma::{DmaBuffer, DmaSlice, DmaSliceMut},
|
||||||
|
error::Error,
|
||||||
|
task::runtime,
|
||||||
|
};
|
||||||
|
use libk_mm::{
|
||||||
|
address::PhysicalAddress, table::MapAttributes, OnDemandPage, PageProvider, VirtualPage,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
command::{self, CsdStructure, OcrRegister},
|
||||||
|
host::SdhciHost,
|
||||||
|
Sdhci,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SdCard<H: SdhciHost> {
|
||||||
|
hc: Arc<Sdhci<H>>,
|
||||||
|
pub(crate) rca: u16,
|
||||||
|
pub(crate) csd: CsdStructure,
|
||||||
|
high_capacity: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: SdhciHost> SdCard<H> {
|
||||||
|
/*
|
||||||
|
* SD/MMC card setup state machine:
|
||||||
|
*
|
||||||
|
* IDLE <------\ If card cannot handle req. voltage
|
||||||
|
* | |
|
||||||
|
* | <--- Send CMD8
|
||||||
|
* |
|
||||||
|
* No response | Voltage range not compatible
|
||||||
|
* | | |
|
||||||
|
* V V V
|
||||||
|
* /--------- ACMD41 --------> INA <-------- CMD15 from data xfer mode
|
||||||
|
* | |
|
||||||
|
* | |
|
||||||
|
* V V
|
||||||
|
* MMC card READY
|
||||||
|
* |
|
||||||
|
* V
|
||||||
|
* CMD2
|
||||||
|
* |
|
||||||
|
* V
|
||||||
|
* IDENT
|
||||||
|
* |
|
||||||
|
* V
|
||||||
|
* CMD3
|
||||||
|
* |
|
||||||
|
* | <--- Card responds with a RCA
|
||||||
|
* Ident mode |
|
||||||
|
* -----------------------+----------------------------------------
|
||||||
|
* Xfer mode |
|
||||||
|
* V
|
||||||
|
* STBY
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
pub async fn setup(hc: Arc<Sdhci<H>>) -> Result<Arc<Self>, Error> {
|
||||||
|
// GO_IDLE_STATE
|
||||||
|
hc.submit_command::<command::CMD0, _>((), ()).await?;
|
||||||
|
// SEND_IF_COND, 2.7-3.3V range
|
||||||
|
// TODO: actually check result
|
||||||
|
hc.submit_command::<command::CMD8, _>(0x1AA, ()).await?;
|
||||||
|
|
||||||
|
// TODO adjust voltage as needed
|
||||||
|
|
||||||
|
// SEND_OP_COND
|
||||||
|
let mut attempts = 10;
|
||||||
|
let mut high_capacity = false;
|
||||||
|
while attempts != 0 {
|
||||||
|
let Ok(r3) = hc
|
||||||
|
.submit_command::<command::ACMD41, _>(0x40FF8000, ())
|
||||||
|
.await
|
||||||
|
else {
|
||||||
|
runtime::sleep(Duration::from_millis(200)).await;
|
||||||
|
attempts -= 1;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
log::trace!("r3 = {r3:?}");
|
||||||
|
|
||||||
|
if r3.0.contains(OcrRegister::POWER_UP) {
|
||||||
|
high_capacity = r3.0.contains(OcrRegister::CCS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts -= 1;
|
||||||
|
runtime::sleep(Duration::from_millis(100)).await;
|
||||||
|
}
|
||||||
|
if attempts == 0 {
|
||||||
|
log::error!("ACMD41 timeout/failure");
|
||||||
|
return Err(Error::InvalidOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ALL_SEND_CID
|
||||||
|
hc.submit_command::<command::CMD2, _>((), ()).await?;
|
||||||
|
// SEND_RELATIVE_ADDR
|
||||||
|
let rca = hc
|
||||||
|
.submit_command::<command::CMD3, _>((), ())
|
||||||
|
.await?
|
||||||
|
.card_address;
|
||||||
|
|
||||||
|
// Card is now in data transfer mode
|
||||||
|
|
||||||
|
// Get card capacity
|
||||||
|
// SEND_CSD
|
||||||
|
let r2 = hc
|
||||||
|
.submit_command::<command::CMD9, _>((rca as u32) << 16, ())
|
||||||
|
.await?;
|
||||||
|
let csd = r2.parse_csd_structure()?;
|
||||||
|
|
||||||
|
hc.submit_command::<command::CMD7, _>((rca as u32) << 16, ())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Arc::new(Self {
|
||||||
|
hc,
|
||||||
|
rca,
|
||||||
|
csd,
|
||||||
|
high_capacity,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<H: SdhciHost> BlockDevice for SdCard<H> {
|
||||||
|
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
||||||
|
DmaBuffer::new_uninit_slice(self.hc.dma_allocator().as_ref(), size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO read directly to cache
|
||||||
|
async fn read_aligned(
|
||||||
|
&self,
|
||||||
|
position: u64,
|
||||||
|
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
assert!(self.high_capacity);
|
||||||
|
assert_eq!(position % 512, 0);
|
||||||
|
assert_eq!(buffer.len() % 512, 0);
|
||||||
|
|
||||||
|
let block_count = buffer.len() / 512;
|
||||||
|
let address: u32 = (position / 512)
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::InvalidArgument)?;
|
||||||
|
|
||||||
|
let (buffer, range) = unsafe { buffer.into_parts() };
|
||||||
|
for i in 0..block_count {
|
||||||
|
let mut block_slice =
|
||||||
|
buffer.slice_mut(range.start + i * 512..range.start + i * 512 + 512);
|
||||||
|
self.hc
|
||||||
|
.read_single_block_pio(address + i as u32, &mut block_slice)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write_aligned(&self, position: u64, buffer: DmaSlice<'_, u8>) -> Result<(), Error> {
|
||||||
|
assert!(self.high_capacity);
|
||||||
|
assert_eq!(position % 512, 0);
|
||||||
|
assert_eq!(buffer.len() % 512, 0);
|
||||||
|
|
||||||
|
let block_count = buffer.len() / 512;
|
||||||
|
let address: u32 = (position / 512)
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::InvalidArgument)?;
|
||||||
|
|
||||||
|
let (buffer, range) = unsafe { buffer.into_parts() };
|
||||||
|
for i in 0..block_count {
|
||||||
|
let block_slice = buffer.slice(range.start + i * 512..range.start + i * 512 + 512);
|
||||||
|
self.hc
|
||||||
|
.write_single_block_pio(address + i as u32, &block_slice)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_size(&self) -> usize {
|
||||||
|
self.csd.read_block_size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_count(&self) -> u64 {
|
||||||
|
self.csd.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_blocks_per_request(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: SdhciHost> PageProvider for SdCard<H> {
|
||||||
|
fn ondemand_fetch(&self, _opaque: u64) -> Result<OnDemandPage, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_page(&self, _offset: u64) -> Result<VirtualPage, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release_page(
|
||||||
|
&self,
|
||||||
|
_offset: u64,
|
||||||
|
_phys: PhysicalAddress,
|
||||||
|
_dirty: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_page(
|
||||||
|
&self,
|
||||||
|
_offset: u64,
|
||||||
|
_src_phys: PhysicalAddress,
|
||||||
|
_src_attrs: MapAttributes,
|
||||||
|
) -> Result<PhysicalAddress, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H: SdhciHost> Device for SdCard<H> {
|
||||||
|
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_name(&self) -> &str {
|
||||||
|
"SD Card"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
pub trait SdArgument {
|
||||||
|
fn into_u32(self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdArgument for u32 {
|
||||||
|
fn into_u32(self) -> u32 {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SdArgument for () {
|
||||||
|
fn into_u32(self) -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
pub mod argument;
|
||||||
|
pub mod register;
|
||||||
|
pub mod response;
|
||||||
|
pub mod transfer;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SdCommandIndex {
|
||||||
|
ACmd(u8),
|
||||||
|
Cmd(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SdCommand {
|
||||||
|
const INDEX: SdCommandIndex;
|
||||||
|
|
||||||
|
type Response: SdResponse = ();
|
||||||
|
type Argument: SdArgument = ();
|
||||||
|
type Transfer<'a>: SdTransfer = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use argument::SdArgument;
|
||||||
|
pub use register::{CsdStructure, CsdStructureV1, CsdStructureV2, OcrRegister};
|
||||||
|
pub use response::{R1b, SdResponse, SdResponseWidth, R1, R2, R3, R6, R7};
|
||||||
|
pub use transfer::{SdDeviceToHostTransfer, SdHostToDeviceTransfer, SdTransfer};
|
||||||
|
|
||||||
|
/// GO_IDLE_STATE
|
||||||
|
pub struct CMD0;
|
||||||
|
/// ALL_SEND_CID
|
||||||
|
pub struct CMD2;
|
||||||
|
/// SEND_RELATIVE_ADDR
|
||||||
|
pub struct CMD3;
|
||||||
|
/// SELECT/DESELECT_CARD
|
||||||
|
///
|
||||||
|
/// Argument: RCA
|
||||||
|
pub struct CMD7;
|
||||||
|
/// SEND_IF_COND
|
||||||
|
///
|
||||||
|
/// Argument: TODO
|
||||||
|
pub struct CMD8;
|
||||||
|
/// SEND_CSD
|
||||||
|
///
|
||||||
|
/// Argument: RCA
|
||||||
|
pub struct CMD9;
|
||||||
|
/// STOP_TRANSMISSION
|
||||||
|
pub struct CMD12;
|
||||||
|
/// READ_SINGLE_BLOCK
|
||||||
|
///
|
||||||
|
/// Argument: data address
|
||||||
|
pub struct CMD17;
|
||||||
|
/// READ_MULTIPLE_BLOCK
|
||||||
|
///
|
||||||
|
/// Argument: data address
|
||||||
|
pub struct CMD18;
|
||||||
|
/// WRITE_SINGLE_BLOCK
|
||||||
|
///
|
||||||
|
/// Argument: data address
|
||||||
|
pub struct CMD24;
|
||||||
|
/// APP_CMD
|
||||||
|
pub struct CMD55;
|
||||||
|
/// SD_SEND_OP_COND
|
||||||
|
///
|
||||||
|
/// Argument: TODO
|
||||||
|
pub struct ACMD41;
|
||||||
|
impl SdCommand for CMD0 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(0);
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD2 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(2);
|
||||||
|
type Argument = ();
|
||||||
|
type Response = R2;
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD3 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(3);
|
||||||
|
type Argument = ();
|
||||||
|
type Response = R6;
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD7 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(7);
|
||||||
|
type Argument = u32;
|
||||||
|
type Response = R1b;
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD8 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(8);
|
||||||
|
type Argument = u32;
|
||||||
|
type Response = R7;
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD9 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(9);
|
||||||
|
|
||||||
|
type Argument = u32;
|
||||||
|
type Response = R2;
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD12 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(12);
|
||||||
|
type Argument = ();
|
||||||
|
type Response = R1b;
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD17 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(17);
|
||||||
|
type Argument = u32;
|
||||||
|
type Response = R1;
|
||||||
|
type Transfer<'a> = SdDeviceToHostTransfer<'a>;
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD18 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(18);
|
||||||
|
type Argument = u32;
|
||||||
|
type Response = R1;
|
||||||
|
type Transfer<'a> = SdDeviceToHostTransfer<'a>;
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD24 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(24);
|
||||||
|
type Argument = u32;
|
||||||
|
type Response = R1;
|
||||||
|
type Transfer<'a> = SdHostToDeviceTransfer<'a>;
|
||||||
|
}
|
||||||
|
impl SdCommand for CMD55 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(55);
|
||||||
|
type Argument = u32;
|
||||||
|
type Response = R1;
|
||||||
|
}
|
||||||
|
impl SdCommand for ACMD41 {
|
||||||
|
const INDEX: SdCommandIndex = SdCommandIndex::ACmd(41);
|
||||||
|
type Argument = u32;
|
||||||
|
type Response = R3;
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use bitfield::BitRange;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CsdStructure {
|
||||||
|
pub read_block_size: usize,
|
||||||
|
pub write_block_size: usize,
|
||||||
|
pub capacity: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CsdStructureV1(u128);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CsdStructureV2(u128);
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct OcrRegister: u32 {
|
||||||
|
const VDD_2_7V_2_8V = 1 << 15;
|
||||||
|
const VDD_2_8V_2_9V = 1 << 16;
|
||||||
|
const VDD_2_9V_3_0V = 1 << 17;
|
||||||
|
const VDD_3_0V_3_1V = 1 << 18;
|
||||||
|
const VDD_3_1V_3_2V = 1 << 19;
|
||||||
|
const VDD_3_2V_3_3V = 1 << 20;
|
||||||
|
const VDD_3_3V_3_4V = 1 << 21;
|
||||||
|
const VDD_3_4V_3_5V = 1 << 22;
|
||||||
|
const VDD_3_5V_3_6V = 1 << 23;
|
||||||
|
const CCS = 1 << 30;
|
||||||
|
const POWER_UP = 1 << 31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe impl Pod for OcrRegister {}
|
||||||
|
unsafe impl Zeroable for OcrRegister {}
|
||||||
|
|
||||||
|
impl CsdStructureV1 {
|
||||||
|
pub fn csd_structure(&self) -> u32 {
|
||||||
|
self.0.bit_range(127 - 8, 126 - 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_bl_len(&self) -> u32 {
|
||||||
|
self.0.bit_range(83 - 8, 80 - 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn c_size(&self) -> u32 {
|
||||||
|
self.0.bit_range(73 - 8, 62 - 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn c_size_mult(&self) -> u32 {
|
||||||
|
self.0.bit_range(49 - 8, 47 - 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CsdStructureV2 {
|
||||||
|
pub fn csd_structure(&self) -> u32 {
|
||||||
|
self.0.bit_range(127 - 8, 126 - 8)
|
||||||
|
}
|
||||||
|
pub fn c_size(&self) -> u32 {
|
||||||
|
self.0.bit_range(69 - 8, 48 - 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_bl_len(&self) -> u32 {
|
||||||
|
self.0.bit_range(83 - 8, 80 - 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for CsdStructureV1 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("CsdStructureV1")
|
||||||
|
.field("CSD_STRUCTURE", &self.csd_structure())
|
||||||
|
.field("READ_BL_LEN", &self.read_bl_len())
|
||||||
|
.field("C_SIZE", &self.c_size())
|
||||||
|
.field("C_SIZE_MULT", &self.c_size_mult())
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for CsdStructureV2 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("CsdStructureV2")
|
||||||
|
.field("CSD_STRUCTURE", &self.csd_structure())
|
||||||
|
.field("READ_BL_LEN", &self.read_bl_len())
|
||||||
|
.field("C_SIZE", &self.c_size())
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
use libk::error::Error;
|
||||||
|
|
||||||
|
use super::{register::OcrRegister, CsdStructure, CsdStructureV1, CsdStructureV2};
|
||||||
|
|
||||||
|
pub enum SdResponseWidth {
|
||||||
|
None,
|
||||||
|
W48,
|
||||||
|
W48b,
|
||||||
|
W136,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SdResponse {
|
||||||
|
const BIT_WIDTH: SdResponseWidth;
|
||||||
|
const CRC_CHECK: bool = true;
|
||||||
|
const INDEX_CHECK: bool = false;
|
||||||
|
|
||||||
|
fn from_bits(bits: &[u32; 4]) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct R1(pub u32);
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct R1b(pub u32);
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct R2(pub [u32; 4]);
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct R3(pub OcrRegister);
|
||||||
|
/// R6 - Published RCA Response
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct R6 {
|
||||||
|
pub card_status: u16,
|
||||||
|
pub card_address: u16,
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct R7(pub u32);
|
||||||
|
|
||||||
|
impl SdResponse for () {
|
||||||
|
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::None;
|
||||||
|
const CRC_CHECK: bool = false;
|
||||||
|
|
||||||
|
fn from_bits(_bits: &[u32; 4]) -> Self {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdResponse for R1 {
|
||||||
|
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
|
||||||
|
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||||
|
Self(bits[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdResponse for R1b {
|
||||||
|
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48b;
|
||||||
|
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||||
|
Self(bits[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdResponse for R2 {
|
||||||
|
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W136;
|
||||||
|
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||||
|
Self(*bits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl R2 {
|
||||||
|
pub fn parse_csd_structure(&self) -> Result<CsdStructure, Error> {
|
||||||
|
let v1 = bytemuck::cast::<_, CsdStructureV1>(self.0);
|
||||||
|
match v1.csd_structure() {
|
||||||
|
0 => {
|
||||||
|
// block_size = 1 << READ_BL_LEN
|
||||||
|
// size_mult = 1 << (C_SIZE_MULT + 2)
|
||||||
|
// capacity = (C_SIZE + 1) * size_mult [blocks]
|
||||||
|
let size_mult = 1usize << (v1.c_size_mult() + 2);
|
||||||
|
Ok(CsdStructure {
|
||||||
|
read_block_size: 1usize << v1.read_bl_len(),
|
||||||
|
write_block_size: 512, // TODO
|
||||||
|
capacity: (v1.c_size() as u64 + 1) * (size_mult as u64),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
// block_size = 512
|
||||||
|
// capacity = (C_SIZE + 1) * block_size * 1024 [bytes]
|
||||||
|
let v2 = bytemuck::cast::<_, CsdStructureV2>(self.0);
|
||||||
|
|
||||||
|
Ok(CsdStructure {
|
||||||
|
read_block_size: 512,
|
||||||
|
write_block_size: 512,
|
||||||
|
capacity: (v2.c_size() as u64 + 1) * 1024,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(Error::InvalidArgument),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdResponse for R3 {
|
||||||
|
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
|
||||||
|
const CRC_CHECK: bool = false;
|
||||||
|
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||||
|
Self(bytemuck::cast(bits[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdResponse for R6 {
|
||||||
|
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
|
||||||
|
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||||
|
Self {
|
||||||
|
card_address: (bits[0] >> 16) as u16,
|
||||||
|
card_status: bits[0] as u16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdResponse for R7 {
|
||||||
|
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
|
||||||
|
const INDEX_CHECK: bool = true;
|
||||||
|
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||||
|
Self(bits[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
use core::{future, mem::MaybeUninit};
|
||||||
|
|
||||||
|
use libk::{
|
||||||
|
dma::{DmaSlice, DmaSliceMut},
|
||||||
|
error::Error,
|
||||||
|
};
|
||||||
|
use tock_registers::fields::FieldValue;
|
||||||
|
|
||||||
|
use crate::{host::SdhciHost, regs, Sdhci};
|
||||||
|
|
||||||
|
pub trait SdTransfer {
|
||||||
|
fn perform_transfer<H: SdhciHost>(
|
||||||
|
self,
|
||||||
|
hc: &Sdhci<H>,
|
||||||
|
) -> impl Future<Output = Result<(), Error>>;
|
||||||
|
|
||||||
|
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register>;
|
||||||
|
fn block_count(&self) -> u16;
|
||||||
|
fn block_size(&self) -> u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SdDeviceToHostTransfer<'a>(&'a mut DmaSliceMut<'a, MaybeUninit<u8>>);
|
||||||
|
pub struct SdHostToDeviceTransfer<'a>(&'a DmaSlice<'a, u8>);
|
||||||
|
|
||||||
|
impl<'a> From<&'a mut DmaSliceMut<'a, MaybeUninit<u8>>> for SdDeviceToHostTransfer<'a> {
|
||||||
|
fn from(value: &'a mut DmaSliceMut<'a, MaybeUninit<u8>>) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdTransfer for SdDeviceToHostTransfer<'_> {
|
||||||
|
fn perform_transfer<H: SdhciHost>(
|
||||||
|
self,
|
||||||
|
hc: &Sdhci<H>,
|
||||||
|
) -> impl Future<Output = Result<(), Error>> {
|
||||||
|
async move {
|
||||||
|
let block_count = self.block_count() as usize;
|
||||||
|
let block_size = self.block_size() as usize;
|
||||||
|
|
||||||
|
for i in 0..block_count {
|
||||||
|
let off = i * block_size;
|
||||||
|
let slice = &mut self.0[off..off + block_size];
|
||||||
|
|
||||||
|
hc.receive_single_block_pio(slice, block_size).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
hc.wait_for_interrupt(regs::EVENT_MASK::TRANSFER_COMPLETE::SET)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register> {
|
||||||
|
(if self.block_count() > 1 {
|
||||||
|
regs::COMMAND::MULTI_BLOCK_SELECT::MultiBlock
|
||||||
|
} else {
|
||||||
|
regs::COMMAND::MULTI_BLOCK_SELECT::SingleBlock
|
||||||
|
}) + regs::COMMAND::DATA_PRESENT_SELECT::SET
|
||||||
|
+ regs::COMMAND::DATA_TRANSFER_DIRECTION::CardToHost
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_size(&self) -> u16 {
|
||||||
|
// TODO
|
||||||
|
512
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_count(&self) -> u16 {
|
||||||
|
(self.0.len() / 512).try_into().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a DmaSlice<'a, u8>> for SdHostToDeviceTransfer<'a> {
|
||||||
|
fn from(value: &'a DmaSlice<'a, u8>) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdTransfer for SdHostToDeviceTransfer<'_> {
|
||||||
|
fn perform_transfer<H: SdhciHost>(
|
||||||
|
self,
|
||||||
|
hc: &Sdhci<H>,
|
||||||
|
) -> impl Future<Output = Result<(), Error>> {
|
||||||
|
async move {
|
||||||
|
let block_count = self.block_count() as usize;
|
||||||
|
let block_size = self.block_size() as usize;
|
||||||
|
|
||||||
|
for i in 0..block_count {
|
||||||
|
let off = i * block_size;
|
||||||
|
let slice = &self.0[off..off + block_size];
|
||||||
|
|
||||||
|
hc.send_single_block_pio(slice, block_size).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
hc.wait_for_interrupt(regs::EVENT_MASK::TRANSFER_COMPLETE::SET)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register> {
|
||||||
|
(if self.block_count() > 1 {
|
||||||
|
regs::COMMAND::MULTI_BLOCK_SELECT::MultiBlock
|
||||||
|
} else {
|
||||||
|
regs::COMMAND::MULTI_BLOCK_SELECT::SingleBlock
|
||||||
|
}) + regs::COMMAND::DATA_PRESENT_SELECT::SET
|
||||||
|
+ regs::COMMAND::DATA_TRANSFER_DIRECTION::HostToCard
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_size(&self) -> u16 {
|
||||||
|
// TODO
|
||||||
|
512
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_count(&self) -> u16 {
|
||||||
|
(self.0.len() / 512).try_into().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdTransfer for () {
|
||||||
|
fn perform_transfer<H: SdhciHost>(
|
||||||
|
self,
|
||||||
|
_hc: &Sdhci<H>,
|
||||||
|
) -> impl Future<Output = Result<(), Error>> {
|
||||||
|
future::ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register> {
|
||||||
|
FieldValue::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_size(&self) -> u16 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_count(&self) -> u16 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
use core::cell::UnsafeCell;
|
||||||
|
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use device_api::{
|
||||||
|
device::{Device, DeviceInitContext},
|
||||||
|
dma::DmaAllocator,
|
||||||
|
interrupt::{InterruptHandler, IrqHandle},
|
||||||
|
};
|
||||||
|
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||||
|
use libk::error::Error;
|
||||||
|
use libk_mm::device::DeviceMemoryIoMut;
|
||||||
|
use libk_util::OneTimeInit;
|
||||||
|
|
||||||
|
use crate::Sdhci;
|
||||||
|
|
||||||
|
pub trait SdhciHost: Send + Sync + 'static {
|
||||||
|
unsafe fn init(&self, cx: DeviceInitContext) -> Result<(), Error>;
|
||||||
|
unsafe fn init_irq(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error>;
|
||||||
|
|
||||||
|
fn dma_allocator(&self) -> &Arc<dyn DmaAllocator>;
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
|
fn read4(&self, reg: usize) -> u32;
|
||||||
|
fn read2(&self, reg: usize) -> u16;
|
||||||
|
fn read1(&self, reg: usize) -> u8;
|
||||||
|
fn write4(&self, reg: usize, val: u32);
|
||||||
|
fn write2(&self, reg: usize, val: u16);
|
||||||
|
fn write1(&self, reg: usize, val: u8);
|
||||||
|
|
||||||
|
// max_clock(), set_clock(...)
|
||||||
|
// reset()
|
||||||
|
// set_bus_width(...)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IprocSdhci {
|
||||||
|
// base: PhysicalAddress,
|
||||||
|
mmio: UnsafeCell<DeviceMemoryIoMut<'static, [u32]>>,
|
||||||
|
irq: IrqHandle,
|
||||||
|
name: &'static str,
|
||||||
|
|
||||||
|
dma: OneTimeInit<Arc<dyn DmaAllocator>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Sync for IprocSdhci {}
|
||||||
|
unsafe impl Send for IprocSdhci {}
|
||||||
|
|
||||||
|
impl SdhciHost for IprocSdhci {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn init(&self, cx: DeviceInitContext) -> Result<(), Error> {
|
||||||
|
self.dma.init(cx.dma_allocator);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn init_irq(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
|
||||||
|
self.irq.register(handler)?;
|
||||||
|
self.irq.enable()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dma_allocator(&self) -> &Arc<dyn DmaAllocator> {
|
||||||
|
self.dma.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read4(&self, reg: usize) -> u32 {
|
||||||
|
assert_eq!(reg % 4, 0);
|
||||||
|
let mmio = unsafe { &mut *self.mmio.get() };
|
||||||
|
unsafe { core::ptr::read_volatile(&mmio[reg / 4]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read2(&self, reg: usize) -> u16 {
|
||||||
|
assert_eq!(reg % 2, 0);
|
||||||
|
let mmio = unsafe { &mut *self.mmio.get() };
|
||||||
|
let address32 = reg / 4;
|
||||||
|
let shift32 = (reg & 2) << 3;
|
||||||
|
let val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
|
||||||
|
(val32 >> shift32) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read1(&self, reg: usize) -> u8 {
|
||||||
|
let mmio = unsafe { &mut *self.mmio.get() };
|
||||||
|
let address32 = reg / 4;
|
||||||
|
let shift32 = (reg & 3) << 3;
|
||||||
|
let val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
|
||||||
|
(val32 >> shift32) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write4(&self, reg: usize, val: u32) {
|
||||||
|
assert_eq!(reg % 4, 0);
|
||||||
|
let mmio = unsafe { &mut *self.mmio.get() };
|
||||||
|
unsafe { core::ptr::write_volatile(&mut mmio[reg / 4], val) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write2(&self, reg: usize, val: u16) {
|
||||||
|
let mmio = unsafe { &mut *self.mmio.get() };
|
||||||
|
let address32 = reg / 4;
|
||||||
|
let shift32 = (reg & 2) << 3;
|
||||||
|
let mut val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
|
||||||
|
val32 &= !(0xFFFF << shift32);
|
||||||
|
val32 |= (val as u32) << shift32;
|
||||||
|
unsafe { core::ptr::write_volatile(&mut mmio[address32], val32) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write1(&self, reg: usize, val: u8) {
|
||||||
|
let mmio = unsafe { &mut *self.mmio.get() };
|
||||||
|
let address32 = reg / 4;
|
||||||
|
let shift32 = (reg & 3) << 3;
|
||||||
|
let mut val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
|
||||||
|
val32 &= !(0xFF << shift32);
|
||||||
|
val32 |= (val as u32) << shift32;
|
||||||
|
unsafe { core::ptr::write_volatile(&mut mmio[address32], val32) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device_tree_driver! {
|
||||||
|
// compatible: ["brcm,bcm2835-mmc", "brcm,bcm2835-sdhci"],
|
||||||
|
compatible: ["brcm,bcm2711-emmc2"],
|
||||||
|
driver: {
|
||||||
|
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||||
|
let base = node.map_base(context, 0)?;
|
||||||
|
let irq = node.interrupt(0)?;
|
||||||
|
let name = node.name()?;
|
||||||
|
|
||||||
|
let mmio = UnsafeCell::new(unsafe {
|
||||||
|
DeviceMemoryIoMut::map_slice(
|
||||||
|
base, 0x100 / 4, Default::default()
|
||||||
|
)
|
||||||
|
.inspect_err(|e| log::error!("{name}: memory mapping error {e:?}"))
|
||||||
|
.ok()?
|
||||||
|
});
|
||||||
|
|
||||||
|
let hc = IprocSdhci {
|
||||||
|
mmio,
|
||||||
|
irq,
|
||||||
|
name,
|
||||||
|
dma: OneTimeInit::new(),
|
||||||
|
};
|
||||||
|
let sdhc = Sdhci::from_host(hc).inspect_err(|e| log::error!("{name}: {e:?}")).ok()?;
|
||||||
|
|
||||||
|
Some(Arc::new(sdhc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,772 @@
|
|||||||
|
#![feature(associated_type_defaults, maybe_uninit_slice)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::{
|
||||||
|
future::poll_fn,
|
||||||
|
mem::MaybeUninit,
|
||||||
|
ops::BitAndAssign,
|
||||||
|
sync::atomic::{AtomicBool, AtomicU32, Ordering},
|
||||||
|
task::Poll,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use alloc::{format, sync::Arc};
|
||||||
|
use card::SdCard;
|
||||||
|
use command::{SdArgument, SdCommand, SdCommandIndex, SdResponse, SdResponseWidth, SdTransfer};
|
||||||
|
use device_api::{
|
||||||
|
clock::{Hertz, IntoHertz},
|
||||||
|
device::{Device, DeviceInitContext},
|
||||||
|
dma::DmaAllocator,
|
||||||
|
interrupt::{InterruptHandler, IrqVector},
|
||||||
|
};
|
||||||
|
use futures_util::task::AtomicWaker;
|
||||||
|
use host::SdhciHost;
|
||||||
|
use libk::{
|
||||||
|
device::manager::probe_partitions,
|
||||||
|
dma::{DmaSlice, DmaSliceMut},
|
||||||
|
error::Error,
|
||||||
|
fs::devfs,
|
||||||
|
task::{runtime, sync::AsyncMutex},
|
||||||
|
};
|
||||||
|
use libk_util::OneTimeInit;
|
||||||
|
use regs::{
|
||||||
|
Reg, CLOCK_CONTROL, EVENT_MASK, INTERRUPT_SIGNAL_ENABLE, INTERRUPT_STATUS_ENABLE,
|
||||||
|
POWER_CONTROL, PRESENT_STATE, SOFTWARE_RESET,
|
||||||
|
};
|
||||||
|
use tock_registers::{fields::FieldValue, LocalRegisterCopy, RegisterLongName, UIntLike};
|
||||||
|
use yggdrasil_abi::io::FileMode;
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
pub mod card;
|
||||||
|
pub mod command;
|
||||||
|
pub mod host;
|
||||||
|
pub mod regs;
|
||||||
|
|
||||||
|
pub enum ClockMode {
|
||||||
|
None,
|
||||||
|
Internal,
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Capabilities {
|
||||||
|
pub version: HostVersion,
|
||||||
|
pub clock_multiplier: Option<u32>,
|
||||||
|
pub voltage_support_1_8v: bool,
|
||||||
|
pub voltage_support_3_0v: bool,
|
||||||
|
pub voltage_support_3_3v: bool,
|
||||||
|
pub sdma_support: bool,
|
||||||
|
pub adma2_support: bool,
|
||||||
|
pub base_frequency: Option<Hertz>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum HostVersion {
|
||||||
|
V1_00,
|
||||||
|
V2_00,
|
||||||
|
V3_00,
|
||||||
|
V4_00,
|
||||||
|
V4_10,
|
||||||
|
V4_20,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SdhciInner<H: SdhciHost> {
|
||||||
|
capabilities: Capabilities,
|
||||||
|
host: H,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InterruptStatus {
|
||||||
|
interrupt_status: AtomicU32,
|
||||||
|
notify: AtomicWaker,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sdhci<H: SdhciHost> {
|
||||||
|
inner: SdhciInner<H>,
|
||||||
|
dma: OneTimeInit<Arc<dyn DmaAllocator>>,
|
||||||
|
|
||||||
|
interrupt_status: InterruptStatus,
|
||||||
|
command_lock: AsyncMutex<()>,
|
||||||
|
|
||||||
|
card_present: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptStatus {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
interrupt_status: AtomicU32::new(0),
|
||||||
|
notify: AtomicWaker::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signal(&self, value: u32) {
|
||||||
|
self.interrupt_status.fetch_or(value, Ordering::Release);
|
||||||
|
self.notify.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait(&self, mask: u32) -> LocalRegisterCopy<u32, EVENT_MASK::Register> {
|
||||||
|
let not_mask = !mask;
|
||||||
|
poll_fn(|cx| {
|
||||||
|
let value = self.interrupt_status.fetch_and(not_mask, Ordering::Acquire);
|
||||||
|
if value & mask != 0 {
|
||||||
|
Poll::Ready(LocalRegisterCopy::new(value))
|
||||||
|
} else {
|
||||||
|
self.notify.register(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl<H: SdhciHost> From<H> for Sdhci<H> {
|
||||||
|
// fn from(value: H) -> Self {
|
||||||
|
// Self {
|
||||||
|
// inner: SdhciInner { host: value },
|
||||||
|
//
|
||||||
|
// interrupt_status: InterruptStatus::new(),
|
||||||
|
// command_lock: AsyncMutex::new(()),
|
||||||
|
//
|
||||||
|
// card_present: AtomicBool::new(false),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl TryFrom<u16> for HostVersion {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||||
|
match value & 0xFF {
|
||||||
|
0x00 => Ok(Self::V1_00),
|
||||||
|
0x01 => Ok(Self::V2_00),
|
||||||
|
0x02 => Ok(Self::V3_00),
|
||||||
|
0x03 => Ok(Self::V4_00),
|
||||||
|
0x04 => Ok(Self::V4_10),
|
||||||
|
0x05 => Ok(Self::V4_20),
|
||||||
|
_ => Err(Error::InvalidArgument),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Capabilities {
|
||||||
|
fn from_host<H: SdhciHost>(host: &H) -> Result<Self, Error> {
|
||||||
|
let version = HostVersion::try_from(host.read2(0xFE))?;
|
||||||
|
log::info!("HC version: {version:?}");
|
||||||
|
|
||||||
|
let reg_caps_0 = host.read4(regs::CAPABILITIES::Register::ADDRESS);
|
||||||
|
let reg_caps_1 = host.read4(regs::CAPABILITIES::Register::ADDRESS + 4);
|
||||||
|
let reg_caps = LocalRegisterCopy::<u64, regs::CAPABILITIES::Register>::new(
|
||||||
|
(reg_caps_0 as u64) | ((reg_caps_1 as u64) << 32),
|
||||||
|
);
|
||||||
|
|
||||||
|
let base_frequency = reg_caps.read(regs::CAPABILITIES::BASE_CLOCK_FREQ);
|
||||||
|
let base_frequency = if base_frequency == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
if version < HostVersion::V3_00 {
|
||||||
|
assert_eq!(base_frequency & !0b111111, 0);
|
||||||
|
}
|
||||||
|
Some(base_frequency.mhz())
|
||||||
|
};
|
||||||
|
let clock_multiplier = match reg_caps.read(regs::CAPABILITIES::BASE_CLOCK_FREQ) {
|
||||||
|
0 => None,
|
||||||
|
n => Some(n as u32 + 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(base_frequency) = base_frequency {
|
||||||
|
log::info!("SD base frequency: {base_frequency}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
version,
|
||||||
|
base_frequency,
|
||||||
|
clock_multiplier,
|
||||||
|
adma2_support: reg_caps.matches_all(regs::CAPABILITIES::ADMA2_SUPPORT::SET),
|
||||||
|
sdma_support: reg_caps.matches_all(regs::CAPABILITIES::SDMA_SUPPORT::SET),
|
||||||
|
voltage_support_1_8v: reg_caps.matches_all(regs::CAPABILITIES::VOLTAGE_1_8V::SET),
|
||||||
|
voltage_support_3_0v: reg_caps.matches_all(regs::CAPABILITIES::VOLTAGE_3_0V::SET),
|
||||||
|
voltage_support_3_3v: reg_caps.matches_all(regs::CAPABILITIES::VOLTAGE_3_3V::SET),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: SdhciHost> Sdhci<H> {
|
||||||
|
pub fn from_host(host: H) -> Result<Self, Error> {
|
||||||
|
let capabilities = Capabilities::from_host(&host)?;
|
||||||
|
let inner = SdhciInner { capabilities, host };
|
||||||
|
Ok(Self {
|
||||||
|
inner,
|
||||||
|
dma: OneTimeInit::new(),
|
||||||
|
interrupt_status: InterruptStatus::new(),
|
||||||
|
command_lock: AsyncMutex::new(()),
|
||||||
|
card_present: AtomicBool::new(false),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn late_init(self: Arc<Self>) -> Result<(), Error> {
|
||||||
|
log::info!("{}: initialize", self.display_name());
|
||||||
|
|
||||||
|
self.inner.software_reset().await?;
|
||||||
|
self.inner.set_default_interrupt_mask();
|
||||||
|
self.inner.set_vdd1_power();
|
||||||
|
self.inner.set_clock_rate(400u64.khz()).await?;
|
||||||
|
self.inner.set_clock_mode(ClockMode::All).await?;
|
||||||
|
|
||||||
|
if self.inner.is_card_inserted() {
|
||||||
|
self.card_present.store(true, Ordering::Release);
|
||||||
|
runtime::spawn(self.clone().card_task())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_setup_card(self: &Arc<Self>) -> Result<Arc<SdCard<H>>, Error> {
|
||||||
|
// Enable SD bus clock
|
||||||
|
self.inner.set_clock_mode(ClockMode::All).await?;
|
||||||
|
|
||||||
|
let mut last_error = Error::InvalidArgument;
|
||||||
|
for _ in 0..3 {
|
||||||
|
match SdCard::setup(self.clone()).await {
|
||||||
|
Ok(card) => return Ok(card),
|
||||||
|
Err(error) => {
|
||||||
|
log::error!("Card setup error: {error:?}");
|
||||||
|
self.inner.set_clock_mode(ClockMode::Internal).await?;
|
||||||
|
|
||||||
|
last_error = error;
|
||||||
|
runtime::sleep(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(last_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn card_task(self: Arc<Self>) {
|
||||||
|
log::info!("Card inserted, try setup");
|
||||||
|
|
||||||
|
let card = match Self::try_setup_card(&self).await {
|
||||||
|
Ok(card) => card,
|
||||||
|
Err(error) => {
|
||||||
|
log::error!("Card setup error: {error:?}");
|
||||||
|
self.card_present.store(false, Ordering::Release);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"Set up card: rca={:#04x}, capacity={}GiB",
|
||||||
|
card.rca,
|
||||||
|
(card.csd.capacity * card.csd.read_block_size as u64) / (1024 * 1024 * 1024)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register card as a block device
|
||||||
|
register_sdcard(card, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn receive_single_block_pio(
|
||||||
|
&self,
|
||||||
|
buffer: &mut [MaybeUninit<u8>],
|
||||||
|
block_size: usize,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
assert_eq!(block_size % 4, 0);
|
||||||
|
|
||||||
|
let words = bytemuck::cast_slice_mut::<_, u32>(unsafe { buffer.assume_init_mut() });
|
||||||
|
|
||||||
|
self.wait_for_interrupt(EVENT_MASK::BUFFER_READ_READY::SET)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
for word in words {
|
||||||
|
*word = self.inner.get::<regs::BUFFER_DATA::Register>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_single_block_pio(&self, buffer: &[u8], block_size: usize) -> Result<(), Error> {
|
||||||
|
assert_eq!(block_size % 4, 0);
|
||||||
|
|
||||||
|
let words = bytemuck::cast_slice::<_, u32>(buffer);
|
||||||
|
|
||||||
|
for word in words {
|
||||||
|
self.wait_for_interrupt(EVENT_MASK::BUFFER_WRITE_READY::SET)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.inner.set::<regs::BUFFER_DATA::Register>(*word);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_single_block_pio<'a>(
|
||||||
|
&self,
|
||||||
|
address: u32,
|
||||||
|
buffer: &'a mut DmaSliceMut<'a, MaybeUninit<u8>>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
assert_eq!(buffer.len(), 512);
|
||||||
|
self.submit_command::<command::CMD17, _>(address, buffer)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| log::error!("CMD17 error: {e:?}"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_single_block_pio<'a>(
|
||||||
|
&self,
|
||||||
|
address: u32,
|
||||||
|
buffer: &'a DmaSlice<'a, u8>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
assert_eq!(buffer.len(), 512);
|
||||||
|
self.submit_command::<command::CMD24, _>(address, buffer)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn submit_command<'a, C: SdCommand, T: Into<C::Transfer<'a>>>(
|
||||||
|
&self,
|
||||||
|
argument: C::Argument,
|
||||||
|
transfer: T,
|
||||||
|
) -> Result<C::Response, Error> {
|
||||||
|
let transfer = transfer.into();
|
||||||
|
let argument = argument.into_u32();
|
||||||
|
|
||||||
|
let _guard = self.command_lock.lock().await;
|
||||||
|
|
||||||
|
let response_bits = match C::INDEX {
|
||||||
|
SdCommandIndex::Cmd(index) => {
|
||||||
|
log::trace!(">> CMD{index} : {argument:#x}");
|
||||||
|
self.submit_command_inner::<C>(argument, index, Some(&transfer))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
SdCommandIndex::ACmd(index) => {
|
||||||
|
log::trace!(">> ACMD{index} : {argument:#x}");
|
||||||
|
self.submit_command_inner::<command::CMD55>(0, 55, None)
|
||||||
|
.await?;
|
||||||
|
self.submit_command_inner::<C>(argument, index, Some(&transfer))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
log::trace!("<< {:x?}", response_bits);
|
||||||
|
let response = C::Response::from_bits(&response_bits);
|
||||||
|
|
||||||
|
transfer.perform_transfer(self).await?;
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn submit_command_inner<'a, C: SdCommand>(
|
||||||
|
&self,
|
||||||
|
argument: u32,
|
||||||
|
index: u8,
|
||||||
|
transfer: Option<&C::Transfer<'a>>,
|
||||||
|
) -> Result<[u32; 4], Error> {
|
||||||
|
// TODO only wait on DAT inhbit if DAT line is required
|
||||||
|
self.inner
|
||||||
|
.wait_inhibit(true, true, Duration::from_secs(1))
|
||||||
|
.await
|
||||||
|
.inspect_err(|_| log::warn!("CMD/DAT inhbit ready timeout"))?;
|
||||||
|
|
||||||
|
let response_type = match C::Response::BIT_WIDTH {
|
||||||
|
SdResponseWidth::None => regs::COMMAND::RESPONSE_TYPE::None,
|
||||||
|
SdResponseWidth::W48 => regs::COMMAND::RESPONSE_TYPE::Response48,
|
||||||
|
SdResponseWidth::W48b => regs::COMMAND::RESPONSE_TYPE::Response48b,
|
||||||
|
SdResponseWidth::W136 => regs::COMMAND::RESPONSE_TYPE::Response136,
|
||||||
|
};
|
||||||
|
|
||||||
|
// let status = self.grab_command().await;
|
||||||
|
let index_check = if C::Response::INDEX_CHECK {
|
||||||
|
regs::COMMAND::COMMAND_INDEX_CHECK::SET
|
||||||
|
} else {
|
||||||
|
regs::COMMAND::COMMAND_INDEX_CHECK::CLEAR
|
||||||
|
};
|
||||||
|
let crc_check = if C::Response::CRC_CHECK {
|
||||||
|
regs::COMMAND::COMMAND_CRC_CHECK::SET
|
||||||
|
} else {
|
||||||
|
regs::COMMAND::COMMAND_CRC_CHECK::CLEAR
|
||||||
|
};
|
||||||
|
|
||||||
|
let command_bits = if let Some(transfer) = transfer {
|
||||||
|
self.inner
|
||||||
|
.set::<regs::BLOCK_SIZE::Register>(transfer.block_size());
|
||||||
|
self.inner
|
||||||
|
.set::<regs::BLOCK_COUNT::Register>(transfer.block_count());
|
||||||
|
|
||||||
|
transfer.command_bits() + regs::COMMAND::DATA_PRESENT_SELECT::SET
|
||||||
|
} else {
|
||||||
|
FieldValue::none()
|
||||||
|
};
|
||||||
|
self.inner.set::<regs::ARGUMENT::Register>(argument);
|
||||||
|
self.inner.write(
|
||||||
|
response_type
|
||||||
|
+ index_check
|
||||||
|
+ crc_check
|
||||||
|
+ command_bits
|
||||||
|
+ regs::COMMAND::COMMAND_TYPE::Normal
|
||||||
|
+ regs::COMMAND::COMMAND_INDEX.val(index as u32),
|
||||||
|
);
|
||||||
|
|
||||||
|
match runtime::with_timeout(
|
||||||
|
self.wait_for_interrupt(EVENT_MASK::COMMAND_COMPLETE::SET),
|
||||||
|
Duration::from_secs(1),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(res) => {
|
||||||
|
res?;
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// TODO abort the command?
|
||||||
|
log::warn!("Command complete timeout");
|
||||||
|
return Err(Error::TimedOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let response_bits = match C::Response::BIT_WIDTH {
|
||||||
|
SdResponseWidth::None => [0; 4],
|
||||||
|
SdResponseWidth::W48 | SdResponseWidth::W48b => self.inner.read_response_48(),
|
||||||
|
SdResponseWidth::W136 => self.inner.read_response_136(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(response_bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_interrupt(
|
||||||
|
&self,
|
||||||
|
mask: FieldValue<u32, EVENT_MASK::Register>,
|
||||||
|
) -> Result<LocalRegisterCopy<u32, EVENT_MASK::Register>, Error> {
|
||||||
|
// Include errors
|
||||||
|
let mask = mask.value | 0xFFFF0000;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let status = self.interrupt_status.wait(mask).await;
|
||||||
|
|
||||||
|
if status.get() & 0xFFFF0000 != 0 {
|
||||||
|
todo!("Handle errors")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise matches the expected mask
|
||||||
|
break Ok(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dma_allocator(&self) -> &Arc<dyn DmaAllocator> {
|
||||||
|
self.dma.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: SdhciHost> Device for Sdhci<H> {
|
||||||
|
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
|
||||||
|
self.dma.init(cx.dma_allocator.clone());
|
||||||
|
unsafe { self.inner.host.init(cx) }?;
|
||||||
|
unsafe { self.inner.host.init_irq(self.clone()) }?;
|
||||||
|
runtime::spawn(self.clone().late_init())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_name(&self) -> &str {
|
||||||
|
self.inner.host.name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: SdhciHost> InterruptHandler for Sdhci<H> {
|
||||||
|
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||||
|
let status = self.inner.clear_status();
|
||||||
|
self.interrupt_status.signal(status.get());
|
||||||
|
status.get() != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RegisterAccess<T: UIntLike> {
|
||||||
|
fn read_register(&self, address: usize) -> T;
|
||||||
|
fn write_register(&self, address: usize, value: T);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: SdhciHost> RegisterAccess<u8> for SdhciInner<H> {
|
||||||
|
fn read_register(&self, address: usize) -> u8 {
|
||||||
|
self.host.read1(address)
|
||||||
|
}
|
||||||
|
fn write_register(&self, address: usize, value: u8) {
|
||||||
|
self.host.write1(address, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H: SdhciHost> RegisterAccess<u16> for SdhciInner<H> {
|
||||||
|
fn read_register(&self, address: usize) -> u16 {
|
||||||
|
self.host.read2(address)
|
||||||
|
}
|
||||||
|
fn write_register(&self, address: usize, value: u16) {
|
||||||
|
self.host.write2(address, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H: SdhciHost> RegisterAccess<u32> for SdhciInner<H> {
|
||||||
|
fn read_register(&self, address: usize) -> u32 {
|
||||||
|
self.host.read4(address)
|
||||||
|
}
|
||||||
|
fn write_register(&self, address: usize, value: u32) {
|
||||||
|
self.host.write4(address, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: SdhciHost> SdhciInner<H> {
|
||||||
|
// async fn wait_command_inhibit(&self, timeout: Duration) -> Result<(), Error> {
|
||||||
|
// const POLL: Duration = Duration::from_millis(25);
|
||||||
|
|
||||||
|
// let deadline = monotonic_time() + timeout;
|
||||||
|
|
||||||
|
// while self.command_inhibit() || self.data_inhibit() {
|
||||||
|
// let now = monotonic_time();
|
||||||
|
// if now >= deadline {
|
||||||
|
// log::error!("sdhci: timeout waiting for command inhibit");
|
||||||
|
// return Err(Error::TimedOut);
|
||||||
|
// }
|
||||||
|
// let sleep = deadline.sub_time(&now).unwrap_or_default().min(POLL);
|
||||||
|
// runtime::sleep(sleep).await;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn command_inhibit(&self) -> bool {
|
||||||
|
// self.matches_all(regs::PRESENT_STATE::COMMAND_INHIBIT_CMD::SET)
|
||||||
|
// }
|
||||||
|
// fn data_inhibit(&self) -> bool {
|
||||||
|
// self.matches_all(regs::PRESENT_STATE::COMMAND_INHIBIT_DAT::SET)
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn write<R: Reg + RegisterLongName>(&self, value: FieldValue<R::Repr, R>)
|
||||||
|
where
|
||||||
|
Self: RegisterAccess<R::Repr>,
|
||||||
|
{
|
||||||
|
self.set::<R>(value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn modify<R: Reg + RegisterLongName>(&self, modify: FieldValue<R::Repr, R>)
|
||||||
|
where
|
||||||
|
Self: RegisterAccess<R::Repr>,
|
||||||
|
R::Repr: BitAndAssign,
|
||||||
|
{
|
||||||
|
let mut value = self.get::<R>();
|
||||||
|
value &= !modify.mask();
|
||||||
|
value |= modify.value;
|
||||||
|
self.set::<R>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches_all<R: Reg + RegisterLongName>(&self, pattern: FieldValue<R::Repr, R>) -> bool
|
||||||
|
where
|
||||||
|
Self: RegisterAccess<R::Repr>,
|
||||||
|
{
|
||||||
|
pattern.matches_all(self.get::<R>())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get<R: Reg>(&self) -> R::Repr
|
||||||
|
where
|
||||||
|
Self: RegisterAccess<R::Repr>,
|
||||||
|
{
|
||||||
|
self.read_register(R::ADDRESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set<R: Reg>(&self, value: R::Repr)
|
||||||
|
where
|
||||||
|
Self: RegisterAccess<R::Repr>,
|
||||||
|
{
|
||||||
|
self.write_register(R::ADDRESS, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn software_reset(&self) -> Result<(), Error> {
|
||||||
|
self.write(SOFTWARE_RESET::SOFTWARE_RESET_ALL::SET);
|
||||||
|
runtime::sleep_wait(Duration::from_secs(1), Duration::from_millis(20), || {
|
||||||
|
self.matches_all(SOFTWARE_RESET::SOFTWARE_RESET_ALL::CLEAR)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
// self.write(SOFTWARE_RESET::SOFTWARE_RESET_ALL::SET);
|
||||||
|
// let mut timeout = 1000000;
|
||||||
|
// while timeout > 0 {
|
||||||
|
// if self.matches_all(SOFTWARE_RESET::SOFTWARE_RESET_ALL::CLEAR) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// timeout -= 1;
|
||||||
|
// }
|
||||||
|
// if timeout == 0 {
|
||||||
|
// Err(Error::TimedOut)
|
||||||
|
// } else {
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn wait_inhibit(
|
||||||
|
&self,
|
||||||
|
wait_cmd: bool,
|
||||||
|
wait_dat: bool,
|
||||||
|
timeout: Duration,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
runtime::sleep_wait(timeout, Duration::from_millis(20), || {
|
||||||
|
(!wait_cmd || self.matches_all(PRESENT_STATE::COMMAND_INHIBIT_CMD::CLEAR))
|
||||||
|
&& (!wait_dat || self.matches_all(PRESENT_STATE::COMMAND_INHIBIT_DAT::CLEAR))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn wait_clock_stable(&self) -> Result<(), Error> {
|
||||||
|
// TODO doesn't work for some reason
|
||||||
|
runtime::sleep_wait(
|
||||||
|
Duration::from_millis(100),
|
||||||
|
Duration::from_millis(10),
|
||||||
|
|| self.matches_all(regs::CLOCK_CONTROL::INTERNAL_CLOCK_STABLE::SET),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_default_interrupt_mask(&self) {
|
||||||
|
let mask = 0xFFFF0000
|
||||||
|
| (EVENT_MASK::COMMAND_COMPLETE::SET
|
||||||
|
+ EVENT_MASK::TRANSFER_COMPLETE::SET
|
||||||
|
+ EVENT_MASK::BUFFER_READ_READY::SET
|
||||||
|
+ EVENT_MASK::BUFFER_WRITE_READY::SET)
|
||||||
|
.value;
|
||||||
|
self.set::<INTERRUPT_STATUS_ENABLE::Register>(mask);
|
||||||
|
self.set::<INTERRUPT_SIGNAL_ENABLE::Register>(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_clock_rate(&self, freq: Hertz) -> Result<(), Error> {
|
||||||
|
let want_divider = if let Some(base_freq) = self.capabilities.base_frequency {
|
||||||
|
log::info!("Want frequency {freq}, base frequency {base_freq}");
|
||||||
|
base_freq / freq
|
||||||
|
} else {
|
||||||
|
log::warn!("Set SD clock without known base frequency");
|
||||||
|
16
|
||||||
|
};
|
||||||
|
let sd_was_enabled = self.matches_all(regs::CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
|
||||||
|
let internal_was_enabled =
|
||||||
|
self.matches_all(regs::CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
|
||||||
|
|
||||||
|
self.wait_inhibit(true, true, Duration::from_secs(1))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if sd_was_enabled || internal_was_enabled {
|
||||||
|
self.modify(regs::CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR);
|
||||||
|
}
|
||||||
|
if internal_was_enabled {
|
||||||
|
self.modify(regs::CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick a matching divider
|
||||||
|
let div2 = want_divider / 2;
|
||||||
|
let divider_lo = (div2 & 0xFF) as u16;
|
||||||
|
let divider_hi = (div2 >> 8) as u16;
|
||||||
|
if self.capabilities.version < HostVersion::V3_00 {
|
||||||
|
todo!("8-bit divided clock mode");
|
||||||
|
}
|
||||||
|
assert_eq!(divider_hi & !0x3, 0);
|
||||||
|
|
||||||
|
self.modify(
|
||||||
|
regs::CLOCK_CONTROL::SDCLK_FREQ_LOWER.val(divider_lo)
|
||||||
|
+ regs::CLOCK_CONTROL::SDCLK_FREQ_UPPER.val(divider_hi)
|
||||||
|
+ regs::CLOCK_CONTROL::CLOCK_GENERATOR_SELECT::ModeDivided,
|
||||||
|
);
|
||||||
|
|
||||||
|
if internal_was_enabled {
|
||||||
|
// Wait for clock to stabilize
|
||||||
|
self.modify(regs::CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
|
||||||
|
self.wait_clock_stable()
|
||||||
|
.await
|
||||||
|
.inspect_err(|_| log::warn!("Clock stabilization timeout"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if sd_was_enabled && internal_was_enabled {
|
||||||
|
self.modify(regs::CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_vdd1_power(&self) {
|
||||||
|
self.write(
|
||||||
|
POWER_CONTROL::SD_BUS_POWER_VDD1::SET + POWER_CONTROL::SD_BUS_VOLTAGE_VDD1::Voltage3_3v,
|
||||||
|
);
|
||||||
|
|
||||||
|
// // let tmp = self.host.read1(Self::REG_POWER_CONTROL);
|
||||||
|
// // self.host.write1(Self::REG_POWER_CONTROL, 0xF);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_clock_mode(&self, mode: ClockMode) -> Result<(), Error> {
|
||||||
|
match mode {
|
||||||
|
ClockMode::None => {
|
||||||
|
self.modify(
|
||||||
|
CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR
|
||||||
|
+ CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
ClockMode::Internal => {
|
||||||
|
self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR);
|
||||||
|
|
||||||
|
if self.matches_all(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR) {
|
||||||
|
self.modify(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
|
||||||
|
self.wait_clock_stable().await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
ClockMode::All => {
|
||||||
|
if self.matches_all(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR) {
|
||||||
|
self.modify(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
|
||||||
|
self.wait_clock_stable().await?;
|
||||||
|
}
|
||||||
|
self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if enable {
|
||||||
|
// self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
|
||||||
|
// self.wait_clock_stable().await
|
||||||
|
// } else {
|
||||||
|
// self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR);
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_card_inserted(&self) -> bool {
|
||||||
|
self.matches_all(PRESENT_STATE::CARD_INSERTED::SET)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_status(&self) -> LocalRegisterCopy<u32, EVENT_MASK::Register> {
|
||||||
|
let value = self.get::<regs::INTERRUPT_STATUS::Register>();
|
||||||
|
self.set::<regs::INTERRUPT_STATUS::Register>(value);
|
||||||
|
LocalRegisterCopy::new(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_response_136(&self) -> [u32; 4] {
|
||||||
|
[
|
||||||
|
self.get::<regs::RESPONSE0::Register>(),
|
||||||
|
self.get::<regs::RESPONSE1::Register>(),
|
||||||
|
self.get::<regs::RESPONSE2::Register>(),
|
||||||
|
self.get::<regs::RESPONSE3::Register>(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_response_48(&self) -> [u32; 4] {
|
||||||
|
[self.get::<regs::RESPONSE0::Register>(), 0, 0, 0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_sdcard<H: SdhciHost>(card: Arc<SdCard<H>>, probe: bool) {
|
||||||
|
// TODO card index allocation
|
||||||
|
let name = format!("mmc0");
|
||||||
|
log::info!("Register SD Card: {name}");
|
||||||
|
devfs::add_named_block_device(card.clone(), name.clone(), FileMode::new(0o600)).ok();
|
||||||
|
|
||||||
|
if probe {
|
||||||
|
runtime::spawn(async move {
|
||||||
|
let name = name;
|
||||||
|
log::info!("Probing partitions for {name}");
|
||||||
|
probe_partitions(card, |index, partition| {
|
||||||
|
let partition_name = format!("{name}p{}", index + 1);
|
||||||
|
devfs::add_named_block_device(
|
||||||
|
Arc::new(partition),
|
||||||
|
partition_name,
|
||||||
|
FileMode::new(0o600),
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.inspect_err(|error| log::error!("{name}: partition probe failed: {error:?}"))
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,400 @@
|
|||||||
|
#![allow(non_camel_case_types, non_snake_case)]
|
||||||
|
|
||||||
|
use tock_registers::{register_bitfields, UIntLike};
|
||||||
|
|
||||||
|
pub trait Reg {
|
||||||
|
const ADDRESS: usize;
|
||||||
|
type Repr: UIntLike;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! define_register {
|
||||||
|
($vis:vis $name:ident: $repr:ident @ $address:literal) => {
|
||||||
|
$vis mod $name {
|
||||||
|
pub struct Register;
|
||||||
|
}
|
||||||
|
impl Reg for $name::Register {
|
||||||
|
const ADDRESS: usize = $address;
|
||||||
|
type Repr = $repr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(
|
||||||
|
$vis:vis $name:ident: $repr:ident @ $address:literal {
|
||||||
|
$( $field:ident OFFSET($offset:literal) NUMBITS($numbits:literal) [
|
||||||
|
$( $fieldVariant:ident = $fieldVariantValue:literal ),* $(,)?
|
||||||
|
] ),+ $(,)?
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
tock_registers::register_bitfields! {
|
||||||
|
$repr,
|
||||||
|
$vis $name [
|
||||||
|
$( $field OFFSET($offset) NUMBITS($numbits) [
|
||||||
|
$( $fieldVariant = $fieldVariantValue ),*
|
||||||
|
] ),+
|
||||||
|
],
|
||||||
|
}
|
||||||
|
impl Reg for $name::Register {
|
||||||
|
const ADDRESS: usize = $address;
|
||||||
|
type Repr = $repr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
register_bitfields! {
|
||||||
|
u32,
|
||||||
|
pub EVENT_MASK [
|
||||||
|
BUFFER_READ_READY OFFSET(5) NUMBITS(1) [],
|
||||||
|
BUFFER_WRITE_READY OFFSET(4) NUMBITS(1) [],
|
||||||
|
DMA_INTERRUPT OFFSET(3) NUMBITS(1) [],
|
||||||
|
BLOCK_GAP_EVENT OFFSET(2) NUMBITS(1) [],
|
||||||
|
TRANSFER_COMPLETE OFFSET(1) NUMBITS(1) [],
|
||||||
|
COMMAND_COMPLETE OFFSET(0) NUMBITS(1) [],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
define_register!(pub BLOCK_SIZE: u16 @ 0x04);
|
||||||
|
define_register!(pub BLOCK_COUNT: u16 @ 0x06);
|
||||||
|
|
||||||
|
define_register!(pub ARGUMENT: u32 @ 0x08);
|
||||||
|
define_register!(pub COMMAND: u32 @ 0x0C {
|
||||||
|
// Transfer mode
|
||||||
|
DMA_ENABLE OFFSET(0) NUMBITS(1) [],
|
||||||
|
BLOCK_COUNT_ENABLE OFFSET(1) NUMBITS(1) [],
|
||||||
|
AUTO_CMD_ENABLE OFFSET(2) NUMBITS(2) [],
|
||||||
|
DATA_TRANSFER_DIRECTION OFFSET(4) NUMBITS(1) [
|
||||||
|
HostToCard = 0,
|
||||||
|
CardToHost = 1,
|
||||||
|
],
|
||||||
|
MULTI_BLOCK_SELECT OFFSET(5) NUMBITS(1) [
|
||||||
|
SingleBlock = 0,
|
||||||
|
MultiBlock = 1,
|
||||||
|
],
|
||||||
|
RESPONSE_TYPE_R1_R5 OFFSET(6) NUMBITS(1) [
|
||||||
|
R1 = 0,
|
||||||
|
R5 = 1,
|
||||||
|
],
|
||||||
|
RESPONSE_ERROR_CHECK OFFSET(7) NUMBITS(1) [],
|
||||||
|
RESPONSE_INTERRUPT_DISABLE OFFSET(8) NUMBITS(1) [],
|
||||||
|
// Command
|
||||||
|
RESPONSE_TYPE OFFSET(16) NUMBITS(2) [
|
||||||
|
None = 0,
|
||||||
|
Response136 = 1,
|
||||||
|
Response48 = 2,
|
||||||
|
Response48b = 3,
|
||||||
|
],
|
||||||
|
SUB_COMMAND_FLAG OFFSET(18) NUMBITS(1) [],
|
||||||
|
COMMAND_CRC_CHECK OFFSET(19) NUMBITS(1) [],
|
||||||
|
COMMAND_INDEX_CHECK OFFSET(20) NUMBITS(1) [],
|
||||||
|
DATA_PRESENT_SELECT OFFSET(21) NUMBITS(1) [],
|
||||||
|
COMMAND_TYPE OFFSET(22) NUMBITS(2) [
|
||||||
|
Normal = 0,
|
||||||
|
Suspend = 1,
|
||||||
|
Resume = 2,
|
||||||
|
Abort = 3,
|
||||||
|
],
|
||||||
|
COMMAND_INDEX OFFSET(24) NUMBITS(6) [],
|
||||||
|
});
|
||||||
|
define_register!(pub RESPONSE0: u32 @ 0x10);
|
||||||
|
define_register!(pub RESPONSE1: u32 @ 0x14);
|
||||||
|
define_register!(pub RESPONSE2: u32 @ 0x18);
|
||||||
|
define_register!(pub RESPONSE3: u32 @ 0x1C);
|
||||||
|
define_register!(pub BUFFER_DATA: u32 @ 0x20);
|
||||||
|
define_register!(pub PRESENT_STATE: u32 @ 0x24 {
|
||||||
|
COMMAND_INHIBIT_CMD OFFSET(0) NUMBITS(1) [],
|
||||||
|
COMMAND_INHIBIT_DAT OFFSET(1) NUMBITS(1) [],
|
||||||
|
DAT_LINE_ACTIVE OFFSET(2) NUMBITS(1) [],
|
||||||
|
RETUNING_REQUEST OFFSET(3) NUMBITS(1) [],
|
||||||
|
DAT_LINE_UPPER OFFSET(4) NUMBITS(4) [],
|
||||||
|
WRITE_TRANSFER_ACTIVE OFFSET(8) NUMBITS(1) [],
|
||||||
|
READ_TRANSFER_ACTIVE OFFSET(9) NUMBITS(1) [],
|
||||||
|
BUFFER_WRITE_ENABLE OFFSET(10) NUMBITS(1) [],
|
||||||
|
BUFFER_READ_ENABLE OFFSET(11) NUMBITS(1) [],
|
||||||
|
CARD_INSERTED OFFSET(16) NUMBITS(1) [],
|
||||||
|
CARD_STATE_STABLE OFFSET(17) NUMBITS(1) [],
|
||||||
|
CARD_DETECT_PIN_LEVEL OFFSET(18) NUMBITS(1) [],
|
||||||
|
WRITE_PROTECT_LEVEL OFFSET(19) NUMBITS(1) [],
|
||||||
|
DAT_LINE_LOWER OFFSET(20) NUMBITS(4) [],
|
||||||
|
CMD_LINE_LEVEL OFFSET(24) NUMBITS(1) [],
|
||||||
|
HOST_REGULATOR_V_STABLE OFFSET(25) NUMBITS(1) [],
|
||||||
|
CMD_NOT_ISSUED_BY_ERROR OFFSET(27) NUMBITS(1) [],
|
||||||
|
SUB_CMD_STATUS OFFSET(28) NUMBITS(1) [],
|
||||||
|
DORMANT_STATE OFFSET(29) NUMBITS(1) [],
|
||||||
|
LANE_SYNC OFFSET(30) NUMBITS(1) [],
|
||||||
|
UHS2_IF_DETECT OFFSET(31) NUMBITS(1) [],
|
||||||
|
});
|
||||||
|
define_register!(pub HOST_CONTROL1: u8 @ 0x28 {
|
||||||
|
LED_CONTROL OFFSET(0) NUMBITS(1) [],
|
||||||
|
});
|
||||||
|
define_register!(pub POWER_CONTROL: u8 @ 0x29 {
|
||||||
|
SD_BUS_POWER_VDD1 OFFSET(0) NUMBITS(1) [],
|
||||||
|
SD_BUS_VOLTAGE_VDD1 OFFSET(1) NUMBITS(3) [
|
||||||
|
Voltage1_8v = 0b101,
|
||||||
|
Voltage3_0v = 0b110,
|
||||||
|
Voltage3_3v = 0b111,
|
||||||
|
],
|
||||||
|
SD_BUS_POWER_VDD2 OFFSET(4) NUMBITS(1) [],
|
||||||
|
SD_BUS_VOLTAGE_VDD2 OFFSET(5) NUMBITS(3) [
|
||||||
|
Voltage1_8v = 0b101,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
define_register!(pub CLOCK_CONTROL: u16 @ 0x2C {
|
||||||
|
INTERNAL_CLOCK_ENABLE OFFSET(0) NUMBITS(1) [],
|
||||||
|
INTERNAL_CLOCK_STABLE OFFSET(1) NUMBITS(1) [],
|
||||||
|
SD_CLOCK_ENABLE OFFSET(2) NUMBITS(1) [],
|
||||||
|
PLL_ENABLE OFFSET(3) NUMBITS(1) [],
|
||||||
|
CLOCK_GENERATOR_SELECT OFFSET(5) NUMBITS(1) [
|
||||||
|
ModeDivided = 0,
|
||||||
|
ModeProgrammable = 1,
|
||||||
|
],
|
||||||
|
SDCLK_FREQ_UPPER OFFSET(6) NUMBITS(2) [],
|
||||||
|
SDCLK_FREQ_LOWER OFFSET(8) NUMBITS(8) [],
|
||||||
|
});
|
||||||
|
define_register!(pub SOFTWARE_RESET: u8 @ 0x2F {
|
||||||
|
SOFTWARE_RESET_ALL OFFSET(0) NUMBITS(1) [],
|
||||||
|
RESET_CMD_LINE OFFSET(1) NUMBITS(1) [],
|
||||||
|
RESET_DAT_LINE OFFSET(2) NUMBITS(1) [],
|
||||||
|
});
|
||||||
|
define_register!(pub INTERRUPT_STATUS: u32 @ 0x30);
|
||||||
|
define_register!(pub INTERRUPT_STATUS_ENABLE: u32 @ 0x34);
|
||||||
|
define_register!(pub INTERRUPT_SIGNAL_ENABLE: u32 @ 0x38);
|
||||||
|
define_register!(pub CAPABILITIES: u64 @ 0x40 {
|
||||||
|
BASE_CLOCK_FREQ OFFSET(8) NUMBITS(8) [],
|
||||||
|
ADMA2_SUPPORT OFFSET(19) NUMBITS(1) [],
|
||||||
|
SDMA_SUPPORT OFFSET(22) NUMBITS(1) [],
|
||||||
|
VOLTAGE_3_3V OFFSET(24) NUMBITS(1) [],
|
||||||
|
VOLTAGE_3_0V OFFSET(25) NUMBITS(1) [],
|
||||||
|
VOLTAGE_1_8V OFFSET(26) NUMBITS(1) [],
|
||||||
|
CLOCK_MULTIPLIER OFFSET(48) NUMBITS(8) [],
|
||||||
|
ADMA3_SUPPORT OFFSET(59) NUMBITS(1) [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// use core::time::Duration;
|
||||||
|
//
|
||||||
|
// use libk::{
|
||||||
|
// error::Error,
|
||||||
|
// task::runtime::{psleep, pwait},
|
||||||
|
// };
|
||||||
|
// use tock_registers::{
|
||||||
|
// interfaces::{ReadWriteable, Readable, Writeable},
|
||||||
|
// register_bitfields, register_structs,
|
||||||
|
// registers::ReadWrite,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// register_bitfields! {
|
||||||
|
// u32,
|
||||||
|
// WORD9 [
|
||||||
|
// COMMAND_INHIBIT_CMD OFFSET(0) NUMBITS(1) [],
|
||||||
|
// COMMAND_INHIBIT_DAT OFFSET(1) NUMBITS(1) [],
|
||||||
|
// DAT_LINE_HIGH OFFSET(2) NUMBITS(1) [],
|
||||||
|
// RETUNING_REQUEST OFFSET(3) NUMBITS(1) [],
|
||||||
|
// DAT_SIGNAL_LEVEL OFFSET(4) NUMBITS(4) [],
|
||||||
|
// WRITE_TRANSFER_ACTIVE OFFSET(8) NUMBITS(1) [],
|
||||||
|
// READ_TRANSFER_ACTIVE OFFSET(9) NUMBITS(1) [],
|
||||||
|
// BUFFER_WRITE_ENABLE OFFSET(10) NUMBITS(1) [],
|
||||||
|
// BUFFER_READ_ENABLE OFFSET(11) NUMBITS(1) [],
|
||||||
|
// CARD_INSERTED OFFSET(16) NUMBITS(1) [],
|
||||||
|
// CARD_STATE_STABLE OFFSET(17) NUMBITS(1) [],
|
||||||
|
// CARD_DETECT_PIN_LEVEL OFFSET(18) NUMBITS(1) [],
|
||||||
|
// WRITE_PROTECT_PIN_LEVEL OFFSET(19) NUMBITS(1) [],
|
||||||
|
// DAT_LINE_LOW OFFSET(20) NUMBITS(4) [],
|
||||||
|
// CMD_LINE OFFSET(24) NUMBITS(1) [],
|
||||||
|
// HOST_REGULATOR_V_STABLE OFFSET(25) NUMBITS(1) [],
|
||||||
|
// COMMAND_NOT_ISSUED OFFSET(27) NUMBITS(1) [],
|
||||||
|
// SUB_COMMAND_STATUS OFFSET(28) NUMBITS(1) [],
|
||||||
|
// DORMANT_STATE OFFSET(29) NUMBITS(1) [],
|
||||||
|
// LANE_SYNC OFFSET(30) NUMBITS(1) [],
|
||||||
|
// UHS2_IF_DETECT OFFSET(31) NUMBITS(1) [],
|
||||||
|
// ],
|
||||||
|
// WORD10 [
|
||||||
|
// LED_CONTROL OFFSET(0) NUMBITS(1) [],
|
||||||
|
// DATA_TRANSFER_WIDTH OFFSET(1) NUMBITS(1) [
|
||||||
|
// Width1b = 0,
|
||||||
|
// Width4b = 1,
|
||||||
|
// ],
|
||||||
|
// HIGH_SPEED_ENABLE OFFSET(2) NUMBITS(1) [],
|
||||||
|
// DMA_SELECT OFFSET(3) NUMBITS(2) [
|
||||||
|
// ModeSdma = 0,
|
||||||
|
// Adma2 = 2,
|
||||||
|
// Adma2or3 = 3,
|
||||||
|
// ],
|
||||||
|
// EXT_DATA_TRANSFER_WIDTH OFFSET(5) NUMBITS(1) [
|
||||||
|
// WidthNormal = 0,
|
||||||
|
// Width8b = 1,
|
||||||
|
// ],
|
||||||
|
// CARD_DETECT_TEST_LEVEL OFFSET(6) NUMBITS(1) [
|
||||||
|
// NotInserted = 0,
|
||||||
|
// Inserted = 1,
|
||||||
|
// ],
|
||||||
|
// CARD_DETECT_SIGNAL_SELECT OFFSET(7) NUMBITS(1) [
|
||||||
|
// SdcdSignal = 0,
|
||||||
|
// TestSignal = 1,
|
||||||
|
// ],
|
||||||
|
// SD_BUS_POWER_VDD1 OFFSET(8) NUMBITS(1) [],
|
||||||
|
// SD_BUS_VOLTAGE_VDD1 OFFSET(9) NUMBITS(3) [
|
||||||
|
// Voltage1_8v = 5,
|
||||||
|
// Voltage3_0v = 6,
|
||||||
|
// Voltage3_3v = 7,
|
||||||
|
// ],
|
||||||
|
// SD_BUS_POWER_VDD2 OFFSET(12) NUMBITS(1) [],
|
||||||
|
// SD_BUS_VOLTAGE_VDD2 OFFSET(13) NUMBITS(3) [
|
||||||
|
// Voltage1_8v = 5,
|
||||||
|
// NotSupported = 0,
|
||||||
|
// ],
|
||||||
|
// STOP_AT_BLOCK_GAP_REQ OFFSET(16) NUMBITS(1) [],
|
||||||
|
// CONTINUE_REQ OFFSET(17) NUMBITS(1) [],
|
||||||
|
// READ_WAIT_CONTROL OFFSET(18) NUMBITS(1) [],
|
||||||
|
// INTERRUPT_AT_BLOCK_GAP OFFSET(19) NUMBITS(1) [],
|
||||||
|
// WAKEUP_ON_CARD_INTERRUPT OFFSET(24) NUMBITS(1) [],
|
||||||
|
// WAKEUP_ON_CARD_INSERT OFFSET(25) NUMBITS(1) [],
|
||||||
|
// WAKEUP_ON_CARD_REMOVE OFFSET(26) NUMBITS(1) [],
|
||||||
|
// ],
|
||||||
|
// WORD11 [
|
||||||
|
// INTERNAL_CLOCK_ENABLE OFFSET(0) NUMBITS(1) [],
|
||||||
|
// INTERNAL_CLOCK_STABLE OFFSET(1) NUMBITS(1) [],
|
||||||
|
// SD_CLOCK_ENABLE OFFSET(2) NUMBITS(1) [],
|
||||||
|
// PLL_ENABLE OFFSET(3) NUMBITS(1) [],
|
||||||
|
// CLOCK_GENERATOR_SELECT OFFSET(5) NUMBITS(1) [
|
||||||
|
// DividedClock = 0,
|
||||||
|
// ProgrammableClock = 1,
|
||||||
|
// ],
|
||||||
|
// SDCLK_FREQ_UPPER OFFSET(6) NUMBITS(2) [],
|
||||||
|
// SDCLK_FREQ_LOWER OFFSET(8) NUMBITS(8) [],
|
||||||
|
// DATA_TIMEOUT_COUNTER OFFSET(16) NUMBITS(4) [],
|
||||||
|
// SOFTWARE_RESET_ALL OFFSET(24) NUMBITS(1) [],
|
||||||
|
// SOFTWARE_RESET_CMD_LINE OFFSET(25) NUMBITS(1) [],
|
||||||
|
// SOFTWARE_RESET_DAT_LINE OFFSET(26) NUMBITS(1) [],
|
||||||
|
// ],
|
||||||
|
// WORD12 [
|
||||||
|
// COMMAND_COMPLETE OFFSET(0) NUMBITS(1) [],
|
||||||
|
// TRANSFER_COMPLETE OFFSET(1) NUMBITS(1) [],
|
||||||
|
// BLOCK_GAP_EVENT OFFSET(2) NUMBITS(1) [],
|
||||||
|
// DMA_INTERRUPT OFFSET(3) NUMBITS(1) [],
|
||||||
|
// BUFFER_WRITE_READY OFFSET(4) NUMBITS(1) [],
|
||||||
|
// BUFFER_READ_READY OFFSET(5) NUMBITS(1) [],
|
||||||
|
// CARD_INSERTION OFFSET(6) NUMBITS(1) [],
|
||||||
|
// CARD_REMOVAL OFFSET(7) NUMBITS(1) [],
|
||||||
|
// CARD_INTERRUPT OFFSET(8) NUMBITS(1) [],
|
||||||
|
// INT_A OFFSET(9) NUMBITS(1) [],
|
||||||
|
// INT_B OFFSET(10) NUMBITS(1) [],
|
||||||
|
// INT_C OFFSET(11) NUMBITS(1) [],
|
||||||
|
// RETUNING_EVENT OFFSET(12) NUMBITS(1) [],
|
||||||
|
// FX_EVENT OFFSET(13) NUMBITS(1) [],
|
||||||
|
// COMMAND_TIMEOUT_ERROR OFFSET(16) NUMBITS(1) [],
|
||||||
|
// COMMAND_CRC_ERROR OFFSET(17) NUMBITS(1) [],
|
||||||
|
// COMMAND_END_BIT_ERROR OFFSET(18) NUMBITS(1) [],
|
||||||
|
// COMMAND_INDEX_ERROR OFFSET(19) NUMBITS(1) [],
|
||||||
|
// DATA_TIMEOUT_ERROR OFFSET(20) NUMBITS(1) [],
|
||||||
|
// DATA_CRC_ERROR OFFSET(21) NUMBITS(1) [],
|
||||||
|
// DATA_END_BIT_ERROR OFFSET(22) NUMBITS(1) [],
|
||||||
|
// CURRENT_LIMIT_ERROR OFFSET(23) NUMBITS(1) [],
|
||||||
|
// AUTO_CMD_ERROR OFFSET(24) NUMBITS(1) [],
|
||||||
|
// ADMA_ERROR OFFSET(25) NUMBITS(1) [],
|
||||||
|
// TUNING_ERROR OFFSET(26) NUMBITS(1) [],
|
||||||
|
// RESPONSE_ERROR OFFSET(27) NUMBITS(1) [],
|
||||||
|
// ],
|
||||||
|
// WORD15 [
|
||||||
|
// AUTO_CMD12_NOT_EXECUTED OFFSET(0) NUMBITS(1) [],
|
||||||
|
// AUTO_CMD_TIMEOUT_ERROR OFFSET(1) NUMBITS(1) [],
|
||||||
|
// AUTO_CMD_CRC_ERROR OFFSET(2) NUMBITS(1) [],
|
||||||
|
// AUTO_CMD_END_BIT_ERROR OFFSET(3) NUMBITS(1) [],
|
||||||
|
// AUTO_CMD_INDEX_ERROR OFFSET(4) NUMBITS(1) [],
|
||||||
|
// AUTO_CMD_RESPONSE_ERROR OFFSET(5) NUMBITS(1) [],
|
||||||
|
// COMMAND_NOT_ISSUED_CMD12 OFFSET(7) NUMBITS(1) [],
|
||||||
|
// UHS_MODE_SELECT OFFSET(16) NUMBITS(3) [
|
||||||
|
// ModeSdr12 = 0,
|
||||||
|
// ModeSdr25 = 1,
|
||||||
|
// ModeSdr50 = 2,
|
||||||
|
// ModeSdr104 = 3,
|
||||||
|
// ModeDdr50 = 4,
|
||||||
|
// Uhs2 = 7,
|
||||||
|
// ],
|
||||||
|
// SIGNALING_1_8V_ENABLE OFFSET(19) NUMBITS(1) [],
|
||||||
|
// DRIVER_SIGNAL_SELECT OFFSET(20) NUMBITS(2) [
|
||||||
|
// DriverTypeB = 0,
|
||||||
|
// DriverTypeA = 1,
|
||||||
|
// DriverTypeC = 2,
|
||||||
|
// DriverTypeD = 3,
|
||||||
|
// ],
|
||||||
|
// EXECUTE_TUNING OFFSET(22) NUMBITS(1) [],
|
||||||
|
// SAMPLING_CLOCK_SELECT OFFSET(23) NUMBITS(1) [],
|
||||||
|
// UHS2_INTERFACE_ENABLE OFFSET(24) NUMBITS(1) [],
|
||||||
|
// ADMA2_LENGTH_MODE OFFSET(26) NUMBITS(1) [
|
||||||
|
// Length16b = 0,
|
||||||
|
// Length26b = 1,
|
||||||
|
// ],
|
||||||
|
// CMD23_ENABLE OFFSET(27) NUMBITS(1) [],
|
||||||
|
// HOST_V4_ENABLE OFFSET(28) NUMBITS(1) [],
|
||||||
|
// ADDRESS_64_BIT OFFSET(29) NUMBITS(1) [],
|
||||||
|
// ASYNC_INTERRUPT_ENABLE OFFSET(30) NUMBITS(1) [],
|
||||||
|
// PRESET_VALUE_ENABLE OFFSET(31) NUMBITS(1) [],
|
||||||
|
// ],
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// register_structs! {
|
||||||
|
// #[allow(non_snake_case)]
|
||||||
|
// pub Regs {
|
||||||
|
// // SDMA system address
|
||||||
|
// (0x000 => WORD0: ReadWrite<u32>),
|
||||||
|
// // Block size, block count
|
||||||
|
// (0x004 => WORD1: ReadWrite<u32>),
|
||||||
|
// // Argument
|
||||||
|
// (0x008 => WORD2: ReadWrite<u32>),
|
||||||
|
// // Transfer mode, command
|
||||||
|
// (0x00C => WORD3: ReadWrite<u32>),
|
||||||
|
// // Response0, Response1
|
||||||
|
// (0x010 => WORD4: ReadWrite<u32>),
|
||||||
|
// // Response2, Response3
|
||||||
|
// (0x014 => WORD5: ReadWrite<u32>),
|
||||||
|
// // Response4, Response5
|
||||||
|
// (0x018 => WORD6: ReadWrite<u32>),
|
||||||
|
// // Response6, Response7
|
||||||
|
// (0x01C => WORD7: ReadWrite<u32>),
|
||||||
|
// // Buffer data port 0, port 1
|
||||||
|
// (0x020 => WORD8: ReadWrite<u32>),
|
||||||
|
// // Present state
|
||||||
|
// (0x024 => WORD9: ReadWrite<u32, WORD9::Register>),
|
||||||
|
// // Host control 1, power control, block gap control, wakeup control
|
||||||
|
// (0x028 => WORD10: ReadWrite<u32, WORD10::Register>),
|
||||||
|
// // Clock control, timeout control, software reset
|
||||||
|
// (0x02C => WORD11: ReadWrite<u32, WORD11::Register>),
|
||||||
|
// // Normal interrupt status, error interrupt status
|
||||||
|
// (0x030 => WORD12: ReadWrite<u32, WORD12::Register>),
|
||||||
|
// // Normal interrupt status enable, error interrupt status enable
|
||||||
|
// (0x034 => WORD13: ReadWrite<u32, WORD12::Register>),
|
||||||
|
// // Normal interrupt signal enable, error interrupt signal enable
|
||||||
|
// (0x038 => WORD14: ReadWrite<u32, WORD12::Register>),
|
||||||
|
// // Auto CMD error status, host control 2
|
||||||
|
// (0x03C => WORD15: ReadWrite<u32, WORD15::Register>),
|
||||||
|
// (0x040 => WORD16: ReadWrite<u32>),
|
||||||
|
// (0x044 => WORD17: ReadWrite<u32>),
|
||||||
|
// (0x048 => WORD18: ReadWrite<u32>),
|
||||||
|
// (0x04C => WORD19: ReadWrite<u32>),
|
||||||
|
// (0x050 => _0),
|
||||||
|
// (0x100 => @END),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// impl Regs {
|
||||||
|
// pub fn software_reset_all(&self, timeout: Duration) -> Result<(), Error> {
|
||||||
|
// self.WORD11.modify(WORD11::SOFTWARE_RESET_ALL::SET);
|
||||||
|
// pwait(timeout, Duration::from_millis(50), || {
|
||||||
|
// self.WORD11.matches_all(WORD11::SOFTWARE_RESET_ALL::CLEAR)
|
||||||
|
// })?;
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn setup_default_interrupts(&self) {
|
||||||
|
// self.WORD13
|
||||||
|
// .write(WORD12::COMMAND_COMPLETE::SET + WORD12::TRANSFER_COMPLETE::SET);
|
||||||
|
//
|
||||||
|
// self.WORD11.modify(WORD11::SOFTWARE_RESET_ALL::CLEAR);
|
||||||
|
//
|
||||||
|
// // Setup VDD1 power
|
||||||
|
// self.WORD10
|
||||||
|
// .modify(WORD10::SD_BUS_POWER_VDD1::SET + WORD10::SD_BUS_VOLTAGE_VDD1::Voltage3_3v);
|
||||||
|
//
|
||||||
|
// loop {
|
||||||
|
// psleep(Duration::from_secs(1));
|
||||||
|
// log::info!("{:#x}", self.WORD9.get());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -50,6 +50,14 @@ impl Div<u32> for Hertz {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Div<Hertz> for Hertz {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn div(self, rhs: Hertz) -> Self::Output {
|
||||||
|
(self.0 / rhs.0) as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Hertz {
|
impl fmt::Display for Hertz {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let (scale, suffix) = match self.0 {
|
let (scale, suffix) = match self.0 {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use yggdrasil_abi::error::Error;
|
|||||||
|
|
||||||
mod controller;
|
mod controller;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod registry;
|
pub(crate) mod registry;
|
||||||
mod syscon;
|
mod syscon;
|
||||||
mod traits;
|
mod traits;
|
||||||
mod tree;
|
mod tree;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ pub struct Node {
|
|||||||
bus_size_cells: usize,
|
bus_size_cells: usize,
|
||||||
interrupt_parent: Option<Phandle>,
|
interrupt_parent: Option<Phandle>,
|
||||||
name: Option<&'static str>,
|
name: Option<&'static str>,
|
||||||
compatible: Option<TProp<'static>>,
|
pub(crate) compatible: Option<TProp<'static>>,
|
||||||
|
|
||||||
// Hierachy info
|
// Hierachy info
|
||||||
children: OneTimeInit<Vec<Arc<Node>>>,
|
children: OneTimeInit<Vec<Arc<Node>>>,
|
||||||
@@ -150,6 +150,7 @@ impl Node {
|
|||||||
if let Some(status) = self.property("status") {
|
if let Some(status) = self.property("status") {
|
||||||
let status = status.as_str().unwrap_or("");
|
let status = status.as_str().unwrap_or("");
|
||||||
if status == "disabled" {
|
if status == "disabled" {
|
||||||
|
// log::info!("Not probing disabled {:?}", self.compatible);
|
||||||
return (None, parent_bus);
|
return (None, parent_bus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,62 @@ pub mod property;
|
|||||||
pub mod tree;
|
pub mod tree;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
pub use node::DeviceTreeNodeExt;
|
pub use node::DeviceTreeNodeExt;
|
||||||
pub use property::DeviceTreePropertyRead;
|
pub use property::DeviceTreePropertyRead;
|
||||||
pub use tree::{DeviceTree, TNode, TProp};
|
pub use tree::{DeviceTree, TNode, TProp};
|
||||||
pub use util::DeviceTreeMemoryRegionIter;
|
pub use util::DeviceTreeMemoryRegionIter;
|
||||||
|
|
||||||
|
fn dump_node(level: log::Level, node: &driver::Node, depth: usize) {
|
||||||
|
fn indent(level: log::Level, x: usize) {
|
||||||
|
for _ in 0..x {
|
||||||
|
log::log!(target: ":raw", level, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn field<D: fmt::Debug>(level: log::Level, depth: usize, name: &str, value: &D) {
|
||||||
|
// indent(level, depth);
|
||||||
|
// log::log!(target: ":raw", level, "{name} = {value:?},\n");
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn field_list<'a, D: fmt::Debug + ?Sized + 'a, I: IntoIterator<Item = &'a D>>(
|
||||||
|
level: log::Level,
|
||||||
|
depth: usize,
|
||||||
|
name: &str,
|
||||||
|
value: I,
|
||||||
|
) {
|
||||||
|
indent(level, depth);
|
||||||
|
log::log!(target: ":raw", level, "{name} = ");
|
||||||
|
for (i, item) in value.into_iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
log::log!(target: ":raw", level, ", ");
|
||||||
|
}
|
||||||
|
log::log!(target: ":raw", level, "{item:?}");
|
||||||
|
}
|
||||||
|
log::log!(target: ":raw", level, ",\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
indent(level, depth);
|
||||||
|
log::log!(target: ":raw", level, "\"{}\" {{\n", node.name().unwrap_or(""));
|
||||||
|
|
||||||
|
if let Some(compatible) = node.compatible.as_ref() {
|
||||||
|
field_list(level, depth + 1, "compatible", compatible.as_str_list());
|
||||||
|
}
|
||||||
|
// for value in node.compatible
|
||||||
|
|
||||||
|
for (i, node) in node.children().enumerate() {
|
||||||
|
if i == 0 {
|
||||||
|
log::log!(target: ":raw", level, "\n");
|
||||||
|
}
|
||||||
|
dump_node(level, node, depth + 1);
|
||||||
|
}
|
||||||
|
indent(level, depth);
|
||||||
|
log::log!(target: ":raw", level, "}},\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump the device tree to debug output
|
||||||
|
pub fn dump(level: log::Level) {
|
||||||
|
let root = driver::registry::ROOT.get();
|
||||||
|
dump_node(level, root, 0);
|
||||||
|
}
|
||||||
|
|||||||
@@ -324,7 +324,7 @@ impl<'a, T> DmaSlice<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO subslicing
|
// TODO subslicing
|
||||||
pub fn into_parts(self) -> (&'a DmaBuffer<[T]>, Range<usize>) {
|
pub unsafe fn into_parts(self) -> (&'a DmaBuffer<[T]>, Range<usize>) {
|
||||||
(self.buffer, self.range)
|
(self.buffer, self.range)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,7 +353,7 @@ impl<'a, T> DmaSliceMut<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO subslicing
|
// TODO subslicing
|
||||||
pub fn into_parts(self) -> (&'a mut DmaBuffer<[T]>, Range<usize>) {
|
pub unsafe fn into_parts(self) -> (&'a mut DmaBuffer<[T]>, Range<usize>) {
|
||||||
(self.buffer, self.range)
|
(self.buffer, self.range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ mod timer;
|
|||||||
pub use executor::{run_to_completion, spawn, spawn_async_worker};
|
pub use executor::{run_to_completion, spawn, spawn_async_worker};
|
||||||
pub use task_queue::init_task_queue;
|
pub use task_queue::init_task_queue;
|
||||||
pub use timer::{
|
pub use timer::{
|
||||||
maybe_timeout, psleep, pwait, pwait_try, sleep, sleep_until, tick, with_timeout, SleepFuture,
|
maybe_timeout, psleep, pwait, pwait_try, sleep, sleep_until, sleep_wait, tick, with_timeout,
|
||||||
|
SleepFuture,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -201,6 +201,25 @@ pub fn pwait_try<P: Fn() -> Result<bool, Error>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn sleep_wait<P: Fn() -> bool>(
|
||||||
|
timeout: Duration,
|
||||||
|
poll_rate: Duration,
|
||||||
|
predicate: P,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let now = monotonic_time();
|
||||||
|
let deadline = now + timeout;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if predicate() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if monotonic_time() > deadline {
|
||||||
|
return Err(Error::TimedOut);
|
||||||
|
}
|
||||||
|
sleep(poll_rate).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_timeout<'a, T: 'a, F: Future<Output = T> + Send + 'a>(
|
pub fn with_timeout<'a, T: 'a, F: Future<Output = T> + Send + 'a>(
|
||||||
fut: F,
|
fut: F,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ extern crate ygg_driver_virtio_blk;
|
|||||||
extern crate ygg_driver_virtio_gpu;
|
extern crate ygg_driver_virtio_gpu;
|
||||||
extern crate ygg_driver_virtio_net;
|
extern crate ygg_driver_virtio_net;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||||
|
extern crate ygg_driver_mmc_generic_sdhci;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(target_arch = "x86_64")] {
|
if #[cfg(target_arch = "x86_64")] {
|
||||||
extern crate ygg_driver_net_igbe;
|
extern crate ygg_driver_net_igbe;
|
||||||
@@ -86,6 +89,7 @@ cfg_if::cfg_if! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod arch;
|
pub mod arch;
|
||||||
|
|
||||||
pub mod device;
|
pub mod device;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
pub mod init;
|
pub mod init;
|
||||||
|
|||||||
Reference in New Issue
Block a user