Compare commits
8 Commits
add-sdio-s
...
prep-rt-pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2e5215435 | ||
| 777634b914 | |||
|
|
cab37a87a3 | ||
| 62cc54fd9b | |||
|
|
d445454a54 | ||
| 0235e1d769 | |||
| df0fc1acc3 | |||
| 52e17c739d |
@@ -2,7 +2,7 @@
|
|||||||
# The following two env variables need to be set for the supplied runner.sh script to work.
|
# The following two env variables need to be set for the supplied runner.sh script to work.
|
||||||
|
|
||||||
# Absolute path to the Vitis install directory.
|
# Absolute path to the Vitis install directory.
|
||||||
# AMD_TOOLS = "/tools/Xilinx/Vitis/2024.1"
|
# AMD_TOOLS = "/tools/2025.2/Vitis"
|
||||||
# Absolute path to the PS7 initialization TCL script.
|
# Absolute path to the PS7 initialization TCL script.
|
||||||
# TCL_INIT_SCRIPT = "/home/$user/$project/$sdt_dir/ps7_init.tcl"
|
# TCL_INIT_SCRIPT = "/home/$user/$project/$sdt_dir/ps7_init.tcl"
|
||||||
|
|
||||||
|
|||||||
2
tools/Cargo.lock
generated
2
tools/Cargo.lock
generated
@@ -725,7 +725,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zynq7000-rt"
|
name = "zynq7000-rt"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aarch32-cpu",
|
"aarch32-cpu",
|
||||||
"arbitrary-int 2.0.0",
|
"arbitrary-int 2.0.0",
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ hardware design to other boards with modifications.
|
|||||||
|
|
||||||
- [Vivado installation](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools.html)
|
- [Vivado installation](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools.html)
|
||||||
or [Vitis installation](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vitis.html)
|
or [Vitis installation](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vitis.html)
|
||||||
which includes Vivado. This example design was created with/for Vivado 2024.1, but also might work
|
which includes Vivado. This example design was created with/for Vivado 2025.2, but also might work
|
||||||
|
for newer versions. You might have to manually adjust some variables in `src/zedboard-bd.tcl`
|
||||||
for newer versions.
|
for newer versions.
|
||||||
- [Zedboard board files](https://github.com/Digilent/vivado-boards) added to the Vivado installation.
|
|
||||||
|
|
||||||
# Loading the project and the block design with the GUI
|
# Loading the project and the block design with the GUI
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ named `sdt_out` for a hardware description files `zedboard-rust/zedboard-rust.xs
|
|||||||
assuming that the Vitis tool suite is installed at `/tools/Xilinx/Vitis/2024.1`:
|
assuming that the Vitis tool suite is installed at `/tools/Xilinx/Vitis/2024.1`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export AMD_TOOLS="/tools/Xilinx/Vitis/2024.1"
|
export AMD_TOOLS="/tools/2025.2/Vitis"
|
||||||
./sdtgen.py -x ./zedboard-rust/zedboard-rust.xsa
|
./sdtgen.py -x ./zedboard-rust/zedboard-rust.xsa
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ set script_folder [_tcl::get_script_folder]
|
|||||||
################################################################
|
################################################################
|
||||||
# Check if script is running in correct Vivado version.
|
# Check if script is running in correct Vivado version.
|
||||||
################################################################
|
################################################################
|
||||||
set scripts_vivado_version 2024.1
|
set scripts_vivado_version 2025.2
|
||||||
set current_vivado_version [version -short]
|
set current_vivado_version [version -short]
|
||||||
|
|
||||||
if { [string first $scripts_vivado_version $current_vivado_version] == -1 } {
|
if { [string first $scripts_vivado_version $current_vivado_version] == -1 } {
|
||||||
@@ -57,7 +57,6 @@ if { [string first $scripts_vivado_version $current_vivado_version] == -1 } {
|
|||||||
set list_projs [get_projects -quiet]
|
set list_projs [get_projects -quiet]
|
||||||
if { $list_projs eq "" } {
|
if { $list_projs eq "" } {
|
||||||
create_project project_1 myproj -part xc7z020clg484-1
|
create_project project_1 myproj -part xc7z020clg484-1
|
||||||
set_property BOARD_PART digilentinc.com:zedboard:part0:1.1 [current_project]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,6 @@ set proj_dir [get_property directory [current_project]]
|
|||||||
|
|
||||||
# Set project properties
|
# Set project properties
|
||||||
set obj [current_project]
|
set obj [current_project]
|
||||||
set_property -name "board_part" -value "digilentinc.com:zedboard:part0:1.1" -objects $obj
|
|
||||||
set_property -name "default_lib" -value "xil_defaultlib" -objects $obj
|
set_property -name "default_lib" -value "xil_defaultlib" -objects $obj
|
||||||
set_property -name "enable_resource_estimation" -value "0" -objects $obj
|
set_property -name "enable_resource_estimation" -value "0" -objects $obj
|
||||||
set_property -name "enable_vhdl_2008" -value "1" -objects $obj
|
set_property -name "enable_vhdl_2008" -value "1" -objects $obj
|
||||||
@@ -221,10 +220,10 @@ catch {
|
|||||||
|
|
||||||
# Create 'synth_1' run (if not found)
|
# Create 'synth_1' run (if not found)
|
||||||
if {[string equal [get_runs -quiet synth_1] ""]} {
|
if {[string equal [get_runs -quiet synth_1] ""]} {
|
||||||
create_run -name synth_1 -part xc7z020clg484-1 -flow {Vivado Synthesis 2024} -strategy "Vivado Synthesis Defaults" -report_strategy {No Reports} -constrset constrs_1
|
create_run -name synth_1 -part xc7z020clg484-1 -flow {Vivado Synthesis 2025} -strategy "Vivado Synthesis Defaults" -report_strategy {No Reports} -constrset constrs_1
|
||||||
} else {
|
} else {
|
||||||
set_property strategy "Vivado Synthesis Defaults" [get_runs synth_1]
|
set_property strategy "Vivado Synthesis Defaults" [get_runs synth_1]
|
||||||
set_property flow "Vivado Synthesis 2024" [get_runs synth_1]
|
set_property flow "Vivado Synthesis 2025" [get_runs synth_1]
|
||||||
}
|
}
|
||||||
set obj [get_runs synth_1]
|
set obj [get_runs synth_1]
|
||||||
set_property set_report_strategy_name 1 $obj
|
set_property set_report_strategy_name 1 $obj
|
||||||
@@ -248,10 +247,10 @@ current_run -synthesis [get_runs synth_1]
|
|||||||
|
|
||||||
# Create 'impl_1' run (if not found)
|
# Create 'impl_1' run (if not found)
|
||||||
if {[string equal [get_runs -quiet impl_1] ""]} {
|
if {[string equal [get_runs -quiet impl_1] ""]} {
|
||||||
create_run -name impl_1 -part xc7z020clg484-1 -flow {Vivado Implementation 2024} -strategy "Vivado Implementation Defaults" -report_strategy {No Reports} -constrset constrs_1 -parent_run synth_1
|
create_run -name impl_1 -part xc7z020clg484-1 -flow {Vivado Implementation 2025} -strategy "Vivado Implementation Defaults" -report_strategy {No Reports} -constrset constrs_1 -parent_run synth_1
|
||||||
} else {
|
} else {
|
||||||
set_property strategy "Vivado Implementation Defaults" [get_runs impl_1]
|
set_property strategy "Vivado Implementation Defaults" [get_runs impl_1]
|
||||||
set_property flow "Vivado Implementation 2024" [get_runs impl_1]
|
set_property flow "Vivado Implementation 2025" [get_runs impl_1]
|
||||||
}
|
}
|
||||||
set obj [get_runs impl_1]
|
set obj [get_runs impl_1]
|
||||||
set_property set_report_strategy_name 1 $obj
|
set_property set_report_strategy_name 1 $obj
|
||||||
|
|||||||
@@ -1,156 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
|
|
||||||
use aarch32_cpu::asm::nop;
|
|
||||||
use core::panic::PanicInfo;
|
|
||||||
use embassy_executor::Spawner;
|
|
||||||
use embassy_time::{Duration, Ticker};
|
|
||||||
use embedded_hal::digital::StatefulOutputPin;
|
|
||||||
use embedded_io::Write;
|
|
||||||
use log::{error, info};
|
|
||||||
use zedboard::PS_CLOCK_FREQUENCY;
|
|
||||||
use zynq7000_hal::gpio::Input;
|
|
||||||
use zynq7000_hal::prelude::*;
|
|
||||||
use zynq7000_hal::{
|
|
||||||
BootMode, clocks, gic, gpio, gtc,
|
|
||||||
sdio::{Sdio, SdioClockConfig},
|
|
||||||
uart,
|
|
||||||
};
|
|
||||||
|
|
||||||
use zynq7000_rt as _;
|
|
||||||
|
|
||||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard SDIO example --\n\r";
|
|
||||||
|
|
||||||
/// Entry point (not called like a normal main function)
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
|
||||||
if cpu_id != 0 {
|
|
||||||
panic!("unexpected CPU ID {}", cpu_id);
|
|
||||||
}
|
|
||||||
main();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::main]
|
|
||||||
#[unsafe(export_name = "main")]
|
|
||||||
async fn main(_spawner: Spawner) -> ! {
|
|
||||||
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
|
|
||||||
init_l2_cache: true,
|
|
||||||
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
|
|
||||||
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
|
||||||
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
|
||||||
|
|
||||||
let gpio_pins = gpio::GpioPins::new(periphs.gpio);
|
|
||||||
|
|
||||||
// Set up global timer counter and embassy time driver.
|
|
||||||
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
|
|
||||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
|
||||||
|
|
||||||
// Set up the UART, we are logging with it.
|
|
||||||
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
|
||||||
.unwrap()
|
|
||||||
.0;
|
|
||||||
let mut uart = uart::Uart::new_with_mio_for_uart_1(
|
|
||||||
periphs.uart_1,
|
|
||||||
uart::Config::new_with_clk_config(uart_clk_config),
|
|
||||||
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
|
||||||
// Safety: We are not multi-threaded yet.
|
|
||||||
unsafe {
|
|
||||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
|
||||||
uart,
|
|
||||||
log::LevelFilter::Trace,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let sdio_clock_config =
|
|
||||||
SdioClockConfig::calculate_for_io_clock(clocks.io_clocks(), 100.MHz(), 10.MHz()).unwrap();
|
|
||||||
let sdio = Sdio::new_for_sdio_0(
|
|
||||||
periphs.sdio_0,
|
|
||||||
sdio_clock_config,
|
|
||||||
gpio_pins.mio.mio40,
|
|
||||||
gpio_pins.mio.mio41,
|
|
||||||
(
|
|
||||||
gpio_pins.mio.mio42,
|
|
||||||
gpio_pins.mio.mio43,
|
|
||||||
gpio_pins.mio.mio44,
|
|
||||||
gpio_pins.mio.mio45,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let card_detect = Input::new_for_mio(gpio_pins.mio.mio47).unwrap();
|
|
||||||
let write_protect = Input::new_for_mio(gpio_pins.mio.mio46).unwrap();
|
|
||||||
// The card detect being active low makes sense according to the Zedboard docs. Not sure
|
|
||||||
// about write-protect though.. It seems that write protect on means that the
|
|
||||||
// the pin is pulled high.
|
|
||||||
info!("Card detect state: {:?}", card_detect.is_low());
|
|
||||||
info!("Write protect state: {:?}", write_protect.is_high());
|
|
||||||
|
|
||||||
let capabilities = sdio.ll().capabilities();
|
|
||||||
info!("SDIO Capabilities: {:?}", capabilities);
|
|
||||||
|
|
||||||
let boot_mode = BootMode::new_from_regs();
|
|
||||||
info!("Boot mode: {:?}", boot_mode);
|
|
||||||
|
|
||||||
let mut ticker = Ticker::every(Duration::from_millis(200));
|
|
||||||
|
|
||||||
let mut mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
|
|
||||||
loop {
|
|
||||||
mio_led.toggle().unwrap();
|
|
||||||
|
|
||||||
ticker.next().await; // Wait for the next cycle of the ticker
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[zynq7000_rt::irq]
|
|
||||||
fn irq_handler() {
|
|
||||||
let mut gic_helper = gic::GicInterruptHelper::new();
|
|
||||||
let irq_info = gic_helper.acknowledge_interrupt();
|
|
||||||
match irq_info.interrupt() {
|
|
||||||
gic::Interrupt::Sgi(_) => (),
|
|
||||||
gic::Interrupt::Ppi(ppi_interrupt) => {
|
|
||||||
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
|
|
||||||
unsafe {
|
|
||||||
zynq7000_embassy::on_interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gic::Interrupt::Spi(_spi_interrupt) => (),
|
|
||||||
gic::Interrupt::Invalid(_) => (),
|
|
||||||
gic::Interrupt::Spurious => (),
|
|
||||||
}
|
|
||||||
gic_helper.end_of_interrupt(irq_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[zynq7000_rt::exception(DataAbort)]
|
|
||||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
|
||||||
loop {
|
|
||||||
nop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[zynq7000_rt::exception(Undefined)]
|
|
||||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
|
||||||
loop {
|
|
||||||
nop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
|
||||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
|
||||||
loop {
|
|
||||||
nop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Panic handler
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(info: &PanicInfo) -> ! {
|
|
||||||
error!("Panic: {info:?}");
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,11 @@ use crate::{clocks::IoClocks, enable_amba_peripheral_clock, slcr::Slcr, time::He
|
|||||||
|
|
||||||
use super::{EthernetId, PsEthernet as _};
|
use super::{EthernetId, PsEthernet as _};
|
||||||
|
|
||||||
|
pub struct EthernetLowLevel {
|
||||||
|
id: EthernetId,
|
||||||
|
pub regs: zynq7000::eth::MmioRegisters<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Speed {
|
pub enum Speed {
|
||||||
Mbps10,
|
Mbps10,
|
||||||
@@ -47,10 +52,7 @@ impl ClockDivisors {
|
|||||||
|
|
||||||
/// Calls [Self::calculate_for_rgmii], assuming that the IO clock is the reference clock,
|
/// Calls [Self::calculate_for_rgmii], assuming that the IO clock is the reference clock,
|
||||||
/// which is the default clock for the Ethernet module.
|
/// which is the default clock for the Ethernet module.
|
||||||
pub fn calculate_for_rgmii_and_io_clock(
|
pub fn calculate_for_rgmii_and_io_clock(io_clks: IoClocks, target_speed: Speed) -> (Self, u32) {
|
||||||
io_clks: &IoClocks,
|
|
||||||
target_speed: Speed,
|
|
||||||
) -> (Self, u32) {
|
|
||||||
Self::calculate_for_rgmii(io_clks.ref_clk(), target_speed)
|
Self::calculate_for_rgmii(io_clks.ref_clk(), target_speed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,17 +174,8 @@ impl ClockDivSet {
|
|||||||
/// Ethernet low-level interface.
|
/// Ethernet low-level interface.
|
||||||
///
|
///
|
||||||
/// Basic building block for higher-level abstraction.
|
/// Basic building block for higher-level abstraction.
|
||||||
pub struct EthernetLowLevel {
|
|
||||||
id: EthernetId,
|
|
||||||
/// Register block. Direct public access is allowed to allow low-level operations.
|
|
||||||
pub regs: zynq7000::eth::MmioRegisters<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EthernetLowLevel {
|
impl EthernetLowLevel {
|
||||||
/// Creates a new instance of the Ethernet low-level interface.
|
/// Creates a new instance of the Ethernet low-level interface.
|
||||||
///
|
|
||||||
/// Returns [None] if the given registers block base address does not correspond to a valid
|
|
||||||
/// Ethernet peripheral.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(regs: zynq7000::eth::MmioRegisters<'static>) -> Option<Self> {
|
pub fn new(regs: zynq7000::eth::MmioRegisters<'static>) -> Option<Self> {
|
||||||
regs.id()?;
|
regs.id()?;
|
||||||
@@ -211,7 +204,33 @@ impl EthernetLowLevel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, cycles: usize) {
|
pub fn reset(&mut self, cycles: usize) {
|
||||||
reset(self.id, cycles);
|
let assert_reset = match self.id {
|
||||||
|
EthernetId::Eth0 => EthernetReset::builder()
|
||||||
|
.with_gem1_ref_rst(false)
|
||||||
|
.with_gem0_ref_rst(true)
|
||||||
|
.with_gem1_rx_rst(false)
|
||||||
|
.with_gem0_rx_rst(true)
|
||||||
|
.with_gem1_cpu1x_rst(false)
|
||||||
|
.with_gem0_cpu1x_rst(true)
|
||||||
|
.build(),
|
||||||
|
EthernetId::Eth1 => EthernetReset::builder()
|
||||||
|
.with_gem1_ref_rst(true)
|
||||||
|
.with_gem0_ref_rst(false)
|
||||||
|
.with_gem1_rx_rst(true)
|
||||||
|
.with_gem0_rx_rst(false)
|
||||||
|
.with_gem1_cpu1x_rst(true)
|
||||||
|
.with_gem0_cpu1x_rst(false)
|
||||||
|
.build(),
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
Slcr::with(|regs| {
|
||||||
|
regs.reset_ctrl().write_eth(assert_reset);
|
||||||
|
for _ in 0..cycles {
|
||||||
|
aarch32_cpu::asm::nop();
|
||||||
|
}
|
||||||
|
regs.reset_ctrl().write_eth(EthernetReset::DEFAULT);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -364,34 +383,3 @@ impl EthernetLowLevel {
|
|||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets the Ethernet peripheral with the given ID.
|
|
||||||
pub fn reset(id: EthernetId, cycles: usize) {
|
|
||||||
let assert_reset = match id {
|
|
||||||
EthernetId::Eth0 => EthernetReset::builder()
|
|
||||||
.with_gem1_ref_rst(false)
|
|
||||||
.with_gem0_ref_rst(true)
|
|
||||||
.with_gem1_rx_rst(false)
|
|
||||||
.with_gem0_rx_rst(true)
|
|
||||||
.with_gem1_cpu1x_rst(false)
|
|
||||||
.with_gem0_cpu1x_rst(true)
|
|
||||||
.build(),
|
|
||||||
EthernetId::Eth1 => EthernetReset::builder()
|
|
||||||
.with_gem1_ref_rst(true)
|
|
||||||
.with_gem0_ref_rst(false)
|
|
||||||
.with_gem1_rx_rst(true)
|
|
||||||
.with_gem0_rx_rst(false)
|
|
||||||
.with_gem1_cpu1x_rst(true)
|
|
||||||
.with_gem0_cpu1x_rst(false)
|
|
||||||
.build(),
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
Slcr::with(|regs| {
|
|
||||||
regs.reset_ctrl().write_eth(assert_reset);
|
|
||||||
for _ in 0..cycles {
|
|
||||||
aarch32_cpu::asm::nop();
|
|
||||||
}
|
|
||||||
regs.reset_ctrl().write_eth(EthernetReset::DEFAULT);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ pub mod log;
|
|||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod priv_tim;
|
pub mod priv_tim;
|
||||||
pub mod qspi;
|
pub mod qspi;
|
||||||
pub mod sdio;
|
|
||||||
pub mod slcr;
|
pub mod slcr;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|||||||
@@ -1,491 +0,0 @@
|
|||||||
use arbitrary_int::{traits::Integer as _, u3, u6};
|
|
||||||
use zynq7000::{
|
|
||||||
sdio::{SDIO_BASE_ADDR_0, SDIO_BASE_ADDR_1, SdClockDivisor},
|
|
||||||
slcr::{clocks::SrcSelIo, reset::DualRefAndClockReset},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
use crate::gpio::mio::{
|
|
||||||
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, Mio40,
|
|
||||||
Mio41, Mio42, Mio43, Mio44, Mio45, Mio46, Mio47, Mio50, Mio51,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
clocks::IoClocks,
|
|
||||||
gpio::{
|
|
||||||
IoPeriphPin,
|
|
||||||
mio::{
|
|
||||||
Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31, Mio32, Mio33,
|
|
||||||
Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio48, Mio49, MioPin, MuxConfig, Pin,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
slcr::Slcr,
|
|
||||||
time::Hertz,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[error("invalid peripheral instance")]
|
|
||||||
pub struct InvalidPeripheralError;
|
|
||||||
|
|
||||||
pub const MUX_CONF: MuxConfig = MuxConfig::new_with_l3(u3::new(0b100));
|
|
||||||
|
|
||||||
pub trait Sdio0ClockPin: MioPin {}
|
|
||||||
pub trait Sdio0CommandPin: MioPin {}
|
|
||||||
pub trait Sdio0Data0Pin: MioPin {}
|
|
||||||
pub trait Sdio0Data1Pin: MioPin {}
|
|
||||||
pub trait Sdio0Data2Pin: MioPin {}
|
|
||||||
pub trait Sdio0Data3Pin: MioPin {}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0ClockPin for Pin<Mio16> {}
|
|
||||||
impl Sdio0ClockPin for Pin<Mio28> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0ClockPin for Pin<Mio40> {}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0CommandPin for Pin<Mio17> {}
|
|
||||||
impl Sdio0CommandPin for Pin<Mio29> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0CommandPin for Pin<Mio41> {}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0Data0Pin for Pin<Mio18> {}
|
|
||||||
impl Sdio0Data0Pin for Pin<Mio30> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0Data0Pin for Pin<Mio42> {}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0Data1Pin for Pin<Mio19> {}
|
|
||||||
impl Sdio0Data1Pin for Pin<Mio31> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0Data1Pin for Pin<Mio43> {}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0Data2Pin for Pin<Mio20> {}
|
|
||||||
impl Sdio0Data2Pin for Pin<Mio32> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0Data2Pin for Pin<Mio44> {}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0Data3Pin for Pin<Mio21> {}
|
|
||||||
impl Sdio0Data3Pin for Pin<Mio33> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio0Data3Pin for Pin<Mio45> {}
|
|
||||||
|
|
||||||
pub trait Sdio1ClockPin: MioPin {}
|
|
||||||
pub trait Sdio1CommandPin: MioPin {}
|
|
||||||
pub trait Sdio1Data0Pin: MioPin {}
|
|
||||||
pub trait Sdio1Data1Pin: MioPin {}
|
|
||||||
pub trait Sdio1Data2Pin: MioPin {}
|
|
||||||
pub trait Sdio1Data3Pin: MioPin {}
|
|
||||||
|
|
||||||
impl Sdio1ClockPin for Pin<Mio12> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1ClockPin for Pin<Mio24> {}
|
|
||||||
impl Sdio1ClockPin for Pin<Mio36> {}
|
|
||||||
impl Sdio1ClockPin for Pin<Mio48> {}
|
|
||||||
|
|
||||||
impl Sdio1CommandPin for Pin<Mio11> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1CommandPin for Pin<Mio23> {}
|
|
||||||
impl Sdio1CommandPin for Pin<Mio35> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1CommandPin for Pin<Mio47> {}
|
|
||||||
|
|
||||||
impl Sdio1Data0Pin for Pin<Mio10> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1Data0Pin for Pin<Mio22> {}
|
|
||||||
impl Sdio1Data0Pin for Pin<Mio34> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1Data0Pin for Pin<Mio46> {}
|
|
||||||
|
|
||||||
impl Sdio1Data1Pin for Pin<Mio13> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1Data1Pin for Pin<Mio25> {}
|
|
||||||
impl Sdio1Data1Pin for Pin<Mio37> {}
|
|
||||||
impl Sdio1Data1Pin for Pin<Mio49> {}
|
|
||||||
|
|
||||||
impl Sdio1Data2Pin for Pin<Mio14> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1Data2Pin for Pin<Mio26> {}
|
|
||||||
impl Sdio1Data2Pin for Pin<Mio38> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1Data2Pin for Pin<Mio50> {}
|
|
||||||
|
|
||||||
impl Sdio1Data2Pin for Pin<Mio15> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1Data3Pin for Pin<Mio27> {}
|
|
||||||
impl Sdio1Data3Pin for Pin<Mio39> {}
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
|
||||||
impl Sdio1Data3Pin for Pin<Mio51> {}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum SdioId {
|
|
||||||
Sdio0,
|
|
||||||
Sdio1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SdioId {
|
|
||||||
/// Steal the ethernet register block for the given ethernet ID.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Circumvents ownership and safety guarantees of the HAL.
|
|
||||||
pub const unsafe fn steal_regs(&self) -> zynq7000::sdio::MmioRegisters<'static> {
|
|
||||||
unsafe {
|
|
||||||
match self {
|
|
||||||
SdioId::Sdio0 => zynq7000::sdio::Registers::new_mmio_fixed_0(),
|
|
||||||
SdioId::Sdio1 => zynq7000::sdio::Registers::new_mmio_fixed_1(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait SdioRegisters {
|
|
||||||
fn reg_block(&self) -> zynq7000::sdio::MmioRegisters<'static>;
|
|
||||||
fn id(&self) -> Option<SdioId>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SdioRegisters for zynq7000::sdio::MmioRegisters<'static> {
|
|
||||||
#[inline]
|
|
||||||
fn reg_block(&self) -> zynq7000::sdio::MmioRegisters<'static> {
|
|
||||||
unsafe { self.clone() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn id(&self) -> Option<SdioId> {
|
|
||||||
let base_addr = unsafe { self.ptr() } as usize;
|
|
||||||
if base_addr == SDIO_BASE_ADDR_0 {
|
|
||||||
return Some(SdioId::Sdio0);
|
|
||||||
} else if base_addr == SDIO_BASE_ADDR_1 {
|
|
||||||
return Some(SdioId::Sdio1);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SdioDivisors {
|
|
||||||
/// Divisor which will be used during the initialization phase when ACMD41 is issued.
|
|
||||||
///
|
|
||||||
/// The SD card specification mentions that the clock needs to be between 100 and 400 kHz for
|
|
||||||
/// that phase.
|
|
||||||
pub divisor_init_phase: SdClockDivisor,
|
|
||||||
/// Divisor for the regular data transfer phase. Common target speeds are 25 MHz or 50 MHz.
|
|
||||||
pub divisor_normal: SdClockDivisor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SdioDivisors {
|
|
||||||
// Calculate the SDIO clock divisors for the given SDIO reference clock and target speed.
|
|
||||||
pub fn calculate(ref_clk: Hertz, target_speed: Hertz) -> Self {
|
|
||||||
const INIT_CLOCK_HZ: u32 = 400_000;
|
|
||||||
let divisor_select_from_value = |value: u32| match value {
|
|
||||||
0..=1 => SdClockDivisor::Div1,
|
|
||||||
2 => SdClockDivisor::Div2,
|
|
||||||
3..=4 => SdClockDivisor::Div4,
|
|
||||||
5..=8 => SdClockDivisor::Div8,
|
|
||||||
9..=16 => SdClockDivisor::Div16,
|
|
||||||
17..=32 => SdClockDivisor::Div32,
|
|
||||||
33..=64 => SdClockDivisor::Div64,
|
|
||||||
65..=128 => SdClockDivisor::Div128,
|
|
||||||
129.. => SdClockDivisor::Div256,
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
divisor_init_phase: divisor_select_from_value(ref_clk.raw().div_ceil(INIT_CLOCK_HZ)),
|
|
||||||
divisor_normal: divisor_select_from_value(ref_clk.raw().div_ceil(target_speed.raw())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate divisors for a regular clock configuration which configures the IO clock as
|
|
||||||
/// source.
|
|
||||||
pub fn calculate_for_io_clock(io_clocks: &IoClocks, target_speed: Hertz) -> Self {
|
|
||||||
Self::calculate(io_clocks.sdio_clk(), target_speed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SdioClockConfig {
|
|
||||||
/// Selects the source clock for the SDIO peripheral reference clock.
|
|
||||||
pub src_sel: SrcSelIo,
|
|
||||||
/// Selects the divisor which divies the source clock to create the SDIO peripheral
|
|
||||||
/// reference clock.
|
|
||||||
pub ref_clock_divisor: u6,
|
|
||||||
/// The SDIO peripheral reference clock is divided again to create the SDIO clock.
|
|
||||||
pub sdio_clock_divisors: SdioDivisors,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SdioClockConfig {
|
|
||||||
pub fn new(
|
|
||||||
src_sel: SrcSelIo,
|
|
||||||
ref_clock_divisor: u6,
|
|
||||||
sdio_clock_divisors: SdioDivisors,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
src_sel,
|
|
||||||
ref_clock_divisor,
|
|
||||||
sdio_clock_divisors,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calculate_for_io_clock(
|
|
||||||
io_clocks: &IoClocks,
|
|
||||||
target_ref_clock: Hertz,
|
|
||||||
target_sdio_speed: Hertz,
|
|
||||||
) -> Option<Self> {
|
|
||||||
let ref_clk = io_clocks.ref_clk();
|
|
||||||
let io_ref_clock_divisor = ref_clk.raw().div_ceil(target_ref_clock.raw());
|
|
||||||
if io_ref_clock_divisor > u6::MAX.as_u32() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let target_speed = ref_clk / io_ref_clock_divisor;
|
|
||||||
|
|
||||||
let sdio_clock_divisors = SdioDivisors::calculate(target_speed, target_sdio_speed);
|
|
||||||
Some(Self {
|
|
||||||
src_sel: SrcSelIo::IoPll,
|
|
||||||
ref_clock_divisor: u6::new(io_ref_clock_divisor as u8),
|
|
||||||
sdio_clock_divisors,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// SDIO low-level interface.
|
|
||||||
///
|
|
||||||
/// Basic building block for higher-level abstraction.
|
|
||||||
pub struct SdioLowLevel {
|
|
||||||
id: SdioId,
|
|
||||||
/// Register block. Direct public access is allowed to allow low-level operations.
|
|
||||||
pub regs: zynq7000::sdio::MmioRegisters<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SdioLowLevel {
|
|
||||||
/// Create a new SDIO low-level interface from the given register block.
|
|
||||||
///
|
|
||||||
/// Returns [None] if the given registers block base address does not correspond to a valid
|
|
||||||
/// Ethernet peripheral.
|
|
||||||
pub fn new(regs: zynq7000::sdio::MmioRegisters<'static>) -> Option<Self> {
|
|
||||||
let id = regs.id()?;
|
|
||||||
Some(Self { id, regs })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn capabilities(&self) -> zynq7000::sdio::Capabilities {
|
|
||||||
self.regs.read_capabilities()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Common SDIO clock configuration routine which should be called once before using the SDIO.
|
|
||||||
///
|
|
||||||
/// This does NOT disable the clock, which should be done before changing the clock
|
|
||||||
/// configuration. It also does NOT enable the clock.
|
|
||||||
///
|
|
||||||
/// It will configure the SDIO peripheral clock as well as initializing the SD clock frequency
|
|
||||||
/// divisor based on the initial phase divider specified in the [SdioDivisors] field of the
|
|
||||||
/// configuration.
|
|
||||||
pub fn configure_clock(&mut self, clock_config: &SdioClockConfig) {
|
|
||||||
unsafe {
|
|
||||||
Slcr::with(|slcr| {
|
|
||||||
slcr.clk_ctrl().modify_sdio_clk_ctrl(|mut val| {
|
|
||||||
val.set_srcsel(clock_config.src_sel);
|
|
||||||
val.set_divisor(clock_config.ref_clock_divisor);
|
|
||||||
if self.id == SdioId::Sdio1 {
|
|
||||||
val.set_clk_1_act(true);
|
|
||||||
} else {
|
|
||||||
val.set_clk_0_act(true);
|
|
||||||
}
|
|
||||||
val
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
self.configure_sd_clock_div_init_phase(&clock_config.sdio_clock_divisors);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure the SD clock divisor for the initialization phase (400 kHz target clock).
|
|
||||||
pub fn configure_sd_clock_div_init_phase(&mut self, divs: &SdioDivisors) {
|
|
||||||
self.regs.modify_clock_timeout_sw_reset_control(|mut val| {
|
|
||||||
val.set_sdclk_frequency_select(divs.divisor_init_phase);
|
|
||||||
val
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configure the SD clock divisor for the normal phase (regular SDIO speed clock).
|
|
||||||
pub fn configure_sd_clock_div_normal_phase(&mut self, divs: &SdioDivisors) {
|
|
||||||
self.regs.modify_clock_timeout_sw_reset_control(|mut val| {
|
|
||||||
val.set_sdclk_frequency_select(divs.divisor_normal);
|
|
||||||
val
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn enable_internal_clock(&mut self) {
|
|
||||||
self.regs.modify_clock_timeout_sw_reset_control(|mut val| {
|
|
||||||
val.set_internal_clock_enable(true);
|
|
||||||
val
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn enable_sd_clock(&mut self) {
|
|
||||||
self.regs.modify_clock_timeout_sw_reset_control(|mut val| {
|
|
||||||
val.set_sd_clock_enable(true);
|
|
||||||
val
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn disable_sd_clock(&mut self) {
|
|
||||||
self.regs.modify_clock_timeout_sw_reset_control(|mut val| {
|
|
||||||
val.set_sd_clock_enable(false);
|
|
||||||
val
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset the SDIO peripheral using the SLCR reset register for SDIO.
|
|
||||||
pub fn reset(&mut self, cycles: u32) {
|
|
||||||
reset(self.id, cycles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Sdio {
|
|
||||||
ll: SdioLowLevel,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sdio {
|
|
||||||
pub fn new_for_sdio_0<
|
|
||||||
Sdio0Clock: Sdio0ClockPin,
|
|
||||||
Sdio0Command: Sdio0CommandPin,
|
|
||||||
Sdio0Data0: Sdio0Data0Pin,
|
|
||||||
Sdio0Data1: Sdio0Data1Pin,
|
|
||||||
Sdio0Data2: Sdio0Data2Pin,
|
|
||||||
Sdio0Data3: Sdio0Data3Pin,
|
|
||||||
>(
|
|
||||||
regs: zynq7000::sdio::MmioRegisters<'static>,
|
|
||||||
clock_config: SdioClockConfig,
|
|
||||||
clock_pin: Sdio0Clock,
|
|
||||||
command_pin: Sdio0Command,
|
|
||||||
data_pins: (Sdio0Data0, Sdio0Data1, Sdio0Data2, Sdio0Data3),
|
|
||||||
) -> Result<Self, InvalidPeripheralError> {
|
|
||||||
let id = regs.id().ok_or(InvalidPeripheralError)?;
|
|
||||||
if id != SdioId::Sdio0 {
|
|
||||||
return Err(InvalidPeripheralError);
|
|
||||||
}
|
|
||||||
Ok(Self::new(
|
|
||||||
regs,
|
|
||||||
clock_config,
|
|
||||||
clock_pin,
|
|
||||||
command_pin,
|
|
||||||
data_pins,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_for_sdio_1<
|
|
||||||
Sdio1Clock: Sdio1ClockPin,
|
|
||||||
Sdio1Command: Sdio1CommandPin,
|
|
||||||
Sdio1Data0: Sdio1Data0Pin,
|
|
||||||
Sdio1Data1: Sdio1Data1Pin,
|
|
||||||
Sdio1Data2: Sdio1Data2Pin,
|
|
||||||
Sdio1Data3: Sdio1Data3Pin,
|
|
||||||
>(
|
|
||||||
regs: zynq7000::sdio::MmioRegisters<'static>,
|
|
||||||
clock_config: SdioClockConfig,
|
|
||||||
clock_pin: Sdio1Clock,
|
|
||||||
command_pin: Sdio1Command,
|
|
||||||
data_pins: (Sdio1Data0, Sdio1Data1, Sdio1Data2, Sdio1Data3),
|
|
||||||
) -> Result<Self, InvalidPeripheralError> {
|
|
||||||
let id = regs.id().ok_or(InvalidPeripheralError)?;
|
|
||||||
if id != SdioId::Sdio1 {
|
|
||||||
return Err(InvalidPeripheralError);
|
|
||||||
}
|
|
||||||
Ok(Self::new(
|
|
||||||
regs,
|
|
||||||
clock_config,
|
|
||||||
clock_pin,
|
|
||||||
command_pin,
|
|
||||||
data_pins,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(
|
|
||||||
regs: zynq7000::sdio::MmioRegisters<'static>,
|
|
||||||
clock_config: SdioClockConfig,
|
|
||||||
clock_pin: impl MioPin,
|
|
||||||
command_pin: impl MioPin,
|
|
||||||
data_pins: (impl MioPin, impl MioPin, impl MioPin, impl MioPin),
|
|
||||||
) -> Self {
|
|
||||||
let mut ll = SdioLowLevel::new(regs).unwrap();
|
|
||||||
Self::initialize(&mut ll, &clock_config);
|
|
||||||
IoPeriphPin::new(clock_pin, MUX_CONF, None);
|
|
||||||
IoPeriphPin::new(command_pin, MUX_CONF, None);
|
|
||||||
IoPeriphPin::new(data_pins.0, MUX_CONF, None);
|
|
||||||
IoPeriphPin::new(data_pins.1, MUX_CONF, None);
|
|
||||||
IoPeriphPin::new(data_pins.2, MUX_CONF, None);
|
|
||||||
IoPeriphPin::new(data_pins.3, MUX_CONF, None);
|
|
||||||
Self { ll }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Direct access to the low-level SDIO driver.
|
|
||||||
pub fn ll_mut(&mut self) -> &mut SdioLowLevel {
|
|
||||||
&mut self.ll
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ll(&self) -> &SdioLowLevel {
|
|
||||||
&self.ll
|
|
||||||
}
|
|
||||||
|
|
||||||
fn initialize(ll: &mut SdioLowLevel, clock_config: &SdioClockConfig) {
|
|
||||||
ll.reset(10);
|
|
||||||
ll.regs.modify_clock_timeout_sw_reset_control(|mut val| {
|
|
||||||
val.set_software_reset_for_all(true);
|
|
||||||
val
|
|
||||||
});
|
|
||||||
ll.regs.modify_clock_timeout_sw_reset_control(|mut val| {
|
|
||||||
val.set_software_reset_for_all(false);
|
|
||||||
val
|
|
||||||
});
|
|
||||||
// Explicitely clear the clock bit.
|
|
||||||
ll.disable_sd_clock();
|
|
||||||
ll.configure_clock(clock_config);
|
|
||||||
// As specified in the TRM, wait until the internal clock is stable before enabling the
|
|
||||||
// SD clock.
|
|
||||||
ll.enable_internal_clock();
|
|
||||||
while !ll
|
|
||||||
.regs
|
|
||||||
.read_clock_timeout_sw_reset_control()
|
|
||||||
.internal_clock_stable()
|
|
||||||
{}
|
|
||||||
ll.enable_sd_clock();
|
|
||||||
|
|
||||||
// TODO: Perform the regular SDIO setup sequence.
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn regs(&mut self) -> &mut zynq7000::sdio::MmioRegisters<'static> {
|
|
||||||
&mut self.ll.regs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset the SDIO peripheral using the SLCR reset register for SDIO.
|
|
||||||
///
|
|
||||||
/// Please note that this function will interfere with an already configured
|
|
||||||
/// SDIO instance.
|
|
||||||
#[inline]
|
|
||||||
pub fn reset(id: SdioId, cycles: u32) {
|
|
||||||
let assert_reset = match id {
|
|
||||||
SdioId::Sdio0 => DualRefAndClockReset::builder()
|
|
||||||
.with_periph1_ref_rst(false)
|
|
||||||
.with_periph0_ref_rst(true)
|
|
||||||
.with_periph1_cpu1x_rst(false)
|
|
||||||
.with_periph0_cpu1x_rst(true)
|
|
||||||
.build(),
|
|
||||||
SdioId::Sdio1 => DualRefAndClockReset::builder()
|
|
||||||
.with_periph1_ref_rst(true)
|
|
||||||
.with_periph0_ref_rst(false)
|
|
||||||
.with_periph1_cpu1x_rst(true)
|
|
||||||
.with_periph0_cpu1x_rst(false)
|
|
||||||
.build(),
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
Slcr::with(|regs| {
|
|
||||||
regs.reset_ctrl().write_sdio(assert_reset);
|
|
||||||
// Keep it in reset for a few cycle.. not sure if this is necessary.
|
|
||||||
for _ in 0..cycles {
|
|
||||||
aarch32_cpu::asm::nop();
|
|
||||||
}
|
|
||||||
regs.reset_ctrl().write_sdio(DualRefAndClockReset::DEFAULT);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
# [v0.1.2] 2025-11-28
|
||||||
|
|
||||||
|
Bugfixes in startup assembler code.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- `.data` initialization is skipped if it is already in place, which is usually the default
|
||||||
|
case because it is flashed to RAM.
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Stack initialization was bugged and stack was not properly initialized for some of the
|
||||||
|
processor modes (all modes except system mode and IRQ mode).
|
||||||
|
- MMU is enabled after the MMU table was copied (which is done in the `.data` coping step).
|
||||||
|
|
||||||
# [v0.1.1] 2025-10-10
|
# [v0.1.1] 2025-10-10
|
||||||
|
|
||||||
Documentation fixes.
|
Documentation fixes.
|
||||||
@@ -16,6 +31,7 @@ Documentation fixes.
|
|||||||
|
|
||||||
Initial release
|
Initial release
|
||||||
|
|
||||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.0...HEAD
|
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.2...HEAD
|
||||||
|
[v0.1.2]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.1...zynq7000-rt-v0.1.2
|
||||||
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.0...zynq7000-rt-v0.1.1
|
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.0...zynq7000-rt-v0.1.1
|
||||||
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-rt-v0.1.0
|
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-rt-v0.1.0
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "zynq7000-rt"
|
name = "zynq7000-rt"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Run-time support for the Zynq7000 family of SoCs for running bare-metal applications"
|
description = "Run-time support for the Zynq7000 family of SoCs for running bare-metal applications"
|
||||||
|
|||||||
@@ -183,7 +183,8 @@ pub mod section_attrs {
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
#[cfg(feature = "rt")]
|
#[cfg(feature = "rt")]
|
||||||
unsafe extern "C" fn load_mmu_table() {
|
unsafe extern "C" fn load_mmu_table() {
|
||||||
let table_base = crate::mmu_table::MMU_L1_PAGE_TABLE.0.get() as u32;
|
// if usize != u32 we are on the wrong platform...
|
||||||
|
let table_base = crate::mmu_table::MMU_L1_PAGE_TABLE.0.get().addr() as u32;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
core::arch::asm!(
|
core::arch::asm!(
|
||||||
|
|||||||
@@ -96,64 +96,43 @@ initialize:
|
|||||||
// Set up stacks first.
|
// Set up stacks first.
|
||||||
ldr r3, =_stack_top
|
ldr r3, =_stack_top
|
||||||
|
|
||||||
// get the current PSR
|
// Set stack pointer (right after) and mask interrupts for IRQ mode (Mode 0x12)
|
||||||
mrs r0, cpsr
|
msr cpsr_c, {irq_mode}
|
||||||
// mask for mode bits
|
|
||||||
mvn r1, #0x1f
|
|
||||||
and r2, r1, r0
|
|
||||||
// IRQ mode
|
|
||||||
orr r2, r2, {irq_mode}
|
|
||||||
msr cpsr, r2
|
|
||||||
// IRQ stack pointer
|
// IRQ stack pointer
|
||||||
mov sp, r3
|
mov sp, r3
|
||||||
ldr r1, =_irq_stack_size
|
ldr r1, =_irq_stack_size
|
||||||
sub r3, r3, r1
|
sub r3, r3, r1
|
||||||
|
|
||||||
mrs r0, cpsr
|
// Set stack pointer (right after) and mask interrupts for Supervisor/SVC mode (Mode 0x13)
|
||||||
and r2, r1, r0
|
msr cpsr_c, {svc_mode}
|
||||||
// Supervisor mode
|
|
||||||
orr r2, r2, {svc_mode}
|
|
||||||
msr cpsr, r2
|
|
||||||
// Supervisor stack pointer
|
// Supervisor stack pointer
|
||||||
mov sp, r3
|
mov sp, r3
|
||||||
ldr r1, =_svc_stack_size
|
ldr r1, =_svc_stack_size
|
||||||
sub r3, r3, r1
|
sub r3, r3, r1
|
||||||
|
|
||||||
mrs r0, cpsr
|
// Set stack pointer (right after) and mask interrupts for Abort/ABT mode (Mode 0x17)
|
||||||
and r2, r1, r0
|
msr cpsr_c, {abt_mode}
|
||||||
// Abort mode
|
|
||||||
orr r2, r2, {abt_mode}
|
|
||||||
msr cpsr, r2
|
|
||||||
// Abort stack pointer
|
// Abort stack pointer
|
||||||
mov sp, r3
|
mov sp, r3
|
||||||
ldr r1, =_abt_stack_size
|
ldr r1, =_abt_stack_size
|
||||||
sub r3, r3, r1
|
sub r3, r3, r1
|
||||||
|
|
||||||
mrs r0, cpsr
|
// Set stack pointer (right after) and mask interrupts for FIQ mode (Mode 0x11)
|
||||||
and r2, r1, r0
|
msr cpsr_c, {fiq_mode}
|
||||||
// FIQ mode
|
|
||||||
orr r2, r2, {fiq_mode}
|
|
||||||
msr cpsr, r2
|
|
||||||
// FIQ stack pointer
|
// FIQ stack pointer
|
||||||
mov sp, r3
|
mov sp, r3
|
||||||
ldr r1, =_fiq_stack_size
|
ldr r1, =_fiq_stack_size
|
||||||
sub r3, r3, r1
|
sub r3, r3, r1
|
||||||
|
|
||||||
mrs r0, cpsr
|
// Set stack pointer (right after) and mask interrupts for Undefined/UND mode (Mode 0x1B)
|
||||||
and r2, r1, r0
|
msr cpsr_c, {und_mode}
|
||||||
// Undefined mode
|
|
||||||
orr r2, r2, {und_mode}
|
|
||||||
msr cpsr, r2
|
|
||||||
// Undefined stack pointer
|
// Undefined stack pointer
|
||||||
mov sp, r3
|
mov sp, r3
|
||||||
ldr r1, =_und_stack_size
|
ldr r1, =_und_stack_size
|
||||||
sub r3, r3, r1
|
sub r3, r3, r1
|
||||||
|
|
||||||
mrs r0, cpsr
|
// Set stack pointer (right after) and mask interrupts for System/SYS mode (Mode 0x1F)
|
||||||
and r2, r1, r0
|
msr cpsr_c, {sys_mode}
|
||||||
// System mode
|
|
||||||
orr r2, r2, {sys_mode}
|
|
||||||
msr cpsr, r2
|
|
||||||
// System stack pointer (main stack)
|
// System stack pointer (main stack)
|
||||||
mov sp, r3
|
mov sp, r3
|
||||||
|
|
||||||
@@ -163,18 +142,6 @@ initialize:
|
|||||||
orr r0, r0, #0x1
|
orr r0, r0, #0x1
|
||||||
str r0, [r7]
|
str r0, [r7]
|
||||||
|
|
||||||
/* enable MMU and cache */
|
|
||||||
bl load_mmu_table
|
|
||||||
|
|
||||||
mvn r0,#0 /* Load MMU domains -- all ones=manager */
|
|
||||||
mcr p15,0,r0,c3,c0,0
|
|
||||||
|
|
||||||
/* Enable mmu, icahce and dcache */
|
|
||||||
ldr r0,=CRValMmuCac
|
|
||||||
mcr p15,0,r0,c1,c0,0 /* Enable cache and MMU */
|
|
||||||
dsb /* dsb allow the MMU to start up */
|
|
||||||
isb /* isb flush prefetch buffer */
|
|
||||||
|
|
||||||
/* Write to ACTLR */
|
/* Write to ACTLR */
|
||||||
mrc p15, 0, r0, c1, c0, 1 /* Read ACTLR*/
|
mrc p15, 0, r0, c1, c0, 1 /* Read ACTLR*/
|
||||||
orr r0, r0, #(0x01 << 6) /* set SMP bit */
|
orr r0, r0, #(0x01 << 6) /* set SMP bit */
|
||||||
@@ -220,13 +187,29 @@ initialize:
|
|||||||
ldr r0, =__sdata
|
ldr r0, =__sdata
|
||||||
ldr r1, =__edata
|
ldr r1, =__edata
|
||||||
ldr r2, =__sidata
|
ldr r2, =__sidata
|
||||||
|
cmp r0, r2 /* Shortcut if code is run from RAM and .data is there already */
|
||||||
|
beq data_init_done
|
||||||
0:
|
0:
|
||||||
cmp r1, r0
|
cmp r1, r0
|
||||||
beq 1f
|
beq data_init_done
|
||||||
ldm r2!, {{r3}}
|
ldm r2!, {{r3}}
|
||||||
stm r0!, {{r3}}
|
stm r0!, {{r3}}
|
||||||
b 0b
|
b 0b
|
||||||
1:
|
data_init_done:
|
||||||
|
/* enable MMU and cache */
|
||||||
|
/* MMU Table is in .data, so this needs to be performed after .data is relocated */
|
||||||
|
/* (Even if in most cases, .data is already in RAM and relocation is a no-op) */
|
||||||
|
bl load_mmu_table
|
||||||
|
|
||||||
|
mvn r0,#0 /* Load MMU domains -- all ones=manager */
|
||||||
|
mcr p15,0,r0,c3,c0,0
|
||||||
|
|
||||||
|
/* Enable mmu, icache and dcache */
|
||||||
|
ldr r0,=CRValMmuCac
|
||||||
|
mcr p15,0,r0,c1,c0,0 /* Enable cache and MMU */
|
||||||
|
dsb /* dsb allow the MMU to start up */
|
||||||
|
isb /* isb flush prefetch buffer */
|
||||||
|
|
||||||
// Jump to application
|
// Jump to application
|
||||||
// Load CPU ID 0, which will be used as a function argument to the boot_core function.
|
// Load CPU ID 0, which will be used as a function argument to the boot_core function.
|
||||||
mov r0, #0x0
|
mov r0, #0x0
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ pub mod l2_cache;
|
|||||||
pub mod mpcore;
|
pub mod mpcore;
|
||||||
pub mod priv_tim;
|
pub mod priv_tim;
|
||||||
pub mod qspi;
|
pub mod qspi;
|
||||||
pub mod sdio;
|
|
||||||
pub mod slcr;
|
pub mod slcr;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
pub mod ttc;
|
pub mod ttc;
|
||||||
@@ -64,8 +63,6 @@ pub struct Peripherals {
|
|||||||
pub qspi: qspi::MmioRegisters<'static>,
|
pub qspi: qspi::MmioRegisters<'static>,
|
||||||
pub devcfg: devcfg::MmioRegisters<'static>,
|
pub devcfg: devcfg::MmioRegisters<'static>,
|
||||||
pub xadc: xadc::MmioRegisters<'static>,
|
pub xadc: xadc::MmioRegisters<'static>,
|
||||||
pub sdio_0: sdio::MmioRegisters<'static>,
|
|
||||||
pub sdio_1: sdio::MmioRegisters<'static>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Peripherals {
|
impl Peripherals {
|
||||||
@@ -106,8 +103,6 @@ impl Peripherals {
|
|||||||
qspi: qspi::Registers::new_mmio_fixed(),
|
qspi: qspi::Registers::new_mmio_fixed(),
|
||||||
devcfg: devcfg::Registers::new_mmio_fixed(),
|
devcfg: devcfg::Registers::new_mmio_fixed(),
|
||||||
xadc: xadc::Registers::new_mmio_fixed(),
|
xadc: xadc::Registers::new_mmio_fixed(),
|
||||||
sdio_0: sdio::Registers::new_mmio_fixed_0(),
|
|
||||||
sdio_1: sdio::Registers::new_mmio_fixed_1(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,489 +0,0 @@
|
|||||||
use arbitrary_int::{u2, u4, u6, u12};
|
|
||||||
|
|
||||||
pub const SDIO_BASE_ADDR_0: usize = 0xE010_0000;
|
|
||||||
pub const SDIO_BASE_ADDR_1: usize = 0xE010_1000;
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u3, exhaustive = true)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum BufferSize {
|
|
||||||
_4kB = 0b000,
|
|
||||||
_8kB = 0b001,
|
|
||||||
_16kB = 0b010,
|
|
||||||
_32kB = 0b011,
|
|
||||||
_64kB = 0b100,
|
|
||||||
_128kB = 0b101,
|
|
||||||
_256kB = 0b110,
|
|
||||||
_512kB = 0b111,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug)]
|
|
||||||
pub struct BlockParams {
|
|
||||||
#[bits(16..=31, rw)]
|
|
||||||
blocks_count: u16,
|
|
||||||
#[bits(12..=14, rw)]
|
|
||||||
buffer_size: BufferSize,
|
|
||||||
#[bits(0..=11, rw)]
|
|
||||||
block_size: u12,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u2, exhaustive = true)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum CommandType {
|
|
||||||
Normal = 0b00,
|
|
||||||
Suspend = 0b01,
|
|
||||||
Resume = 0b10,
|
|
||||||
Abort = 0b11,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u2, exhaustive = true)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum ResponseLength {
|
|
||||||
NoResponse = 0b00,
|
|
||||||
ResponseLength136 = 0b01,
|
|
||||||
ResponseLength48 = 0b10,
|
|
||||||
ResponseLength48Check = 0b11,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum BlockSelect {
|
|
||||||
SingleBlock = 0,
|
|
||||||
MultiBlock = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum TransferDirection {
|
|
||||||
/// Host to card.
|
|
||||||
Write = 0,
|
|
||||||
/// Card to host.
|
|
||||||
Read = 1,
|
|
||||||
}
|
|
||||||
#[bitbybit::bitfield(u32, debug)]
|
|
||||||
pub struct TransferModeAndCommand {
|
|
||||||
/// Set to command number (CMD0-63, ACMD0-63)
|
|
||||||
#[bits(24..=29, rw)]
|
|
||||||
command_index: u6,
|
|
||||||
#[bits(22..=23, rw)]
|
|
||||||
command_type: CommandType,
|
|
||||||
/// Set to [false] for the following:
|
|
||||||
///
|
|
||||||
/// 1. Commands using only CMD line (ex. CMD52).
|
|
||||||
/// 2. Commands with no data transfer but using busy signal on DAT\[0\].
|
|
||||||
/// 3. Resume Command.
|
|
||||||
#[bit(21, rw)]
|
|
||||||
data_is_present: bool,
|
|
||||||
/// When 1, the host controller checks the index field in the response to see if it has the
|
|
||||||
/// same value as the command index.
|
|
||||||
#[bit(20, rw)]
|
|
||||||
command_index_check_enable: bool,
|
|
||||||
/// When 1, the host controller checks the CRC field in the response.
|
|
||||||
#[bit(18, rw)]
|
|
||||||
command_crc_check_enable: bool,
|
|
||||||
#[bits(16..=17, rw)]
|
|
||||||
response_type_select: u2,
|
|
||||||
#[bit(5, rw)]
|
|
||||||
multi_single_block_select: BlockSelect,
|
|
||||||
#[bit(4, rw)]
|
|
||||||
data_transfer_direction: TransferDirection,
|
|
||||||
/// Multiple block transfers for memory require CMD12 to stop the transaction. When this bit is
|
|
||||||
/// 1, the host controller issues CMD12 automatically when completing the last block tranfer.
|
|
||||||
#[bit(2, rw)]
|
|
||||||
auto_cmd12_enable: bool,
|
|
||||||
/// Enable block count register, which is only relevant for multiple block transfers.
|
|
||||||
#[bit(1, rw)]
|
|
||||||
block_count_enable: bool,
|
|
||||||
#[bit(0, rw)]
|
|
||||||
dma_enable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug)]
|
|
||||||
pub struct PresentState {
|
|
||||||
#[bit(24, r)]
|
|
||||||
cmd_line_signal_level: bool,
|
|
||||||
#[bits(20..=23, r)]
|
|
||||||
data_line_signal_level: u4,
|
|
||||||
/// The Write Protect Switch is supported for memory and combo cards. This bit reflects the
|
|
||||||
/// inversion of the SDx_WP pin.
|
|
||||||
#[bit(19, r)]
|
|
||||||
write_protect_switch_level: bool,
|
|
||||||
/// This bit reflects the inverse value of the SDx_CDn pin.
|
|
||||||
#[bit(18, r)]
|
|
||||||
card_detect_pin_level: bool,
|
|
||||||
/// This bit is used for testing. If it is 0, the Card Detect Pin Level is not stable. If this
|
|
||||||
/// bit is set to 1, it means the Card Detect Pin Level is stable. The Software Reset For All
|
|
||||||
/// in the Software Reset Register shall not affect this bit.
|
|
||||||
#[bit(17, r)]
|
|
||||||
card_state_stable: bool,
|
|
||||||
/// This bit indicates whether a card has been inserted. Changing from 0 to 1 generates a Card
|
|
||||||
/// Insertion interrupt in the Normal Interrupt Status register and changing from 1 to 0
|
|
||||||
/// generates a Card Removal Interrupt in the Normal Interrupt Status register. The Software
|
|
||||||
/// Reset For All in the Software Reset register shall not affect this bit. If a Card is
|
|
||||||
/// removed while its power is on and its clock is oscillating, the HC shall clear SD Bus Power
|
|
||||||
/// in the Power Control register and SD Clock Enable in the Clock control register. In
|
|
||||||
/// addition the HD should clear the HC by the Software Reset For All in Software register. The
|
|
||||||
/// card detect is active regardless of the SD Bus Power.
|
|
||||||
#[bit(16, r)]
|
|
||||||
card_inserted: bool,
|
|
||||||
/// This status is used for non-DMA read transfers. This read only flag indicates that valid
|
|
||||||
/// data exists in the host side buffer status. If this bit is 1, readable data exists in the
|
|
||||||
/// buffer. A change of this bit from 1 to 0 occurs when all the block data is read from the
|
|
||||||
/// buffer. A change of this bit from 0 to 1 occurs when all the block data is ready in the
|
|
||||||
/// buffer and generates the Buffer Read Ready Interrupt.
|
|
||||||
#[bit(11, r)]
|
|
||||||
buffer_readable: bool,
|
|
||||||
/// This status is used for non-DMA write transfers. This read only flag indicates if space is
|
|
||||||
/// available for write data. If this bit is 1, data can be written to the buffer. A change of
|
|
||||||
/// this bit from 1 to 0 occurs when all the block data is written to the buffer. A change of
|
|
||||||
/// this bit from 0 to 1 occurs when top of block data can be written to the buffer and
|
|
||||||
/// generates the Buffer Write Ready Interrupt.
|
|
||||||
#[bit(10, r)]
|
|
||||||
buffer_writable: bool,
|
|
||||||
/// This status is used for detecting completion of a read transfer. This bit is set to 1 for
|
|
||||||
/// either of the following conditions:
|
|
||||||
///
|
|
||||||
/// 1. After the end bit of the read command
|
|
||||||
/// 2. When writing a 1 to continue Request in the Block Gap Control register to restart a read
|
|
||||||
/// transfer.
|
|
||||||
///
|
|
||||||
/// This bit is cleared to 0 for either of the following conditions:
|
|
||||||
///
|
|
||||||
/// 1. When the last data block as specified by block length is transferred to the system.
|
|
||||||
/// 2. When all valid data blocks have been transferred to the system and no current block
|
|
||||||
/// transfers are being sent as a result of the Stop At Block Gap Request set to 1. A transfer
|
|
||||||
/// complete interrupt is generated when this bit changes to 0.
|
|
||||||
#[bit(9, r)]
|
|
||||||
read_transfer_active: bool,
|
|
||||||
/// This status indicates a write transfer is active. If this bit is 0, it means no valid write
|
|
||||||
/// data exists in the HC. This bit is set in either of the following cases: 1. After the end
|
|
||||||
/// bit of the write command. 2. When writing a 1 to Continue Request in the Block Gap Control
|
|
||||||
/// register to restart a write transfer.
|
|
||||||
///
|
|
||||||
/// This bit is cleared in either of the following cases:
|
|
||||||
///
|
|
||||||
/// 1. After getting the CRC status of the last data block as specified by the transfer count
|
|
||||||
/// (Single or Multiple)
|
|
||||||
/// 2. After getting a CRC status of any block where data transmission is about to be stopped
|
|
||||||
/// by a Stop At Block Gap Request.
|
|
||||||
///
|
|
||||||
/// During a write transaction, a Block Gap Event interrupt is generated when this bit is
|
|
||||||
/// changed to 0, as a result of the Stop At Block Gap Request being set. This status is useful
|
|
||||||
/// for the HD in determining when to issue commands during write busy.
|
|
||||||
#[bit(8, r)]
|
|
||||||
write_transfer_active: bool,
|
|
||||||
#[bit(2, r)]
|
|
||||||
dat_line_active: bool,
|
|
||||||
/// This status bit is generated if either the DAT Line Active or the Read transfer Active is
|
|
||||||
/// set to 1. If this bit is 0, it indicates the HC can issue the next SD command. Commands
|
|
||||||
/// with busy signal belong to Command Inhibit (DAT) (ex. R1b, R5b type). Changing from 1 to 0
|
|
||||||
/// generates a Transfer Complete interrupt in the Normal interrupt status register.
|
|
||||||
#[bit(1, r)]
|
|
||||||
command_inhibit_dat: bool,
|
|
||||||
/// 0 indicates the CMD line is not in use and the host controller can issue a SD command
|
|
||||||
/// using the CMD line. This bit is set immediately after the Command register (00Fh) is
|
|
||||||
/// written. This bit is cleared when the command response is received. Even if the Command
|
|
||||||
/// Inhibit (DAT) is set to 1, Commands using only the CMD line can be issued if this bit is 0.
|
|
||||||
/// Changing from 1 to 0 generates a Command complete interrupt in the Normal Interrupt Status
|
|
||||||
/// register. If the HC cannot issue the command because of a command conflict error or because
|
|
||||||
/// of Command Not Issued By Auto CMD12 Error, this bit shall remain 1 and the Command Complete
|
|
||||||
/// is not set. Status issuing Auto CMD12 is not read from this bit.
|
|
||||||
#[bit(0, r)]
|
|
||||||
command_inhibit_cmd: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum DataTransferWidth {
|
|
||||||
_1bit = 0,
|
|
||||||
_4bit = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u2, exhaustive = true)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum DmaSelect {
|
|
||||||
Sdma = 0b00,
|
|
||||||
Adma1_32bits = 0b01,
|
|
||||||
Adma2_32bits = 0b10,
|
|
||||||
Adma2_64bits = 0b11,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u3, exhaustive = false)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum SdBusVoltageSelect {
|
|
||||||
_1_8V = 0b101,
|
|
||||||
_3_0V = 0b110,
|
|
||||||
_3_3V = 0b111,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug)]
|
|
||||||
pub struct HostPowerBlockgapWakeupControl {
|
|
||||||
#[bit(26, rw)]
|
|
||||||
wakeup_event_enable_on_sd_card_removal: bool,
|
|
||||||
#[bit(25, rw)]
|
|
||||||
wakeup_event_enable_on_sd_card_insertion: bool,
|
|
||||||
#[bit(24, rw)]
|
|
||||||
wakeup_event_enable_on_card_interrupt: bool,
|
|
||||||
#[bit(19, rw)]
|
|
||||||
interrupt_at_block_gap: bool,
|
|
||||||
#[bit(18, rw)]
|
|
||||||
read_wait_control: bool,
|
|
||||||
#[bit(17, rw)]
|
|
||||||
continue_request: bool,
|
|
||||||
#[bit(16, rw)]
|
|
||||||
stop_as_block_gap_request: bool,
|
|
||||||
#[bits(9..=11, rw)]
|
|
||||||
sd_bus_voltage_select: Option<SdBusVoltageSelect>,
|
|
||||||
#[bit(8, rw)]
|
|
||||||
sd_bus_power: bool,
|
|
||||||
#[bit(7, rw)]
|
|
||||||
card_detect_signal_detection: bool,
|
|
||||||
#[bit(6, rw)]
|
|
||||||
card_detetect_test_level: bool,
|
|
||||||
#[bits(3..=4, rw)]
|
|
||||||
dma_select: DmaSelect,
|
|
||||||
#[bit(2, rw)]
|
|
||||||
high_speed_enable: bool,
|
|
||||||
#[bit(1, rw)]
|
|
||||||
data_transfer_width: DataTransferWidth,
|
|
||||||
#[bit(0, rw)]
|
|
||||||
led_control: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u8, exhaustive = false)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub enum SdClockDivisor {
|
|
||||||
Div256 = 0x80,
|
|
||||||
Div128 = 0x40,
|
|
||||||
Div64 = 0x20,
|
|
||||||
Div32 = 0x10,
|
|
||||||
Div16 = 0x08,
|
|
||||||
Div8 = 0x04,
|
|
||||||
Div4 = 0x02,
|
|
||||||
Div2 = 0x01,
|
|
||||||
Div1 = 0x00,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug)]
|
|
||||||
pub struct ClockAndTimeoutAndSwResetControl {
|
|
||||||
#[bit(26, rw)]
|
|
||||||
software_reset_for_dat_line: bool,
|
|
||||||
#[bit(25, rw)]
|
|
||||||
software_reset_for_cmd_line: bool,
|
|
||||||
#[bit(24, rw)]
|
|
||||||
software_reset_for_all: bool,
|
|
||||||
/// Interval: TMCLK * 2^(13 + register value)
|
|
||||||
///
|
|
||||||
/// 0b1111 is reserved.
|
|
||||||
#[bits(16..=19, rw)]
|
|
||||||
data_timeout_counter_value: u4,
|
|
||||||
#[bits(8..=15, rw)]
|
|
||||||
sdclk_frequency_select: Option<SdClockDivisor>,
|
|
||||||
#[bit(2, rw)]
|
|
||||||
sd_clock_enable: bool,
|
|
||||||
#[bit(1, r)]
|
|
||||||
internal_clock_stable: bool,
|
|
||||||
#[bit(0, rw)]
|
|
||||||
internal_clock_enable: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug)]
|
|
||||||
pub struct InterruptStatus {
|
|
||||||
#[bit(29, rw)]
|
|
||||||
ceata_error_status: bool,
|
|
||||||
#[bit(28, rw)]
|
|
||||||
target_response_error: bool,
|
|
||||||
#[bit(25, rw)]
|
|
||||||
adma_error: bool,
|
|
||||||
#[bit(24, rw)]
|
|
||||||
auto_cmd12_error: bool,
|
|
||||||
#[bit(23, rw)]
|
|
||||||
current_limit_error: bool,
|
|
||||||
#[bit(22, rw)]
|
|
||||||
data_end_bit_error: bool,
|
|
||||||
#[bit(21, rw)]
|
|
||||||
data_crc_error: bool,
|
|
||||||
#[bit(20, rw)]
|
|
||||||
data_timeout_error: bool,
|
|
||||||
#[bit(19, rw)]
|
|
||||||
command_index_error: bool,
|
|
||||||
#[bit(18, rw)]
|
|
||||||
command_end_bit_error: bool,
|
|
||||||
#[bit(17, rw)]
|
|
||||||
command_crc_error: bool,
|
|
||||||
#[bit(16, rw)]
|
|
||||||
command_timeout_error: bool,
|
|
||||||
#[bit(15, r)]
|
|
||||||
error_interrupt: bool,
|
|
||||||
#[bit(10, rw)]
|
|
||||||
boot_terminate: bool,
|
|
||||||
#[bit(9, rw)]
|
|
||||||
boot_ack_recv: bool,
|
|
||||||
#[bit(8, r)]
|
|
||||||
card_interrupt: bool,
|
|
||||||
#[bit(7, rw)]
|
|
||||||
card_removal: bool,
|
|
||||||
#[bit(6, rw)]
|
|
||||||
card_insertion: bool,
|
|
||||||
#[bit(5, rw)]
|
|
||||||
buffer_read_ready: bool,
|
|
||||||
#[bit(4, rw)]
|
|
||||||
buffer_write_ready: bool,
|
|
||||||
#[bit(3, rw)]
|
|
||||||
dma_interrupt: bool,
|
|
||||||
#[bit(2, rw)]
|
|
||||||
blockgap_event: bool,
|
|
||||||
#[bit(1, rw)]
|
|
||||||
transfer_complete: bool,
|
|
||||||
#[bit(0, rw)]
|
|
||||||
command_complete: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug)]
|
|
||||||
pub struct InterruptMask {
|
|
||||||
#[bit(29, rw)]
|
|
||||||
ceata_error_status: bool,
|
|
||||||
#[bit(28, rw)]
|
|
||||||
target_response_error: bool,
|
|
||||||
#[bit(25, rw)]
|
|
||||||
adma_error: bool,
|
|
||||||
#[bit(24, rw)]
|
|
||||||
auto_cmd12_error: bool,
|
|
||||||
#[bit(23, rw)]
|
|
||||||
current_limit_error: bool,
|
|
||||||
#[bit(22, rw)]
|
|
||||||
data_end_bit_error: bool,
|
|
||||||
#[bit(21, rw)]
|
|
||||||
data_crc_error: bool,
|
|
||||||
#[bit(20, rw)]
|
|
||||||
data_timeout_error: bool,
|
|
||||||
#[bit(19, rw)]
|
|
||||||
command_index_error: bool,
|
|
||||||
#[bit(18, rw)]
|
|
||||||
command_end_bit_error: bool,
|
|
||||||
#[bit(17, rw)]
|
|
||||||
command_crc_error: bool,
|
|
||||||
#[bit(16, rw)]
|
|
||||||
command_timeout_error: bool,
|
|
||||||
#[bit(15, rw)]
|
|
||||||
error_interrupt: bool,
|
|
||||||
#[bit(10, rw)]
|
|
||||||
boot_terminate: bool,
|
|
||||||
#[bit(9, rw)]
|
|
||||||
boot_ack_recv: bool,
|
|
||||||
#[bit(8, rw)]
|
|
||||||
card_interrupt: bool,
|
|
||||||
#[bit(7, rw)]
|
|
||||||
card_removal: bool,
|
|
||||||
#[bit(6, rw)]
|
|
||||||
card_insertion: bool,
|
|
||||||
#[bit(5, rw)]
|
|
||||||
buffer_read_ready: bool,
|
|
||||||
#[bit(4, rw)]
|
|
||||||
buffer_write_ready: bool,
|
|
||||||
#[bit(3, rw)]
|
|
||||||
dma_interrupt: bool,
|
|
||||||
#[bit(2, rw)]
|
|
||||||
blockgap_event: bool,
|
|
||||||
#[bit(1, rw)]
|
|
||||||
transfer_complete: bool,
|
|
||||||
#[bit(0, rw)]
|
|
||||||
command_complete: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32, debug)]
|
|
||||||
pub struct Capabilities {
|
|
||||||
#[bit(30, rw)]
|
|
||||||
spi_block_mode: bool,
|
|
||||||
#[bit(29, rw)]
|
|
||||||
spi_mode: bool,
|
|
||||||
#[bit(28, rw)]
|
|
||||||
_64_bit_system_bus_support: bool,
|
|
||||||
#[bit(27, rw)]
|
|
||||||
interrupt_mode: bool,
|
|
||||||
#[bit(26, rw)]
|
|
||||||
voltage_support_1_8v: bool,
|
|
||||||
#[bit(25, rw)]
|
|
||||||
voltage_support_3_0v: bool,
|
|
||||||
#[bit(24, rw)]
|
|
||||||
voltage_support_3_3v: bool,
|
|
||||||
#[bit(23, rw)]
|
|
||||||
suspend_resume_support: bool,
|
|
||||||
#[bit(22, rw)]
|
|
||||||
sdma_support: bool,
|
|
||||||
#[bit(21, rw)]
|
|
||||||
high_speed_support: bool,
|
|
||||||
#[bit(19, rw)]
|
|
||||||
adma2_support: bool,
|
|
||||||
#[bit(18, rw)]
|
|
||||||
extended_media_bus_support: bool,
|
|
||||||
#[bits(16..=17, rw)]
|
|
||||||
max_block_length: u2,
|
|
||||||
#[bit(7, rw)]
|
|
||||||
timeout_clock_unit: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(derive_mmio::Mmio)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Registers {
|
|
||||||
sdma_system_addr: u32,
|
|
||||||
block_params: u32,
|
|
||||||
/// Bit 39-8 of Command-Format.
|
|
||||||
argument: u32,
|
|
||||||
transfer_mode_and_command: TransferModeAndCommand,
|
|
||||||
#[mmio(PureRead)]
|
|
||||||
responses: [u32; 4],
|
|
||||||
buffer_data_port: u32,
|
|
||||||
#[mmio(PureRead)]
|
|
||||||
present_state: PresentState,
|
|
||||||
host_power_blockgap_wakeup_control: HostPowerBlockgapWakeupControl,
|
|
||||||
clock_timeout_sw_reset_control: ClockAndTimeoutAndSwResetControl,
|
|
||||||
interrupt_status: InterruptStatus,
|
|
||||||
interrupt_status_enable: InterruptMask,
|
|
||||||
interrupt_signal_enable: InterruptMask,
|
|
||||||
#[mmio(PureRead)]
|
|
||||||
auto_cmd12_error_status: u32,
|
|
||||||
#[mmio(PureRead)]
|
|
||||||
capabilities: Capabilities,
|
|
||||||
_reserved_0: u32,
|
|
||||||
#[mmio(PureRead)]
|
|
||||||
maximum_current_capabilities: u32,
|
|
||||||
_reserved_1: u32,
|
|
||||||
force_event_register: u32,
|
|
||||||
adma_error_status: u32,
|
|
||||||
adma_system_address: u32,
|
|
||||||
_reserved_2: u32,
|
|
||||||
boot_timeout_control: u32,
|
|
||||||
debug_selection: u32,
|
|
||||||
_reserved_3: [u32; 0x22],
|
|
||||||
spi_interrupt_support: u32,
|
|
||||||
_reserved_4: [u32; 0x2],
|
|
||||||
slot_interrupt_status_host_controll_version: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assertions::const_assert_eq!(core::mem::size_of::<Registers>(), 0x100);
|
|
||||||
|
|
||||||
impl Registers {
|
|
||||||
/// Create a new SDIO MMIO instance for SDIO 0 at address [SDIO_BASE_ADDR_0].
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This API can be used to potentially create a driver to the same peripheral structure
|
|
||||||
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
|
||||||
/// interfere with each other.
|
|
||||||
#[inline]
|
|
||||||
pub const unsafe fn new_mmio_fixed_0() -> MmioRegisters<'static> {
|
|
||||||
unsafe { Self::new_mmio_at(SDIO_BASE_ADDR_0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new SDIO MMIO instance for SDIO 1 at address [SDIO_BASE_ADDR_1].
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This API can be used to potentially create a driver to the same peripheral structure
|
|
||||||
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
|
||||||
/// interfere with each other.
|
|
||||||
#[inline]
|
|
||||||
pub const unsafe fn new_mmio_fixed_1() -> MmioRegisters<'static> {
|
|
||||||
unsafe { Self::new_mmio_at(SDIO_BASE_ADDR_1) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user