diff --git a/zynq7000-hal/src/eth/mod.rs b/zynq7000-hal/src/eth/mod.rs index 2be43a6..3de610e 100644 --- a/zynq7000-hal/src/eth/mod.rs +++ b/zynq7000-hal/src/eth/mod.rs @@ -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; +} + +impl PsEthernet for MmioEthernet<'static> { + #[inline] + fn reg_block(&self) -> MmioEthernet<'static> { + unsafe { self.clone() } + } + + #[inline] + fn id(&self) -> Option { + 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 {} +impl MdIo for Pin {} + +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl TxClk for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl TxCtrl for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl TxData0 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl TxData1 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl TxData2 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl TxData3 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl RxClk for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl RxCtrl for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl RxData0 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl RxData1 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl RxData2 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} +#[cfg(not(feature = "7z010-7z007s-clg225"))] +impl RxData3 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth0; +} + +impl TxClk for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl TxCtrl for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl TxData0 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl TxData1 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl TxData2 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl TxData3 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl RxClk for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl RxCtrl for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl RxData0 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl RxData1 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl RxData2 for Pin { + const ETH_ID: EthernetId = EthernetId::Eth1; +} +impl RxData3 for Pin { + 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. diff --git a/zynq7000-hal/src/gpio/ll.rs b/zynq7000-hal/src/gpio/ll.rs index 5a85275..9313681 100644 --- a/zynq7000-hal/src/gpio/ll.rs +++ b/zynq7000-hal/src/gpio/ll.rs @@ -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(); diff --git a/zynq7000-hal/src/gpio/mio.rs b/zynq7000-hal/src/gpio/mio.rs index 3ce38f2..ca6fc59 100644 --- a/zynq7000-hal/src/gpio/mio.rs +++ b/zynq7000-hal/src/gpio/mio.rs @@ -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) } diff --git a/zynq7000-hal/src/gpio/mod.rs b/zynq7000-hal/src/gpio/mod.rs index fca11cc..29afee9 100644 --- a/zynq7000-hal/src/gpio/mod.rs +++ b/zynq7000-hal/src/gpio/mod.rs @@ -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) -> 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 { diff --git a/zynq7000-hal/src/slcr.rs b/zynq7000-hal/src/slcr.rs index 778e1f4..1c710ea 100644 --- a/zynq7000-hal/src/slcr.rs +++ b/zynq7000-hal/src/slcr.rs @@ -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(mut f: F) { + pub unsafe fn with)>(f: F) { let mut slcr = unsafe { zynq7000::slcr::Slcr::new_mmio_fixed() }; slcr.write_unlock(UNLOCK_KEY); f(&mut slcr); diff --git a/zynq7000/src/slcr/mio.rs b/zynq7000/src/slcr/mio.rs index 7236019..f37ec10 100644 --- a/zynq7000/src/slcr/mio.rs +++ b/zynq7000/src/slcr/mio.rs @@ -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)] diff --git a/zynq7000/src/slcr/mod.rs b/zynq7000/src/slcr/mod.rs index c6b648e..d6d9bab 100644 --- a/zynq7000/src/slcr/mod.rs +++ b/zynq7000/src/slcr/mod.rs @@ -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::(), 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, + #[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,