aboutsummaryrefslogtreecommitdiff
path: root/crates/net-phys/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/net-phys/src')
-rw-r--r--crates/net-phys/src/ksz8081.rs130
-rw-r--r--crates/net-phys/src/lib.rs62
2 files changed, 192 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)
+ }
+}
diff --git a/crates/net-phys/src/lib.rs b/crates/net-phys/src/lib.rs
new file mode 100644
index 0000000..c476799
--- /dev/null
+++ b/crates/net-phys/src/lib.rs
@@ -0,0 +1,62 @@
+#![no_std]
+#![feature(rustc_private)]
+extern crate fusible;
+
+pub mod ksz8081;
+
+pub trait Mdio {
+ type Error: core::error::Error;
+
+ fn read(&mut self, ctrl: u16) -> Result<u16, Self::Error>;
+
+ fn write(&mut self, ctrl: u16, data: u16) -> Result<(), Self::Error>;
+}
+
+pub trait Miim {
+ type Error: core::error::Error;
+
+ fn read(&mut self, phy: u8, reg: u8) -> Result<u16, Self::Error>;
+
+ fn write(&mut self, phy: u8, reg: u8, data: u16) -> Result<(), Self::Error>;
+}
+
+impl<T> Miim for T
+where
+ T: Mdio,
+{
+ type Error = <T as Mdio>::Error;
+
+ fn read(&mut self, phy: u8, reg: u8) -> Result<u16, Self::Error> {
+ Mdio::read(self, read_ctrl_bits(phy, reg))
+ }
+
+ fn write(&mut self, phy: u8, reg: u8, data: u16) -> Result<(), Self::Error> {
+ Mdio::write(self, write_ctrl_bits(phy, reg), data)
+ }
+}
+
+const fn phy_addr_ctrl_bits(phy_addr: u8) -> u16 {
+ const PHY_ADDR_OFFSET: u16 = 7;
+ ((phy_addr & 0b00011111) as u16) << PHY_ADDR_OFFSET
+}
+
+const fn reg_addr_ctrl_bits(reg_addr: u8) -> u16 {
+ const REG_ADDR_OFFSET: u16 = 2;
+ ((reg_addr & 0b00011111) as u16) << REG_ADDR_OFFSET
+}
+
+pub const fn read_ctrl_bits(phy_addr: u8, reg_addr: u8) -> u16 {
+ const READ_CTRL_BITS: u16 = 0b0110_00000_00000_00;
+ READ_CTRL_BITS | phy_addr_ctrl_bits(phy_addr) | reg_addr_ctrl_bits(reg_addr)
+}
+
+pub const fn write_ctrl_bits(phy_addr: u8, reg_addr: u8) -> u16 {
+ const WRITE_CTRL_BITS: u16 = 0b0101_00000_00000_10;
+ WRITE_CTRL_BITS | phy_addr_ctrl_bits(phy_addr) | reg_addr_ctrl_bits(reg_addr)
+}
+
+pub trait Phy<M: Miim> {
+ type Error: core::error::Error;
+
+ fn initialize(&mut self, miim: &mut M) -> Result<(), Self::Error>;
+}