continue 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-11-03 11:46:30 +01:00
parent 848e2113a0
commit 337b00a442
3 changed files with 140 additions and 49 deletions

View File

@@ -8,11 +8,6 @@ use crate::{clocks::IoClocks, enable_amba_peripheral_clock, slcr::Slcr, time::He
use super::{EthernetId, PsEthernet as _};
pub struct EthernetLowLevel {
id: EthernetId,
pub regs: zynq7000::eth::MmioRegisters<'static>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Speed {
Mbps10,
@@ -177,8 +172,17 @@ impl ClockDivSet {
/// Ethernet low-level interface.
///
/// 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 {
/// 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]
pub fn new(regs: zynq7000::eth::MmioRegisters<'static>) -> Option<Self> {
regs.id()?;
@@ -207,33 +211,7 @@ impl EthernetLowLevel {
}
pub fn reset(&mut self, cycles: usize) {
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);
});
}
reset(self.id, cycles);
}
#[inline]
@@ -386,3 +364,34 @@ impl EthernetLowLevel {
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);
});
}
}

View File

@@ -470,7 +470,7 @@ impl Ethernet {
});
});
}
ll.configure_clock(config.clk_config_1000_mbps, true);
ll.configure_peripheral_clock(config.clk_config_1000_mbps, true);
let mut mdio = mdio::Mdio::new(&ll, true);
mdio.configure_clock_div(config.mdc_clk_div);
ll.regs.modify_net_ctrl(|mut val| {
@@ -491,7 +491,7 @@ impl Ethernet {
pub fn new(mut ll: EthernetLowLevel, config: EthernetConfig) -> Self {
Self::common_init(&mut ll, config.mac_address);
ll.configure_clock(config.clk_config_1000_mbps, true);
ll.configure_peripheral_clock(config.clk_config_1000_mbps, true);
let mut mdio = mdio::Mdio::new(&ll, true);
mdio.configure_clock_div(config.mdc_clk_div);
Ethernet {

View File

@@ -240,9 +240,91 @@ impl SdioClockConfig {
})
}
}
/// 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 })
}
/// 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_clock(&mut self) {
self.regs.modify_clock_timeout_sw_reset_control(|mut val| {
val.set_sd_clock_enable(true);
val
});
}
#[inline]
pub fn disable_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 {
regs: zynq7000::sdio::MmioRegisters<'static>,
ll: SdioLowLevel,
}
impl Sdio {
@@ -265,7 +347,6 @@ impl Sdio {
return None;
}
Some(Self::new(
id,
regs,
clock_config,
clock_pin,
@@ -293,7 +374,6 @@ impl Sdio {
return None;
}
Some(Self::new(
id,
regs,
clock_config,
clock_pin,
@@ -303,37 +383,39 @@ impl Sdio {
}
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 {
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 { regs }
Self { ll }
}
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
fn initialize(ll: &mut SdioLowLevel, clock_config: &SdioClockConfig) {
ll.reset(10);
// TODO: SW reset for all?
// TODO: Internal clock?
ll.disable_clock();
ll.configure_clock(clock_config);
ll.enable_clock();
// TODO: There is probably some other configuration necessary.. the docs really are not
// complete here..
unsafe {}
}
#[inline]
pub fn regs(&mut self) -> &mut zynq7000::sdio::MmioRegisters<'static> {
&mut self.regs
&mut self.ll.regs
}
}
@@ -362,7 +444,7 @@ pub fn reset(id: SdioId, cycles: u32) {
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 {
cortex_ar::asm::nop();
aarch32_cpu::asm::nop();
}
regs.reset_ctrl().write_sdio(DualRefAndClockReset::DEFAULT);
});