aboutsummaryrefslogtreecommitdiff
path: root/crates/net-phys/src/ksz8081.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/net-phys/src/ksz8081.rs')
-rw-r--r--crates/net-phys/src/ksz8081.rs130
1 files changed, 130 insertions, 0 deletions
diff --git a/crates/net-phys/src/ksz8081.rs b/crates/net-phys/src/ksz8081.rs
new file mode 100644
index 0000000..476997e
--- /dev/null
+++ b/crates/net-phys/src/ksz8081.rs
@@ -0,0 +1,130 @@
+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<E> {
+ Phy(E),
+ NotFound,
+}
+
+impl<E: core::fmt::Display> core::fmt::Display for Error<E> {
+ 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<E> From<E> for Error<E> {
+ fn from(value: E) -> Self {
+ Self::Phy(value)
+ }
+}
+
+impl<E: core::error::Error> core::error::Error for Error<E> {
+ // TODO
+}
+
+fn init<M: super::Miim>(miim: &mut M) -> Result<(), Error<M::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<M: super::Miim> super::Phy<M> for Ksz8081 {
+ type Error = Error<M::Error>;
+
+ fn initialize(&mut self, miim: &mut M) -> Result<(), Self::Error> {
+ init(miim)
+ }
+}