use core::task::Context;
use crate::ethernet::{
mac::{Duplex, LinkState, Speed},
phy::{ANAR, ANLPAR, BMCR, BMSR, MdioBus, PHYIDR1, Phy, PhyError, an, bmcr, bmsr},
};
const RESET_POLL_LIMIT: u32 = 500_000;
#[derive(Clone, Copy, Debug)]
pub struct GenericPhy {
addr: Option<u8>,
}
impl GenericPhy {
pub const fn new(addr: u8) -> Self {
Self { addr: Some(addr) }
}
pub const fn new_auto() -> Self {
Self { addr: None }
}
fn discover<M: MdioBus>(mdio: &mut M) -> Option<u8> {
for _ in 0..3 {
for addr in 0..32_u8 {
let id = mdio.read(addr, PHYIDR1);
if id != 0x0000 && id != 0xFFFF {
debug!("phy found at addr {} - id: {:x}", addr, id);
return Some(addr);
}
}
}
None
}
}
impl Phy for GenericPhy {
fn address(&self) -> u8 {
self.addr
.expect("GenericPhy address not yet resolved — call init() first")
}
fn init<M: MdioBus>(&mut self, mdio: &mut M) -> Result<(), PhyError> {
if self.addr.is_none() {
self.addr = Some(Self::discover(mdio).ok_or(PhyError::NotFound)?);
}
let addr = self.addr.unwrap();
mdio.write(addr, BMCR, bmcr::RESET);
for _ in 0..RESET_POLL_LIMIT {
if mdio.read(addr, BMCR) & bmcr::RESET == 0 {
let adv =
an::BASE_10_HALF | an::BASE_10_FULL | an::BASE_100_HALF | an::BASE_100_FULL;
mdio.write(addr, ANAR, adv | 0x0001);
let ctrl = mdio.read(addr, BMCR);
mdio.write(addr, BMCR, ctrl | bmcr::ANEN | bmcr::RESTART_AN);
return Ok(());
}
crate::rom::ets_delay_us(1);
}
Err(PhyError::Timeout)
}
fn poll_link<M: MdioBus>(&mut self, mdio: &mut M, cx: Option<&mut Context<'_>>) -> LinkState {
const LINK_STATE_DOWN: LinkState = LinkState {
up: false,
speed: Speed::_100M,
duplex: Duplex::Full,
};
if let Some(cx) = cx {
cx.waker().wake_by_ref();
}
let Some(addr) = self.addr else {
debug!("poll_link called before init");
return LINK_STATE_DOWN;
};
let _ = mdio.read(addr, BMSR);
let bmsr_val = mdio.read(addr, BMSR);
trace!("bmsr_val: {:x}", bmsr_val);
if bmsr_val & bmsr::LINK_STATUS == 0 {
return LINK_STATE_DOWN;
}
if bmsr_val & bmsr::AN_COMPLETE == 0 {
return LINK_STATE_DOWN;
}
let anlpar_val = mdio.read(addr, ANLPAR);
let speed = if anlpar_val & an::BASE_100_FULL != 0 || anlpar_val & an::BASE_100_HALF != 0 {
Speed::_100M
} else {
Speed::_10M
};
let duplex = if anlpar_val & an::BASE_10_FULL != 0 || anlpar_val & an::BASE_100_FULL != 0 {
Duplex::Full
} else {
Duplex::Half
};
LinkState {
up: true,
speed,
duplex,
}
}
}