1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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)
}
}
|