zynq::eth: implement phy::extended_status, set clock for link speed

This commit is contained in:
Astro 2019-11-04 02:23:27 +01:00
parent 961e2e1dd0
commit 99a00e019b
4 changed files with 175 additions and 20 deletions

View File

@ -13,6 +13,8 @@ pub mod tx;
pub const MTU: usize = 1536; pub const MTU: usize = 1536;
/// Maximum MDC clock /// Maximum MDC clock
const MAX_MDC: u32 = 2_500_000; const MAX_MDC: u32 = 2_500_000;
const TX_10: u32 = 10_000_000;
const TX_100: u32 = 25_000_000;
/// Clock for GbE /// Clock for GbE
const TX_1000: u32 = 125_000_000; 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 { fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self {
let mut inner = EthInner { let mut inner = EthInner {
regs, regs,
link: false, link: None,
}; };
inner.init(); inner.init();
inner.configure(macaddr); 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> { struct EthInner<'r> {
regs: &'r mut regs::RegisterBlock, regs: &'r mut regs::RegisterBlock,
link: bool, link: Option<phy::Link>,
} }
impl<'r> EthInner<'r> { impl<'r> EthInner<'r> {
@ -502,28 +504,42 @@ impl<'r> EthInner<'r> {
fn check_link_change(&mut self, phy: &Phy) { 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 // Check link state transition
match (self.link, link) { if self.link != link {
(false, true) => { match &link {
println!("eth: got link, setting clock for gigabit"); Some(link) => {
// TODO: should derive gem0/gem107 println!("eth: got {:?}", link);
Eth::<(), ()>::setup_gem0_clock(TX_1000);
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)
);
} }
(true, false) => { None => {
println!("eth: link lost"); println!("eth: link lost");
phy.modify_control(self, |control| phy.modify_control(self, |control|
control.set_autoneg_enable(true) control.set_autoneg_enable(true)
.set_restart_autoneg(true) .set_restart_autoneg(true)
); );
} }
_ => {}
} }
self.link = link; self.link = link;
} }
} }
}
impl<'r> PhyAccess for EthInner<'r> { impl<'r> PhyAccess for EthInner<'r> {
fn read_phy(&mut self, addr: u8, reg: u8) -> u16 { fn read_phy(&mut self, addr: u8, reg: u8) -> u16 {

View File

@ -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<Link> {
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<u16> for ExtendedStatus {
fn from(value: u16) -> Self {
ExtendedStatus(value)
}
}

View File

@ -2,9 +2,30 @@ pub mod id;
use id::{identify_phy, PhyIdentifier}; use id::{identify_phy, PhyIdentifier};
mod status; mod status;
pub use status::Status; pub use status::Status;
mod extended_status;
pub use extended_status::ExtendedStatus;
mod control; mod control;
pub use control::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 { pub trait PhyAccess {
fn read_phy(&mut self, addr: u8, reg: u8) -> u16; fn read_phy(&mut self, addr: u8, reg: u8) -> u16;
fn write_phy(&mut self, addr: u8, reg: u8, data: u16); fn write_phy(&mut self, addr: u8, reg: u8, data: u16);
@ -94,6 +115,22 @@ impl Phy {
self.read_reg(pa) self.read_reg(pa)
} }
pub fn get_link<PA: PhyAccess>(&self, pa: &mut PA) -> Option<Link> {
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<PA: PhyAccess>(&self, pa: &mut PA) { pub fn reset<PA: PhyAccess>(&self, pa: &mut PA) {
self.modify_control(pa, |control| self.modify_control(pa, |control|
control.set_reset(true) control.set_reset(true)

View File

@ -1,5 +1,5 @@
use bit_field::BitField; use bit_field::BitField;
use super::PhyRegister; use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
/// Basic Mode Status Register /// Basic Mode Status Register
@ -51,6 +51,49 @@ impl Status {
pub fn cap_100base_t4(&self) -> bool { pub fn cap_100base_t4(&self) -> bool {
self.0.get_bit(15) self.0.get_bit(15)
} }
pub fn get_link(&self) -> Option<Link> {
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 { impl PhyRegister for Status {