continue eth support

This commit is contained in:
Robin Müller 2025-05-27 16:05:43 +02:00
parent b8fdf1008b
commit 8c9e34f96a
Signed by: muellerr
GPG Key ID: A649FB78196E3849
7 changed files with 430 additions and 8 deletions

View File

@ -1,5 +1,349 @@
use arbitrary_int::{u2, u3, u13, u30};
use zynq7000::eth::{GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, MmioEthernet};
#[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,
},
};
pub const MUX_CONF_PHY: MuxConf = MuxConf::new_with_l0();
pub const MUX_CONF_MDIO: MuxConf = MuxConf::new_with_l3(u3::new(0b100));
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EthernetId {
Eth0 = 0,
Eth1 = 1,
}
pub trait PsEthernet {
fn reg_block(&self) -> MmioEthernet<'static>;
fn id(&self) -> Option<EthernetId>;
}
impl PsEthernet for MmioEthernet<'static> {
#[inline]
fn reg_block(&self) -> MmioEthernet<'static> {
unsafe { self.clone() }
}
#[inline]
fn id(&self) -> Option<EthernetId> {
let base_addr = unsafe { self.ptr() } as usize;
if base_addr == GEM_0_BASE_ADDR {
return Some(EthernetId::Eth0);
} else if base_addr == GEM_1_BASE_ADDR {
return Some(EthernetId::Eth1);
}
None
}
}
pub trait TxClk: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxCtrl: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxData0: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxData1: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxData2: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait TxData3: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxClk: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxCtrl: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxData0: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxData1: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxData2: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait RxData3: MioPinMarker {
const ETH_ID: EthernetId;
}
pub trait MdClk: MioPinMarker {}
pub trait MdIo: MioPinMarker {}
impl MdClk for Pin<Mio52> {}
impl MdIo for Pin<Mio53> {}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxClk for Pin<Mio16> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxCtrl for Pin<Mio21> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData0 for Pin<Mio17> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData1 for Pin<Mio18> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData2 for Pin<Mio19> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl TxData3 for Pin<Mio20> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxClk for Pin<Mio22> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxCtrl for Pin<Mio27> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData0 for Pin<Mio23> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData1 for Pin<Mio24> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData2 for Pin<Mio25> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
#[cfg(not(feature = "7z010-7z007s-clg225"))]
impl RxData3 for Pin<Mio26> {
const ETH_ID: EthernetId = EthernetId::Eth0;
}
impl TxClk for Pin<Mio28> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxCtrl for Pin<Mio33> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData0 for Pin<Mio29> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData1 for Pin<Mio30> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData2 for Pin<Mio31> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl TxData3 for Pin<Mio32> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxClk for Pin<Mio34> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxCtrl for Pin<Mio39> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData0 for Pin<Mio35> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData1 for Pin<Mio36> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData2 for Pin<Mio37> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
impl RxData3 for Pin<Mio38> {
const ETH_ID: EthernetId = EthernetId::Eth1;
}
pub struct EthernetLowLevel(zynq7000::eth::MmioEthernet<'static>);
impl EthernetLowLevel {
/// Creates a new instance of the Ethernet low-level interface.
pub fn new(eth: zynq7000::eth::MmioEthernet<'static>) -> Self {
EthernetLowLevel(eth)
}
/// Returns a reference to the underlying MMIO Ethernet interface.
pub fn regs(&self) -> &zynq7000::eth::MmioEthernet<'static> {
&self.0
}
/// 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 struct Ethernet {
ll: EthernetLowLevel,
}
impl Ethernet {
#[allow(clippy::too_many_arguments)]
pub fn new_with_mio<
TxClkPin: TxClk,
TxCtrlPin: TxCtrl,
TxData0Pin: TxData0,
TxData1Pin: TxData1,
TxData2Pin: TxData2,
TxData3Pin: TxData3,
RxClkPin: RxClk,
RxCtrlPin: RxCtrl,
RxData0Pin: RxData0,
RxData1Pin: RxData1,
RxData2Pin: RxData2,
RxData3Pin: RxData3,
MdClkPin: MdClk,
MdIoPin: MdIo,
>(
ll: EthernetLowLevel,
tx_clk: TxClkPin,
tx_ctrl: TxCtrlPin,
tx_data: (TxData0Pin, TxData1Pin, TxData2Pin, TxData3Pin),
rx_clk: RxClkPin,
rx_ctrl: RxCtrlPin,
rx_data: (RxData0Pin, RxData1Pin, RxData2Pin, RxData3Pin),
md_pins: Option<(MdClkPin, MdIoPin)>,
) -> Self {
let tx_mio_config = zynq7000::slcr::mio::Config::builder()
.with_disable_hstl_rcvr(true)
.with_pullup(true)
.with_io_type(zynq7000::slcr::mio::IoType::Hstl)
.with_speed(zynq7000::slcr::mio::Speed::FastCmosEdge)
.with_l3_sel(MUX_CONF_PHY.l3_sel())
.with_l2_sel(MUX_CONF_PHY.l2_sel())
.with_l1_sel(MUX_CONF_PHY.l1_sel())
.with_l0_sel(MUX_CONF_PHY.l0_sel())
.with_tri_enable(false)
.build();
let rx_mio_config = zynq7000::slcr::mio::Config::builder()
.with_disable_hstl_rcvr(false)
.with_pullup(true)
.with_io_type(zynq7000::slcr::mio::IoType::Hstl)
.with_speed(zynq7000::slcr::mio::Speed::FastCmosEdge)
.with_l3_sel(MUX_CONF_PHY.l3_sel())
.with_l2_sel(MUX_CONF_PHY.l2_sel())
.with_l1_sel(MUX_CONF_PHY.l1_sel())
.with_l0_sel(MUX_CONF_PHY.l0_sel())
// Disable output driver.
.with_tri_enable(true)
.build();
unsafe {
crate::slcr::Slcr::with(|slcr_mut| {
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_clk,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_ctrl,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_data.0,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_data.1,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_data.2,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
tx_data.3,
slcr_mut,
tx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_clk,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_ctrl,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_data.0,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_data.1,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_data.2,
slcr_mut,
rx_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
rx_data.3,
slcr_mut,
rx_mio_config,
);
if let Some((md_clk, md_io)) = md_pins {
let md_mio_config = zynq7000::slcr::mio::Config::builder()
.with_disable_hstl_rcvr(false)
.with_pullup(true)
.with_io_type(zynq7000::slcr::mio::IoType::LvCmos18)
.with_speed(zynq7000::slcr::mio::Speed::SlowCmosEdge)
.with_l3_sel(MUX_CONF_MDIO.l3_sel())
.with_l2_sel(MUX_CONF_MDIO.l2_sel())
.with_l1_sel(MUX_CONF_MDIO.l1_sel())
.with_l0_sel(MUX_CONF_MDIO.l0_sel())
.with_tri_enable(false)
.build();
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
md_clk,
slcr_mut,
md_mio_config,
);
IoPeriphPin::new_with_full_config_and_unlocked_slcr(
md_io,
slcr_mut,
md_mio_config,
);
}
// Enable VREF internal generator, which is required for HSTL pin mode.
slcr_mut.gpiob().modify_ctrl(|mut ctrl| {
ctrl.set_vref_en(true);
ctrl
});
});
}
Ethernet { ll }
}
pub fn new(ll: EthernetLowLevel) -> Self {
Ethernet { ll }
}
}
/// RX buffer descriptor.
///
/// The user should declare an array of this structure inside uncached memory.

