From 47b150369d65ab88efe1a7e5c2a8b05d027de1bf Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 28 May 2025 01:03:03 +0200 Subject: [PATCH] continue --- zynq7000-hal/src/eth/mod.rs | 252 +++++++++++++++++++++++++++++++++--- zynq7000/src/eth.rs | 18 +++ zynq7000/src/slcr/reset.rs | 19 ++- 3 files changed, 269 insertions(+), 20 deletions(-) diff --git a/zynq7000-hal/src/eth/mod.rs b/zynq7000-hal/src/eth/mod.rs index 3de610e..4667112 100644 --- a/zynq7000-hal/src/eth/mod.rs +++ b/zynq7000-hal/src/eth/mod.rs @@ -1,14 +1,27 @@ -use arbitrary_int::{u2, u3, u13, u30}; -use zynq7000::eth::{GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, MmioEthernet}; +use arbitrary_int::{Number, u2, u3, u6, u13, u30}; +use zynq7000::{ + eth::{ + GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, InterruptControl, MmioEthernet, NetworkControl, RxStatus, + SpeedMode, TxStatus, + }, + slcr::reset::EthernetReset, +}; #[cfg(not(feature = "7z010-7z007s-clg225"))] -use crate::gpio::mio::{Mio28, Mio29, Mio30, Mio31, Mio32, Mio33}; -use crate::gpio::{ - IoPeriphPin, - mio::{ - Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, Mio34, - Mio35, Mio36, Mio37, Mio38, Mio39, Mio52, Mio53, MioPinMarker, MuxConf, Pin, +use crate::gpio::mio::{ + Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, +}; +use crate::{ + enable_amba_peripheral_clock, + gpio::{ + IoPeriphPin, + mio::{ + Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, + Mio52, Mio53, MioPinMarker, MuxConf, Pin, + }, }, + slcr::Slcr, + time::Hertz, }; pub const MUX_CONF_PHY: MuxConf = MuxConf::new_with_l0(); @@ -172,22 +185,191 @@ impl RxData3 for Pin { const ETH_ID: EthernetId = EthernetId::Eth1; } -pub struct EthernetLowLevel(zynq7000::eth::MmioEthernet<'static>); +pub struct EthernetLowLevel { + id: EthernetId, + pub regs: zynq7000::eth::MmioEthernet<'static>, +} +#[derive(Debug, Clone, Copy)] +pub struct ClkConfig { + pub src_sel: zynq7000::slcr::clocks::SrcSelIo, + pub use_emio_tx_clk: bool, + pub divisor_0: u6, + pub divisor_1: u6, + /// Enable the clock. + pub enable: bool, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Speed { + Mbps10, + Mbps100, + Mbps1000, +} + +impl Speed { + pub const fn rgmii_clk_rate(&self) -> Hertz { + match self { + Speed::Mbps10 => Hertz::from_raw(2_500_000), + Speed::Mbps100 => Hertz::from_raw(25_000_000), + Speed::Mbps1000 => Hertz::from_raw(125_000_000), + } + } +} + +impl ClkConfig { + pub const fn new(divisor_0: u6, divisor_1: u6) -> Self { + Self { + src_sel: zynq7000::slcr::clocks::SrcSelIo::IoPll, + use_emio_tx_clk: false, + divisor_0, + divisor_1, + enable: true, + } + } + + /// Calculate the best clock configuration (divisors) for the given reference clock + /// and desired target speed when using a RGMII interface. + /// + /// Usually, the reference clock will be the IO clock. + /// + /// Returns a tuple where the first entry is the calcualted clock configuration + /// and the second entry is the difference between calculated clock speed for the divisors + /// and the target speed. Ideally, this difference should be 0. + pub fn calculate_for_rgmii(ref_clk: Hertz, target_speed: Speed) -> (Self, u32) { + let mut smallest_diff = u32::MAX; + let target_speed = target_speed.rgmii_clk_rate(); + let mut best_div_0 = u6::new(0); + let mut best_div_1 = u6::new(0); + for div_0 in 0..=u6::MAX.as_usize() { + for div_1 in 0..=u6::MAX.as_usize() { + let clk_rate = ref_clk.raw() / div_0 as u32 / div_1 as u32; + let diff = (target_speed.raw() as i64 - clk_rate as i64).unsigned_abs() as u32; + if diff < smallest_diff { + smallest_diff = diff; + best_div_0 = u6::new(div_0 as u8); + best_div_1 = u6::new(div_1 as u8); + } + // We found a perfect match. No need to continue. + if diff == 0 { + break; + } + } + } + (Self::new(best_div_0, best_div_1), smallest_diff) + } +} + +/// Ethernet low-level interface. impl EthernetLowLevel { /// Creates a new instance of the Ethernet low-level interface. - pub fn new(eth: zynq7000::eth::MmioEthernet<'static>) -> Self { - EthernetLowLevel(eth) + #[inline] + pub fn new(regs: zynq7000::eth::MmioEthernet<'static>) -> Option { + regs.id()?; + Some(EthernetLowLevel { + id: regs.id().unwrap(), + regs, + }) } - /// Returns a reference to the underlying MMIO Ethernet interface. - pub fn regs(&self) -> &zynq7000::eth::MmioEthernet<'static> { - &self.0 + /// Create a low-level instance for the given [EthernetId]. + /// + /// # Safety + /// + /// Circumvents ownership and safety guarantees of the HAL. + #[inline] + pub const unsafe fn steal(id: EthernetId) -> Self { + Self { + id, + regs: unsafe { + match id { + EthernetId::Eth0 => zynq7000::eth::Ethernet::new_mmio_fixed_0(), + EthernetId::Eth1 => zynq7000::eth::Ethernet::new_mmio_fixed_1(), + } + }, + } } - /// Returns a mutable reference to the underlying MMIO Ethernet interface. - pub fn regs_mut(&mut self) -> &mut zynq7000::eth::MmioEthernet<'static> { - &mut self.0 + 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 { + cortex_ar::asm::nop(); + } + regs.reset_ctrl().write_eth(EthernetReset::DEFAULT); + }); + } + } + + #[inline] + pub fn enable_peripheral_clock(&mut self) { + let periph_sel = match self.id { + EthernetId::Eth0 => crate::PeripheralSelect::Gem0, + EthernetId::Eth1 => crate::PeripheralSelect::Gem1, + }; + enable_amba_peripheral_clock(periph_sel); + } + + #[inline] + pub fn configure_clock(&mut self, cfg: ClkConfig) { + unsafe { + Slcr::with(|regs| { + regs.clk_ctrl().modify_gem_0_clk_ctrl(|mut val| { + val.set_srcsel(cfg.src_sel); + val.set_divisor_0(cfg.divisor_0); + val.set_divisor_1(cfg.divisor_1); + val.set_use_emio_tx_clk(cfg.use_emio_tx_clk); + val.set_clk_act(cfg.enable); + val + }); + }) + } + } + + #[inline] + pub fn configure_mdc_clk_div() { + // TODO: + } + + /// Performs initialization according to TRM p.541. + /// + /// These steps do not include any resets or clock configuration. + pub fn initialize(&mut self) { + let mut ctrl_val = NetworkControl::new_with_raw_value(0); + self.regs.write_net_ctrl(ctrl_val); + // Now clear statistics. + ctrl_val.set_clear_statistics(true); + self.regs.write_net_ctrl(ctrl_val); + self.regs.write_tx_status(TxStatus::new_clear_all()); + self.regs.write_rx_status(RxStatus::new_clear_all()); + self.regs + .write_interrupt_disable(InterruptControl::new_clear_all()); + self.regs.write_rx_buf_queue_base_addr(0); + self.regs.write_tx_buf_queue_base_addr(0); + } + + #[inline] + pub const fn id(&self) -> EthernetId { + self.id } } @@ -213,7 +395,8 @@ impl Ethernet { MdClkPin: MdClk, MdIoPin: MdIo, >( - ll: EthernetLowLevel, + mut ll: EthernetLowLevel, + clk_config: ClkConfig, tx_clk: TxClkPin, tx_ctrl: TxCtrlPin, tx_data: (TxData0Pin, TxData1Pin, TxData2Pin, TxData3Pin), @@ -222,6 +405,7 @@ impl Ethernet { rx_data: (RxData0Pin, RxData1Pin, RxData2Pin, RxData3Pin), md_pins: Option<(MdClkPin, MdIoPin)>, ) -> Self { + Self::common_init(&mut ll); let tx_mio_config = zynq7000::slcr::mio::Config::builder() .with_disable_hstl_rcvr(true) .with_pullup(true) @@ -337,12 +521,42 @@ impl Ethernet { }); }); } + ll.configure_clock(clk_config); Ethernet { ll } } - pub fn new(ll: EthernetLowLevel) -> Self { + pub fn new(mut ll: EthernetLowLevel, clk_config: ClkConfig) -> Self { + Self::common_init(&mut ll); + ll.configure_clock(clk_config); Ethernet { ll } } + + fn common_init(ll: &mut EthernetLowLevel) { + ll.enable_peripheral_clock(); + ll.reset(3); + ll.initialize(); + ll.regs.modify_net_cfg(|mut net_cfg| { + net_cfg.set_full_duplex(true); + net_cfg.set_gigabit_enable(true); + net_cfg.set_speed_mode(SpeedMode::High100Mbps); + net_cfg + }); + } + + #[inline] + pub fn ll(&mut self) -> &mut EthernetLowLevel { + &mut self.ll + } + + #[inline] + pub fn regs(&mut self) -> &MmioEthernet<'static> { + &self.ll.regs + } + + #[inline] + pub fn regs_mut(&mut self) -> &mut MmioEthernet<'static> { + &mut self.ll.regs + } } /// RX buffer descriptor. /// diff --git a/zynq7000/src/eth.rs b/zynq7000/src/eth.rs index 1d4c3f1..afe29cf 100644 --- a/zynq7000/src/eth.rs +++ b/zynq7000/src/eth.rs @@ -263,6 +263,12 @@ pub struct TxStatus { read_when_used: bool, } +impl TxStatus { + pub fn new_clear_all() -> Self { + Self::new_with_raw_value(0xFF) + } +} + #[bitbybit::bitfield(u32)] #[derive(Debug)] pub struct RxStatus { @@ -276,6 +282,12 @@ pub struct RxStatus { buf_not_available: bool, } +impl RxStatus { + pub fn new_clear_all() -> Self { + Self::new_with_raw_value(0xF) + } +} + #[bitbybit::bitfield(u32)] #[derive(Debug)] pub struct InterruptStatus { @@ -362,6 +374,12 @@ pub struct InterruptControl { mgmt_frame_sent: bool, } +impl InterruptControl { + pub fn new_clear_all() -> Self { + Self::new_with_raw_value(0xFFFF_FFFF) + } +} + #[bitbybit::bitenum(u2, exhaustive = false)] pub enum PhyOperation { Read = 0b10, diff --git a/zynq7000/src/slcr/reset.rs b/zynq7000/src/slcr/reset.rs index d427994..286ed23 100644 --- a/zynq7000/src/slcr/reset.rs +++ b/zynq7000/src/slcr/reset.rs @@ -35,6 +35,23 @@ pub struct GpioClockReset { gpio_cpu1x_rst: bool, } +#[bitbybit::bitfield(u32, default = 0x0)] +#[derive(Debug)] +pub struct EthernetReset { + #[bit(5, rw)] + gem1_ref_rst: bool, + #[bit(4, rw)] + gem0_ref_rst: bool, + #[bit(3, rw)] + gem1_rx_rst: bool, + #[bit(2, rw)] + gem0_rx_rst: bool, + #[bit(1, rw)] + gem1_cpu1x_rst: bool, + #[bit(0, rw)] + gem0_cpu1x_rst: bool, +} + #[derive(derive_mmio::Mmio)] #[repr(C)] pub struct ResetControl { @@ -45,7 +62,7 @@ pub struct ResetControl { topsw: u32, dmac: u32, usb: u32, - gem: u32, + eth: EthernetReset, sdio: DualRefAndClockReset, spi: DualRefAndClockReset, can: DualClockReset,