continue with SDIO
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled

This commit is contained in:
Robin Mueller
2025-10-27 01:05:49 +01:00
parent 08efd91dd4
commit 9fd048c2a2
3 changed files with 220 additions and 39 deletions

View File

@@ -52,7 +52,10 @@ impl ClockDivisors {
/// Calls [Self::calculate_for_rgmii], assuming that the IO clock is the reference clock,
/// which is the default clock for the Ethernet module.
pub fn calculate_for_rgmii_and_io_clock(io_clks: IoClocks, target_speed: Speed) -> (Self, u32) {
pub fn calculate_for_rgmii_and_io_clock(
io_clks: &IoClocks,
target_speed: Speed,
) -> (Self, u32) {
Self::calculate_for_rgmii(io_clks.ref_clk(), target_speed)
}

View File

@@ -1,5 +1,6 @@
use arbitrary_int::{traits::Integer as _, u3, u6};
use zynq7000::{
sdio::SdClockDivisor,
sdio::{SDIO_BASE_ADDR_0, SDIO_BASE_ADDR_1, SdClockDivisor},
slcr::{clocks::SrcSelIo, reset::DualRefAndClockReset},
};
@@ -9,21 +10,26 @@ use crate::gpio::mio::{
Mio41, Mio42, Mio43, Mio44, Mio45, Mio46, Mio47, Mio50, Mio51,
};
use crate::{
clocks::Clocks,
gpio::mio::{
Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34,
Mio35, Mio36, Mio37, Mio38, Mio39, Mio48, Mio49, Pin,
clocks::{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,
};
pub trait Sdio0ClockPin {}
pub trait Sdio0CommandPin {}
pub trait Sdio0Data0Pin {}
pub trait Sdio0Data1Pin {}
pub trait Sdio0Data2Pin {}
pub trait Sdio0Data3Pin {}
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> {}
@@ -61,12 +67,12 @@ impl Sdio0Data3Pin for Pin<Mio33> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl Sdio0Data3Pin for Pin<Mio45> {}
pub trait Sdio1ClockPin {}
pub trait Sdio1CommandPin {}
pub trait Sdio1Data0Pin {}
pub trait Sdio1Data1Pin {}
pub trait Sdio1Data2Pin {}
pub trait Sdio1Data3Pin {}
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"))]
@@ -114,9 +120,44 @@ pub enum SdioId {
Sdio1,
}
pub struct Sdio {}
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(),
}
}
}
}
impl Sdio {}
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.
@@ -129,13 +170,9 @@ pub struct SdioDivisors {
}
impl SdioDivisors {
pub fn calculate(src_sel: SrcSelIo, clocks: &Clocks, target_speed: Hertz) -> Self {
// 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 ref_clk = match src_sel {
SrcSelIo::IoPll | SrcSelIo::IoPllAlt => clocks.io_clocks().ref_clk(),
SrcSelIo::ArmPll => clocks.arm_clocks().ref_clk(),
SrcSelIo::DdrPll => clocks.ddr_clocks().ref_clk(),
};
let divisor_select_from_value = |value: u32| match value {
0..=1 => SdClockDivisor::Div1,
2 => SdClockDivisor::Div2,
@@ -152,31 +189,158 @@ impl SdioDivisors {
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 {
src_sel: SrcSelIo,
divisors: SdioDivisors,
/// 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, divisors: SdioDivisors) -> Self {
Self { src_sel, divisors }
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(clocks: &Clocks, target_speed: Hertz) -> Self {
let divisors = SdioDivisors::calculate(SrcSelIo::IoPll, clocks, target_speed);
Self {
src_sel: SrcSelIo::IoPll,
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,
})
}
}
/// Reset the UART peripheral using the SLCR reset register for UART.
pub struct Sdio {
regs: zynq7000::sdio::MmioRegisters<'static>,
}
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),
) -> Option<Self> {
let id = regs.id()?;
if id != SdioId::Sdio1 {
return None;
}
Some(Self::new(
id,
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),
) -> Option<Self> {
let id = regs.id()?;
if id != SdioId::Sdio1 {
return None;
}
Some(Self::new(
id,
regs,
clock_config,
clock_pin,
command_pin,
data_pins,
))
}
fn new(
id: SdioId,
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 {
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 { regs }
}
fn initialize(
id: SdioId,
regs: &mut zynq7000::sdio::MmioRegisters<'static>,
clock_config: &SdioClockConfig,
) {
reset(id, 10);
// TODO: Clock Config
// TODO: There is probably some other configuartion necessary.. the docs really are not
// complete here..
unsafe {}
}
#[inline]
pub fn regs(&mut self) -> &mut zynq7000::sdio::MmioRegisters<'static> {
&mut self.regs
}
}
/// Reset the SDIO peripheral using the SLCR reset register for SDIO.
///
/// Please note that this function will interfere with an already configured
/// UART instance.
/// SDIO instance.
#[inline]
pub fn reset(id: SdioId, cycles: u32) {
let assert_reset = match id {

View File

@@ -431,13 +431,27 @@ pub struct Registers {
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 fn new_mmio_fixed_0() -> MmioRegisters<'static> {
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 fn new_mmio_fixed_1() -> MmioRegisters<'static> {
pub const unsafe fn new_mmio_fixed_1() -> MmioRegisters<'static> {
unsafe { Self::new_mmio_at(SDIO_BASE_ADDR_1) }
}
}