From 9fd048c2a25fddc9b84dd18e1a0c3dedb1c8396e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 27 Oct 2025 01:05:49 +0100 Subject: [PATCH] continue with SDIO --- zynq/zynq7000-hal/src/eth/ll.rs | 5 +- zynq/zynq7000-hal/src/sdio.rs | 236 +++++++++++++++++++++++++++----- zynq/zynq7000/src/sdio.rs | 18 ++- 3 files changed, 220 insertions(+), 39 deletions(-) diff --git a/zynq/zynq7000-hal/src/eth/ll.rs b/zynq/zynq7000-hal/src/eth/ll.rs index 1f57bf1..ee00c66 100644 --- a/zynq/zynq7000-hal/src/eth/ll.rs +++ b/zynq/zynq7000-hal/src/eth/ll.rs @@ -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) } diff --git a/zynq/zynq7000-hal/src/sdio.rs b/zynq/zynq7000-hal/src/sdio.rs index a861df1..e4422fd 100644 --- a/zynq/zynq7000-hal/src/sdio.rs +++ b/zynq/zynq7000-hal/src/sdio.rs @@ -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 {} @@ -61,12 +67,12 @@ impl Sdio0Data3Pin for Pin {} #[cfg(not(feature = "7z010-7z007s-clg225"))] impl Sdio0Data3Pin for Pin {} -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 {} #[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; +} + +impl SdioRegisters for zynq7000::sdio::MmioRegisters<'static> { + #[inline] + fn reg_block(&self) -> zynq7000::sdio::MmioRegisters<'static> { + unsafe { self.clone() } + } + + #[inline] + fn id(&self) -> Option { + 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 { + 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 { + 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 { + 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 { diff --git a/zynq/zynq7000/src/sdio.rs b/zynq/zynq7000/src/sdio.rs index f124ce7..f278f3b 100644 --- a/zynq/zynq7000/src/sdio.rs +++ b/zynq/zynq7000/src/sdio.rs @@ -431,13 +431,27 @@ pub struct Registers { static_assertions::const_assert_eq!(core::mem::size_of::(), 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) } } }