continue
This commit is contained in:
parent
47b150369d
commit
42335ab8c1
197
zynq7000-hal/src/eth/ll.rs
Normal file
197
zynq7000-hal/src/eth/ll.rs
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
use arbitrary_int::{Number, u6};
|
||||||
|
use zynq7000::{
|
||||||
|
eth::{InterruptControl, NetworkControl, RxStatus, TxStatus},
|
||||||
|
slcr::reset::EthernetReset,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{enable_amba_peripheral_clock, slcr::Slcr, time::Hertz};
|
||||||
|
|
||||||
|
use super::{EthernetId, PsEthernet as _};
|
||||||
|
|
||||||
|
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.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(regs: zynq7000::eth::MmioEthernet<'static>) -> Option<Self> {
|
||||||
|
regs.id()?;
|
||||||
|
Some(EthernetLowLevel {
|
||||||
|
id: regs.id().unwrap(),
|
||||||
|
regs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
65
zynq7000-hal/src/eth/mdio.rs
Normal file
65
zynq7000-hal/src/eth/mdio.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
use arbitrary_int::{u2, u5};
|
||||||
|
use zynq7000::eth::{MdcClkDiv, PhyMaintenance};
|
||||||
|
|
||||||
|
use super::{EthernetId, ll::EthernetLowLevel};
|
||||||
|
|
||||||
|
pub struct Mdio {
|
||||||
|
regs: zynq7000::eth::MmioEthernet<'static>,
|
||||||
|
clause22: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mdio {
|
||||||
|
pub fn new(ll: &EthernetLowLevel, clause22: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
regs: unsafe { ll.regs.clone() },
|
||||||
|
clause22,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Circumvents ownership and safety guarantees of the HAL.
|
||||||
|
pub unsafe fn steal(eth_id: EthernetId, clause22: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
regs: unsafe { eth_id.steal_regs() },
|
||||||
|
clause22,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn configure_clock_div(&mut self, clk_div: MdcClkDiv) {
|
||||||
|
self.regs.modify_net_cfg(|mut val| {
|
||||||
|
val.set_mdc_clk_div(clk_div);
|
||||||
|
val
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_blocking(&mut self, phy_addr: u5, reg_addr: u5) -> u16 {
|
||||||
|
self.regs.write_phy_maintenance(
|
||||||
|
PhyMaintenance::builder()
|
||||||
|
.with_clause_22(self.clause22)
|
||||||
|
.with_op(zynq7000::eth::PhyOperation::Read)
|
||||||
|
.with_phy_addr(phy_addr)
|
||||||
|
.with_reg_addr(reg_addr)
|
||||||
|
.with_must_be_0b10(u2::new(0b10))
|
||||||
|
.with_data(0x0000)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
while !self.regs.read_net_status().phy_mgmt_idle() {}
|
||||||
|
self.regs.read_phy_maintenance().data()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_blocking(&mut self, phy_addr: u5, reg_addr: u5, data: u16) {
|
||||||
|
self.regs.write_phy_maintenance(
|
||||||
|
PhyMaintenance::builder()
|
||||||
|
.with_clause_22(self.clause22)
|
||||||
|
.with_op(zynq7000::eth::PhyOperation::Write)
|
||||||
|
.with_phy_addr(phy_addr)
|
||||||
|
.with_reg_addr(reg_addr)
|
||||||
|
.with_must_be_0b10(u2::new(0b10))
|
||||||
|
.with_data(data)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
while !self.regs.read_net_status().phy_mgmt_idle() {}
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,24 @@
|
|||||||
use arbitrary_int::{Number, u2, u3, u6, u13, u30};
|
use arbitrary_int::u3;
|
||||||
use zynq7000::{
|
pub use zynq7000::eth::MdcClkDiv;
|
||||||
eth::{
|
use zynq7000::eth::{GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, MmioEthernet, SpeedMode};
|
||||||
GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, InterruptControl, MmioEthernet, NetworkControl, RxStatus,
|
|
||||||
SpeedMode, TxStatus,
|
pub use ll::{ClkConfig, EthernetLowLevel};
|
||||||
},
|
|
||||||
slcr::reset::EthernetReset,
|
pub mod ll;
|
||||||
};
|
pub mod mdio;
|
||||||
|
pub mod rx_descr;
|
||||||
|
pub mod tx_descr;
|
||||||
|
|
||||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
use crate::gpio::mio::{
|
use crate::gpio::mio::{
|
||||||
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27,
|
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::gpio::{
|
||||||
enable_amba_peripheral_clock,
|
IoPeriphPin,
|
||||||
gpio::{
|
mio::{
|
||||||
IoPeriphPin,
|
Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio52,
|
||||||
mio::{
|
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();
|
pub const MUX_CONF_PHY: MuxConf = MuxConf::new_with_l0();
|
||||||
@ -33,6 +30,22 @@ pub enum EthernetId {
|
|||||||
Eth1 = 1,
|
Eth1 = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EthernetId {
|
||||||
|
/// Steal the ethernet register block for the given ethernet ID.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Circumvents ownership and safety guarantees of the HAL.
|
||||||
|
pub const unsafe fn steal_regs(&self) -> zynq7000::eth::MmioEthernet<'static> {
|
||||||
|
unsafe {
|
||||||
|
match self {
|
||||||
|
EthernetId::Eth0 => zynq7000::eth::Ethernet::new_mmio_fixed_0(),
|
||||||
|
EthernetId::Eth1 => zynq7000::eth::Ethernet::new_mmio_fixed_1(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait PsEthernet {
|
pub trait PsEthernet {
|
||||||
fn reg_block(&self) -> MmioEthernet<'static>;
|
fn reg_block(&self) -> MmioEthernet<'static>;
|
||||||
fn id(&self) -> Option<EthernetId>;
|
fn id(&self) -> Option<EthernetId>;
|
||||||
@ -185,196 +198,9 @@ impl RxData3 for Pin<Mio38> {
|
|||||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(regs: zynq7000::eth::MmioEthernet<'static>) -> Option<Self> {
|
|
||||||
regs.id()?;
|
|
||||||
Some(EthernetLowLevel {
|
|
||||||
id: regs.id().unwrap(),
|
|
||||||
regs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Ethernet {
|
pub struct Ethernet {
|
||||||
ll: EthernetLowLevel,
|
ll: ll::EthernetLowLevel,
|
||||||
|
mdio: mdio::Mdio,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ethernet {
|
impl Ethernet {
|
||||||
@ -395,8 +221,9 @@ impl Ethernet {
|
|||||||
MdClkPin: MdClk,
|
MdClkPin: MdClk,
|
||||||
MdIoPin: MdIo,
|
MdIoPin: MdIo,
|
||||||
>(
|
>(
|
||||||
mut ll: EthernetLowLevel,
|
mut ll: ll::EthernetLowLevel,
|
||||||
clk_config: ClkConfig,
|
clk_config: ll::ClkConfig,
|
||||||
|
mdc_clk_div: MdcClkDiv,
|
||||||
tx_clk: TxClkPin,
|
tx_clk: TxClkPin,
|
||||||
tx_ctrl: TxCtrlPin,
|
tx_ctrl: TxCtrlPin,
|
||||||
tx_data: (TxData0Pin, TxData1Pin, TxData2Pin, TxData3Pin),
|
tx_data: (TxData0Pin, TxData1Pin, TxData2Pin, TxData3Pin),
|
||||||
@ -522,13 +349,17 @@ impl Ethernet {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ll.configure_clock(clk_config);
|
ll.configure_clock(clk_config);
|
||||||
Ethernet { ll }
|
let mut mdio = mdio::Mdio::new(&ll, true);
|
||||||
|
mdio.configure_clock_div(mdc_clk_div);
|
||||||
|
Ethernet { ll, mdio }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(mut ll: EthernetLowLevel, clk_config: ClkConfig) -> Self {
|
pub fn new(mut ll: EthernetLowLevel, clk_config: ClkConfig, mdc_clk_div: MdcClkDiv) -> Self {
|
||||||
Self::common_init(&mut ll);
|
Self::common_init(&mut ll);
|
||||||
ll.configure_clock(clk_config);
|
ll.configure_clock(clk_config);
|
||||||
Ethernet { ll }
|
let mut mdio = mdio::Mdio::new(&ll, true);
|
||||||
|
mdio.configure_clock_div(mdc_clk_div);
|
||||||
|
Ethernet { ll, mdio }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn common_init(ll: &mut EthernetLowLevel) {
|
fn common_init(ll: &mut EthernetLowLevel) {
|
||||||
@ -558,73 +389,10 @@ impl Ethernet {
|
|||||||
&mut self.ll.regs
|
&mut self.ll.regs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// RX buffer descriptor.
|
|
||||||
///
|
|
||||||
/// The user should declare an array of this structure inside uncached memory.
|
|
||||||
///
|
|
||||||
/// These descriptors are shared between software and hardware and contain information
|
|
||||||
/// related to frame reception.
|
|
||||||
pub struct RxBufDescr {
|
|
||||||
/// The first word of the descriptor.
|
|
||||||
pub word0: RxBufDescrWord0,
|
|
||||||
/// The second word of the descriptor.
|
|
||||||
pub word1: RxBufDescrWord1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Ownership {
|
pub(crate) enum Ownership {
|
||||||
Hardware = 0,
|
Hardware = 0,
|
||||||
Software = 1,
|
Software = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub struct RxBufDescrWord0 {
|
|
||||||
/// The full reception address with the last two bits cleared.
|
|
||||||
#[bits(2..=31, rw)]
|
|
||||||
addr: u30,
|
|
||||||
#[bit(1, rw)]
|
|
||||||
wrap: bool,
|
|
||||||
#[bit(1, rw)]
|
|
||||||
ownership: Ownership,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub struct RxBufDescrWord1 {
|
|
||||||
#[bit(31, r)]
|
|
||||||
broadcast_detect: bool,
|
|
||||||
#[bit(30, r)]
|
|
||||||
multicast_hash: bool,
|
|
||||||
#[bit(29, r)]
|
|
||||||
unicast_hash: bool,
|
|
||||||
#[bit(27, r)]
|
|
||||||
specific_addr_match: bool,
|
|
||||||
/// Specifies which of the 4 specific address registers was matched.
|
|
||||||
#[bits(25..=26, r)]
|
|
||||||
specific_addr_match_info: u2,
|
|
||||||
#[bit(24, r)]
|
|
||||||
type_id_match_or_snap_info: bool,
|
|
||||||
#[bits(22..=23, r)]
|
|
||||||
type_id_match_info_or_chksum_status: u2,
|
|
||||||
#[bit(21, r)]
|
|
||||||
vlan_tag_detected: bool,
|
|
||||||
#[bit(20, r)]
|
|
||||||
priority_tag_detected: bool,
|
|
||||||
#[bits(17..=19, r)]
|
|
||||||
vlan_prio: u3,
|
|
||||||
#[bit(16, r)]
|
|
||||||
cfi_bit: bool,
|
|
||||||
#[bit(15, r)]
|
|
||||||
end_of_frame: bool,
|
|
||||||
#[bit(14, r)]
|
|
||||||
start_of_frame: bool,
|
|
||||||
/// Relevant when FCS errors are not ignored.
|
|
||||||
/// 0: Frame has good FCS, 1: Frame has bad FCS, but was copied to memory as the ignore FCS
|
|
||||||
/// functionality was enabled.
|
|
||||||
#[bit(13, r)]
|
|
||||||
fcs_status: bool,
|
|
||||||
#[bits(0..=12, r)]
|
|
||||||
rx_len: u13,
|
|
||||||
}
|
|
||||||
|
85
zynq7000-hal/src/eth/rx_descr.rs
Normal file
85
zynq7000-hal/src/eth/rx_descr.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//! RX buffer descriptor module.
|
||||||
|
pub use super::Ownership;
|
||||||
|
use arbitrary_int::{u2, u3, u13, u30};
|
||||||
|
|
||||||
|
/// RX buffer descriptor.
|
||||||
|
///
|
||||||
|
/// The user should declare an array of this structure inside uncached memory.
|
||||||
|
///
|
||||||
|
/// These descriptors are shared between software and hardware and contain information
|
||||||
|
/// related to frame reception.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Descriptor {
|
||||||
|
/// The first word of the descriptor.
|
||||||
|
pub word0: Word0,
|
||||||
|
/// The second word of the descriptor.
|
||||||
|
pub word1: Word1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct Word0 {
|
||||||
|
/// The full reception address with the last two bits cleared.
|
||||||
|
#[bits(2..=31, rw)]
|
||||||
|
addr_upper_30_bits: u30,
|
||||||
|
#[bit(1, rw)]
|
||||||
|
wrap: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
ownership: Ownership,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct Word1 {
|
||||||
|
#[bit(31, r)]
|
||||||
|
broadcast_detect: bool,
|
||||||
|
#[bit(30, r)]
|
||||||
|
multicast_hash: bool,
|
||||||
|
#[bit(29, r)]
|
||||||
|
unicast_hash: bool,
|
||||||
|
#[bit(27, r)]
|
||||||
|
specific_addr_match: bool,
|
||||||
|
/// Specifies which of the 4 specific address registers was matched.
|
||||||
|
#[bits(25..=26, r)]
|
||||||
|
specific_addr_match_info: u2,
|
||||||
|
#[bit(24, r)]
|
||||||
|
type_id_match_or_snap_info: bool,
|
||||||
|
#[bits(22..=23, r)]
|
||||||
|
type_id_match_info_or_chksum_status: u2,
|
||||||
|
#[bit(21, r)]
|
||||||
|
vlan_tag_detected: bool,
|
||||||
|
#[bit(20, r)]
|
||||||
|
priority_tag_detected: bool,
|
||||||
|
#[bits(17..=19, r)]
|
||||||
|
vlan_prio: u3,
|
||||||
|
#[bit(16, r)]
|
||||||
|
cfi_bit: bool,
|
||||||
|
#[bit(15, r)]
|
||||||
|
end_of_frame: bool,
|
||||||
|
#[bit(14, r)]
|
||||||
|
start_of_frame: bool,
|
||||||
|
/// Relevant when FCS errors are not ignored.
|
||||||
|
/// 0: Frame has good FCS, 1: Frame has bad FCS, but was copied to memory as the ignore FCS
|
||||||
|
/// functionality was enabled.
|
||||||
|
#[bit(13, r)]
|
||||||
|
fcs_status: bool,
|
||||||
|
#[bits(0..=12, r)]
|
||||||
|
rx_len: u13,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Descriptor {
|
||||||
|
#[inline]
|
||||||
|
pub fn set_ownership(&mut self, ownership: Ownership) {
|
||||||
|
self.word0.set_ownership(ownership);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_wrap_bit(&mut self) {
|
||||||
|
self.word0.set_wrap(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn write_rx_addr(&mut self, addr: u32) {
|
||||||
|
self.word0.set_addr_upper_30_bits(u30::new(addr >> 2));
|
||||||
|
}
|
||||||
|
}
|
53
zynq7000-hal/src/eth/tx_descr.rs
Normal file
53
zynq7000-hal/src/eth/tx_descr.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use arbitrary_int::u14;
|
||||||
|
|
||||||
|
pub use super::Ownership;
|
||||||
|
|
||||||
|
/// RX buffer descriptor.
|
||||||
|
///
|
||||||
|
/// The user should declare an array of this structure inside uncached memory.
|
||||||
|
///
|
||||||
|
/// These descriptors are shared between software and hardware and contain information
|
||||||
|
/// related to frame reception.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Descriptor {
|
||||||
|
/// The first word of the descriptor which is the byte address of the buffer.
|
||||||
|
pub word0: u32,
|
||||||
|
/// The second word of the descriptor.
|
||||||
|
pub word1: Word1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u3, exhaustive = true)]
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum TransmitChecksumGenerationStatus {
|
||||||
|
NoError = 0b000,
|
||||||
|
VlanError = 0b001,
|
||||||
|
SnapError = 0b010,
|
||||||
|
IpError = 0b011,
|
||||||
|
NotVlanOrSnapOrIp = 0b100,
|
||||||
|
NonSupportedPacketFragmentation = 0b101,
|
||||||
|
PacketNotTcpUdp = 0b110,
|
||||||
|
PrematureEndOfFrame = 0b111,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct Word1 {
|
||||||
|
#[bit(31, rw)]
|
||||||
|
ownership: Ownership,
|
||||||
|
#[bit(30, rw)]
|
||||||
|
wrap: bool,
|
||||||
|
#[bit(29, rw)]
|
||||||
|
retry_limit_exceeded: bool,
|
||||||
|
#[bit(27, rw)]
|
||||||
|
transmit_frame_corruption_ahb_error: bool,
|
||||||
|
#[bit(26, rw)]
|
||||||
|
late_collision: bool,
|
||||||
|
#[bits(20..=22, rw)]
|
||||||
|
checksum_status: TransmitChecksumGenerationStatus,
|
||||||
|
#[bit(16, rw)]
|
||||||
|
no_crc_generation: bool,
|
||||||
|
#[bit(15, rw)]
|
||||||
|
last_buffer: bool,
|
||||||
|
#[bits(0..=13, rw)]
|
||||||
|
tx_len: u14,
|
||||||
|
}
|
@ -386,7 +386,7 @@ pub enum PhyOperation {
|
|||||||
Write = 0b01,
|
Write = 0b01,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32)]
|
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PhyMaintenance {
|
pub struct PhyMaintenance {
|
||||||
/// Must be 1 for Clause 22 operations.
|
/// Must be 1 for Clause 22 operations.
|
||||||
@ -399,9 +399,9 @@ pub struct PhyMaintenance {
|
|||||||
#[bits(18..=22, rw)]
|
#[bits(18..=22, rw)]
|
||||||
reg_addr: u5,
|
reg_addr: u5,
|
||||||
#[bits(16..=17, rw)]
|
#[bits(16..=17, rw)]
|
||||||
must_be_10: u2,
|
must_be_0b10: u2,
|
||||||
#[bits(0..=15, rw)]
|
#[bits(0..=15, rw)]
|
||||||
data_mask: u16,
|
data: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bitbybit::bitfield(u32)]
|
#[bitbybit::bitfield(u32)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user