continue
This commit is contained in:
parent
8c9e34f96a
commit
47b150369d
@ -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::{
|
||||
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::{
|
||||
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, Mio34,
|
||||
Mio35, Mio36, Mio37, Mio38, Mio39, Mio52, Mio53, MioPinMarker, MuxConf, Pin,
|
||||
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<Mio38> {
|
||||
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<Self> {
|
||||
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.
|
||||
///
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user