View File

@ -148,6 +148,23 @@ impl LowLevelGpio {
self.reconfigure_slcr_mio_cfg(false, pullup, Some(mux_conf));
}
pub fn set_mio_pin_config(&mut self, config: zynq7000::slcr::mio::Config) {
let raw_offset = self.offset.offset();
// Safety: We only modify the MIO config of the pin.
let mut slcr_wrapper = unsafe { Slcr::steal() };
slcr_wrapper.modify(|mut_slcr| mut_slcr.write_mio_pins(raw_offset, config).unwrap());
}
/// Set the MIO pin configuration with an unlocked SLCR.
pub fn set_mio_pin_config_with_unlocked_slcr(
&mut self,
slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
config: zynq7000::slcr::mio::Config,
) {
let raw_offset = self.offset.offset();
slcr.write_mio_pins(raw_offset, config).unwrap();
}
#[inline]
pub fn is_low(&self) -> bool {
let (offset, in_reg) = self.get_data_in_reg_and_local_offset();

View File

@ -31,6 +31,10 @@ impl MuxConf {
Self { l3, l2, l1, l0 }
}
pub const fn new_with_l0() -> Self {
Self::new(true, false, u2::new(0b00), u3::new(0b000))
}
pub const fn new_with_l3(l3: u3) -> Self {
Self::new(false, false, u2::new(0b00), l3)
}

View File

@ -384,6 +384,8 @@ pub struct IoPeriphPin {
}
impl IoPeriphPin {
/// Constructor for IO peripheral pins where only the multiplexer and pullup configuration
/// need to be changed.
pub fn new(pin: impl MioPinMarker, mux_conf: MuxConf, pullup: Option<bool>) -> Self {
let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
low_level.configure_as_io_periph_pin(mux_conf, pullup);
@ -392,6 +394,43 @@ impl IoPeriphPin {
mux_conf,
}
}
/// Constructor to fully configure an IO peripheral pin with a specific MIO pin configuration.
pub fn new_with_full_config(
pin: impl MioPinMarker,
config: zynq7000::slcr::mio::Config,
) -> Self {
let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
low_level.set_mio_pin_config(config);
Self {
pin: low_level,
mux_conf: MuxConf::new(
config.l0_sel(),
config.l1_sel(),
config.l2_sel(),
config.l3_sel(),
),
}
}
/// Constructor to fully configure an IO peripheral pin with a specific MIO pin configuration.
pub fn new_with_full_config_and_unlocked_slcr(
pin: impl MioPinMarker,
slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
config: zynq7000::slcr::mio::Config,
) -> Self {
let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
low_level.set_mio_pin_config_with_unlocked_slcr(slcr, config);
Self {
pin: low_level,
mux_conf: MuxConf::new(
config.l0_sel(),
config.l1_sel(),
config.l2_sel(),
config.l3_sel(),
),
}
}
}
impl IoPinProvider for IoPeriphPin {

View File

@ -14,7 +14,7 @@ impl Slcr {
/// This method unsafely steals the SLCR MMIO block and then calls a user provided function
/// with the [SLCR MMIO][MmioSlcr] block as an input argument. It is the user's responsibility
/// that the SLCR is not used concurrently in a way which leads to data races.
pub unsafe fn with<F: FnMut(&mut MmioSlcr)>(mut f: F) {
pub unsafe fn with<F: FnOnce(&mut MmioSlcr<'static>)>(f: F) {
let mut slcr = unsafe { zynq7000::slcr::Slcr::new_mmio_fixed() };
slcr.write_unlock(UNLOCK_KEY);
f(&mut slcr);

View File

@ -17,7 +17,7 @@ pub enum IoType {
Hstl = 0b100,
}
#[bitbybit::bitfield(u32)]
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Config {
#[bit(13, rw)]

View File

@ -1,7 +1,7 @@
//! System Level Control Registers (slcr)
//!
//! Writing any of these registers required unlocking the SLCR first.
use arbitrary_int::u4;
use arbitrary_int::{u3, u4};
pub use clocks::{ClockControl, MmioClockControl};
pub use reset::{MmioResetControl, ResetControl};
@ -50,10 +50,27 @@ impl DdrIoB {
static_assertions::const_assert_eq!(core::mem::size_of::<DdrIoB>(), 0x38);
#[bitbybit::bitenum(u3, exhaustive = false)]
pub enum VrefSel {
Disabled = 0b000,
Vref0_9V = 0b001,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct GpiobControl {
#[bit(11, rw)]
vref_sw_en: bool,
#[bits(4..=6, rw)]
vref_sel: Option<VrefSel>,
#[bit(0, rw)]
vref_en: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct GpiobCtrl {
ctrl: u32,
pub struct GpiobRegisters {
ctrl: GpiobControl,
cfg_cmos18: u32,
cfg_cmos25: u32,
cfg_cmos33: u32,
@ -62,7 +79,7 @@ pub struct GpiobCtrl {
drvr_bias_ctrl: u32,
}
impl GpiobCtrl {
impl GpiobRegisters {
/// Create a new handle to this peripheral.
///
/// Writing to this register requires unlocking the SLCR registers first.
@ -71,12 +88,13 @@ impl GpiobCtrl {
///
/// If you create multiple instances of this handle at the same time, you are responsible for
/// ensuring that there are no read-modify-write races on any of the registers.
pub unsafe fn new_mmio_fixed() -> MmioGpiobCtrl<'static> {
pub unsafe fn new_mmio_fixed() -> MmioGpiobRegisters<'static> {
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + GPIOB_OFFSET) }
}
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct BootModeRegister {
#[bit(4, r)]
pll_bypass: bool,
@ -183,7 +201,7 @@ pub struct Slcr {
_gap18: [u32; 0x09],
#[mmio(inner)]
gpiob: GpiobCtrl,
gpiob: GpiobRegisters,
#[mmio(inner)]
ddriob: DdrIoB,