From c4962c8b9207e9474659526e91a1cf387d2a7b8f Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Sun, 9 Aug 2026 12:09:22 -0400 Subject: Integrate ENET, GPIO, PHY drivers for 1170EVK --- crates/net-phys/Cargo.toml | 8 +++ crates/net-phys/src/ksz8081.rs | 130 +++++++++++++++++++++++++++++++++++++++++ crates/net-phys/src/lib.rs | 62 ++++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 crates/net-phys/Cargo.toml create mode 100644 crates/net-phys/src/ksz8081.rs create mode 100644 crates/net-phys/src/lib.rs (limited to 'crates/net-phys') diff --git a/crates/net-phys/Cargo.toml b/crates/net-phys/Cargo.toml new file mode 100644 index 0000000..d7b4d3e --- /dev/null +++ b/crates/net-phys/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust-threadx-net-phys" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] +bitflags = { version = "2.13.1" } 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 { + 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) + } +} 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; + + 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; + + fn write(&mut self, phy: u8, reg: u8, data: u16) -> Result<(), Self::Error>; +} + +impl Miim for T +where + T: Mdio, +{ + type Error = ::Error; + + fn read(&mut self, phy: u8, reg: u8) -> Result { + 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 { + type Error: core::error::Error; + + fn initialize(&mut self, miim: &mut M) -> Result<(), Self::Error>; +} -- cgit v1.2.3