const PHY_ID1_REG: u8 = 0x02; const PHY_BASICCONTROL_REG: u8 = 0x00; const PHY_BASICSTATUS_REG: u8 = 0x01; const PHY_AUTONEG_ADVERTISE_REG: u8 = 0x04; const KSZ8081_PHY_ADDR: u8 = 0x02; // Arbitrary. const KSZ8081_PHY_CONTROL2_REG: u8 = 0x1F; const KSZ8081_PHY_CTL2_REFCLK_SELECT_MASK: u16 = 0x0080; bitflags::bitflags! { struct BasicControl : u16 { const RESET = 0x8000; const AUTONEG = 0x1000; const RESTART_AUTONEG = 0x0200; } } bitflags::bitflags! { struct BasicStatus : u16 { const LINKSTATUS = 0x0004; const AUTONEG_COMP = 0x0020; } } bitflags::bitflags! { struct AutoNegotiation : u16 { const BASET4_100_ABILITY = 0x0200; const BASETX_100_FULL_DUPLEX = 0x0100; const BASETX_100_HALF_DUPLEX = 0x0080; const BASETX_10_FULL_DUPLEX = 0x0040; const BASETX_10_HALF_DUPLEX = 0x0020; const IEEE802_3_SELECTOR = 0x0001; } } const KSZ8081_ID: u16 = 0x22; #[derive(Debug)] pub enum Error { Phy(E), NotFound, } impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Phy(err) => write!(f, "PHY error: {err}"), Self::NotFound => write!(f, "KSZ8081 not found"), } } } impl From for Error { fn from(value: E) -> Self { Self::Phy(value) } } impl core::error::Error for Error { // TODO } fn init(miim: &mut M) -> Result<(), Error> { let mut retries = 100000usize; while retries > 0 { let address = miim.read(KSZ8081_PHY_ADDR, PHY_ID1_REG)?; if address == KSZ8081_ID { break; } retries -= 1; fusible::thread::sleep(1); } if retries == 0 { return Err(Error::NotFound); } miim.write( KSZ8081_PHY_ADDR, PHY_BASICCONTROL_REG, BasicControl::RESET.bits(), )?; // Use RMII50M (KSZ8081-specific configuration). let ctrl2 = miim.read(KSZ8081_PHY_ADDR, KSZ8081_PHY_CONTROL2_REG)?; miim.write( KSZ8081_PHY_ADDR, KSZ8081_PHY_CONTROL2_REG, ctrl2 | KSZ8081_PHY_CTL2_REFCLK_SELECT_MASK, )?; // Enable auto-negotiation. miim.write( KSZ8081_PHY_ADDR, PHY_AUTONEG_ADVERTISE_REG, (AutoNegotiation::BASETX_100_FULL_DUPLEX | AutoNegotiation::IEEE802_3_SELECTOR).bits(), )?; miim.write( KSZ8081_PHY_ADDR, PHY_BASICCONTROL_REG, (BasicControl::AUTONEG | BasicControl::RESTART_AUTONEG).bits(), )?; // Wait for auto-negotiation, and for the link to be available. // // This blocks while there's no link. while !BasicStatus::from_bits_truncate(miim.read(KSZ8081_PHY_ADDR, PHY_BASICSTATUS_REG)?) .contains(BasicStatus::AUTONEG_COMP | BasicStatus::LINKSTATUS) { fusible::thread::sleep(1); } Ok(()) } pub struct Ksz8081(()); impl Ksz8081 { pub fn new() -> Self { Self(()) } } impl super::Phy for Ksz8081 { type Error = Error; fn initialize(&mut self, miim: &mut M) -> Result<(), Self::Error> { init(miim) } }