diff --git a/src/zynq/eth/mod.rs b/src/zynq/eth/mod.rs index ba6a2413..51680a28 100644 --- a/src/zynq/eth/mod.rs +++ b/src/zynq/eth/mod.rs @@ -13,6 +13,8 @@ pub mod tx; pub const MTU: usize = 1536; /// Maximum MDC clock const MAX_MDC: u32 = 2_500_000; +const TX_10: u32 = 10_000_000; +const TX_100: u32 = 25_000_000; /// Clock for GbE const TX_1000: u32 = 125_000_000; @@ -171,7 +173,7 @@ impl<'r> Eth<'r, (), ()> { fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self { let mut inner = EthInner { regs, - link: false, + link: None, }; inner.init(); inner.configure(macaddr); @@ -369,7 +371,7 @@ impl<'r, 'rx, 'tx: 'a, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescLis struct EthInner<'r> { regs: &'r mut regs::RegisterBlock, - link: bool, + link: Option, } impl<'r> EthInner<'r> { @@ -502,26 +504,40 @@ impl<'r> EthInner<'r> { fn check_link_change(&mut self, phy: &Phy) { - let link = phy.get_status(self).link_status(); + let link = phy.get_link(self); // Check link state transition - match (self.link, link) { - (false, true) => { - println!("eth: got link, setting clock for gigabit"); - // TODO: should derive gem0/gem107 - Eth::<(), ()>::setup_gem0_clock(TX_1000); - } - (true, false) => { - println!("eth: link lost"); - phy.modify_control(self, |control| - control.set_autoneg_enable(true) - .set_restart_autoneg(true) - ); - } - _ => {} - } + if self.link != link { + match &link { + Some(link) => { + println!("eth: got {:?}", link); - self.link = link; + use phy::LinkSpeed::*; + let txclock = match link.speed { + S10 => TX_10, + S100 => TX_100, + S1000 => TX_1000, + }; + Eth::<(), ()>::setup_gem0_clock(txclock); + /* .full_duplex(false) doesn't work even if + half duplex has been negotiated. */ + self.regs.net_cfg.modify(|_, w| w + .full_duplex(true) + .gige_en(link.speed == S1000) + .speed(link.speed != S10) + ); + } + None => { + println!("eth: link lost"); + phy.modify_control(self, |control| + control.set_autoneg_enable(true) + .set_restart_autoneg(true) + ); + } + } + + self.link = link; + } } } diff --git a/src/zynq/eth/phy/extended_status.rs b/src/zynq/eth/phy/extended_status.rs new file mode 100644 index 00000000..abf2d7a7 --- /dev/null +++ b/src/zynq/eth/phy/extended_status.rs @@ -0,0 +1,59 @@ +use bit_field::BitField; +use super::{PhyRegister, Link, LinkDuplex, LinkSpeed}; + +#[derive(Clone, Copy, Debug)] +/// 1000Base-T Extended Status Register +pub struct ExtendedStatus(pub u16); + +impl ExtendedStatus { + pub fn cap_1000base_t_half(&self) -> bool { + self.0.get_bit(12) + } + pub fn cap_1000base_t_full(&self) -> bool { + self.0.get_bit(13) + } + pub fn cap_1000base_x_half(&self) -> bool { + self.0.get_bit(14) + } + pub fn cap_1000base_x_full(&self) -> bool { + self.0.get_bit(12) + } + + pub fn get_link(&self) -> Option { + if self.cap_1000base_t_half() { + Some(Link { + speed: LinkSpeed::S1000, + duplex: LinkDuplex::Half, + }) + } else if self.cap_1000base_t_full() { + Some(Link { + speed: LinkSpeed::S1000, + duplex: LinkDuplex::Full, + }) + } else if self.cap_1000base_x_half() { + Some(Link { + speed: LinkSpeed::S1000, + duplex: LinkDuplex::Half, + }) + } else if self.cap_1000base_x_full() { + Some(Link { + speed: LinkSpeed::S1000, + duplex: LinkDuplex::Full, + }) + } else { + None + } + } +} + +impl PhyRegister for ExtendedStatus { + fn addr() -> u8 { + 0xF + } +} + +impl From for ExtendedStatus { + fn from(value: u16) -> Self { + ExtendedStatus(value) + } +} diff --git a/src/zynq/eth/phy/mod.rs b/src/zynq/eth/phy/mod.rs index 903f4c50..bc22fd89 100644 --- a/src/zynq/eth/phy/mod.rs +++ b/src/zynq/eth/phy/mod.rs @@ -2,9 +2,30 @@ pub mod id; use id::{identify_phy, PhyIdentifier}; mod status; pub use status::Status; +mod extended_status; +pub use extended_status::ExtendedStatus; mod control; pub use control::Control; +#[derive(Clone, Debug, PartialEq)] +pub struct Link { + pub speed: LinkSpeed, + pub duplex: LinkDuplex, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum LinkSpeed { + S10, + S100, + S1000, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum LinkDuplex { + Half, + Full, +} + pub trait PhyAccess { fn read_phy(&mut self, addr: u8, reg: u8) -> u16; fn write_phy(&mut self, addr: u8, reg: u8, data: u16); @@ -94,6 +115,22 @@ impl Phy { self.read_reg(pa) } + pub fn get_link(&self, pa: &mut PA) -> Option { + let status = self.get_status(pa); + if !status.link_status() { + None + } else if status.cap_1000base_t_extended_status() { + let ext_status: ExtendedStatus = self.read_reg(pa); + if let Some(link) = ext_status.get_link() { + Some(link) + } else { + status.get_link() + } + } else { + status.get_link() + } + } + pub fn reset(&self, pa: &mut PA) { self.modify_control(pa, |control| control.set_reset(true) diff --git a/src/zynq/eth/phy/status.rs b/src/zynq/eth/phy/status.rs index e220b34e..1ff8016e 100644 --- a/src/zynq/eth/phy/status.rs +++ b/src/zynq/eth/phy/status.rs @@ -1,5 +1,5 @@ use bit_field::BitField; -use super::PhyRegister; +use super::{PhyRegister, Link, LinkDuplex, LinkSpeed}; #[derive(Clone, Copy, Debug)] /// Basic Mode Status Register @@ -51,6 +51,49 @@ impl Status { pub fn cap_100base_t4(&self) -> bool { self.0.get_bit(15) } + + pub fn get_link(&self) -> Option { + if ! self.link_status() { + None + } else if self.cap_10base_t_half() { + Some(Link { + speed: LinkSpeed::S10, + duplex: LinkDuplex::Half, + }) + } else if self.cap_10base_t_full() { + Some(Link { + speed: LinkSpeed::S10, + duplex: LinkDuplex::Full, + }) + } else if self.cap_10base_t2_half() { + Some(Link { + speed: LinkSpeed::S10, + duplex: LinkDuplex::Half, + }) + } else if self.cap_10base_t2_full() { + Some(Link { + speed: LinkSpeed::S10, + duplex: LinkDuplex::Full, + }) + } else if self.cap_100base_t4() { + Some(Link { + speed: LinkSpeed::S100, + duplex: LinkDuplex::Half, + }) + } else if self.cap_100base_tx_half() { + Some(Link { + speed: LinkSpeed::S100, + duplex: LinkDuplex::Half, + }) + } else if self.cap_100base_tx_full() { + Some(Link { + speed: LinkSpeed::S100, + duplex: LinkDuplex::Full, + }) + } else { + None + } + } } impl PhyRegister for Status {