aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/eth/lan8742a.rs103
-rw-r--r--embassy-stm32/src/eth/mod.rs27
-rw-r--r--embassy-stm32/src/eth/v2/mod.rs94
3 files changed, 204 insertions, 20 deletions
diff --git a/embassy-stm32/src/eth/lan8742a.rs b/embassy-stm32/src/eth/lan8742a.rs
new file mode 100644
index 000000000..74d0ca5de
--- /dev/null
+++ b/embassy-stm32/src/eth/lan8742a.rs
@@ -0,0 +1,103 @@
1//! SMSC LAN8742A Ethernet PHY
2
3use super::{StationManagement, PHY};
4
5#[allow(dead_code)]
6mod phy_consts {
7 pub const PHY_REG_BCR: u8 = 0x00;
8 pub const PHY_REG_BSR: u8 = 0x01;
9 pub const PHY_REG_ID1: u8 = 0x02;
10 pub const PHY_REG_ID2: u8 = 0x03;
11 pub const PHY_REG_ANTX: u8 = 0x04;
12 pub const PHY_REG_ANRX: u8 = 0x05;
13 pub const PHY_REG_ANEXP: u8 = 0x06;
14 pub const PHY_REG_ANNPTX: u8 = 0x07;
15 pub const PHY_REG_ANNPRX: u8 = 0x08;
16 pub const PHY_REG_SSR: u8 = 0x1F; // Special Status Register
17 pub const PHY_REG_CTL: u8 = 0x0D; // Ethernet PHY Register Control
18 pub const PHY_REG_ADDAR: u8 = 0x0E; // Ethernet PHY Address or Data
19
20 pub const PHY_REG_WUCSR: u16 = 0x8010;
21
22 pub const PHY_REG_BCR_COLTEST: u16 = 1 << 7;
23 pub const PHY_REG_BCR_FD: u16 = 1 << 8;
24 pub const PHY_REG_BCR_ANRST: u16 = 1 << 9;
25 pub const PHY_REG_BCR_ISOLATE: u16 = 1 << 10;
26 pub const PHY_REG_BCR_POWERDN: u16 = 1 << 11;
27 pub const PHY_REG_BCR_AN: u16 = 1 << 12;
28 pub const PHY_REG_BCR_100M: u16 = 1 << 13;
29 pub const PHY_REG_BCR_LOOPBACK: u16 = 1 << 14;
30 pub const PHY_REG_BCR_RESET: u16 = 1 << 15;
31
32 pub const PHY_REG_BSR_JABBER: u16 = 1 << 1;
33 pub const PHY_REG_BSR_UP: u16 = 1 << 2;
34 pub const PHY_REG_BSR_FAULT: u16 = 1 << 4;
35 pub const PHY_REG_BSR_ANDONE: u16 = 1 << 5;
36
37 pub const PHY_REG_SSR_ANDONE: u16 = 1 << 12;
38 pub const PHY_REG_SSR_SPEED: u16 = 0b111 << 2;
39 pub const PHY_REG_SSR_10BASE_HD: u16 = 0b001 << 2;
40 pub const PHY_REG_SSR_10BASE_FD: u16 = 0b101 << 2;
41 pub const PHY_REG_SSR_100BASE_HD: u16 = 0b010 << 2;
42 pub const PHY_REG_SSR_100BASE_FD: u16 = 0b110 << 2;
43}
44use self::phy_consts::*;
45
46/// SMSC LAN8742A Ethernet PHY
47pub struct LAN8742A;
48
49unsafe impl PHY for LAN8742A {
50 /// Reset PHY and wait for it to come out of reset.
51 fn phy_reset<S: StationManagement>(sm: &mut S) {
52 sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_RESET);
53 while sm.smi_read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}
54 }
55
56 /// PHY initialisation.
57 fn phy_init<S: StationManagement>(sm: &mut S) {
58 // Clear WU CSR
59 Self::smi_write_ext(sm, PHY_REG_WUCSR, 0);
60
61 // Enable auto-negotiation
62 sm.smi_write(
63 PHY_REG_BCR,
64 PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M,
65 );
66 }
67
68 fn poll_link<S: StationManagement>(sm: &mut S) -> bool {
69 let bsr = sm.smi_read(PHY_REG_BSR);
70 let ssr = sm.smi_read(PHY_REG_SSR);
71
72 // No link without autonegotiate
73 if bsr & PHY_REG_BSR_ANDONE == 0 {
74 return false;
75 }
76 // No link if link is down
77 if bsr & PHY_REG_BSR_UP == 0 {
78 return false;
79 }
80 // No link if autonegotiate incomplete
81 if ssr & PHY_REG_SSR_ANDONE == 0 {
82 return false;
83 }
84 // No link if other side isn't 100Mbps full duplex
85 if ssr & PHY_REG_SSR_SPEED != PHY_REG_SSR_100BASE_FD {
86 return false;
87 }
88
89 // Got link
90 true
91 }
92}
93
94/// Public functions for the LAN8742A
95impl LAN8742A {
96 // Writes a value to an extended PHY register in MMD address space
97 fn smi_write_ext<S: StationManagement>(sm: &mut S, reg_addr: u16, reg_data: u16) {
98 sm.smi_write(PHY_REG_CTL, 0x0003); // set address
99 sm.smi_write(PHY_REG_ADDAR, reg_addr);
100 sm.smi_write(PHY_REG_CTL, 0x4003); // set data
101 sm.smi_write(PHY_REG_ADDAR, reg_data);
102 }
103}
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs
index 8791e1552..e41ebf4d0 100644
--- a/embassy-stm32/src/eth/mod.rs
+++ b/embassy-stm32/src/eth/mod.rs
@@ -3,5 +3,32 @@
3#[cfg_attr(eth_v1, path = "v1.rs")] 3#[cfg_attr(eth_v1, path = "v1.rs")]
4#[cfg_attr(eth_v2, path = "v2/mod.rs")] 4#[cfg_attr(eth_v2, path = "v2/mod.rs")]
5mod _version; 5mod _version;
6pub mod lan8742a;
6 7
7pub use _version::*; 8pub use _version::*;
9
10/// Station Management Interface (SMI) on an ethernet PHY
11///
12/// # Safety
13///
14/// The methods cannot move out of self
15pub unsafe trait StationManagement {
16 /// Read a register over SMI.
17 fn smi_read(&mut self, reg: u8) -> u16;
18 /// Write a register over SMI.
19 fn smi_write(&mut self, reg: u8, val: u16);
20}
21
22/// Traits for an Ethernet PHY
23///
24/// # Safety
25///
26/// The methods cannot move S
27pub unsafe trait PHY {
28 /// Reset PHY and wait for it to come out of reset.
29 fn phy_reset<S: StationManagement>(sm: &mut S);
30 /// PHY initialisation.
31 fn phy_init<S: StationManagement>(sm: &mut S);
32 /// Poll link to see if it is up and FD with 100Mbps
33 fn poll_link<S: StationManagement>(sm: &mut S) -> bool;
34}
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs
index d54c6e859..67d722d15 100644
--- a/embassy-stm32/src/eth/v2/mod.rs
+++ b/embassy-stm32/src/eth/v2/mod.rs
@@ -14,32 +14,21 @@ use crate::interrupt::Interrupt;
14use crate::pac::gpio::vals::Ospeedr; 14use crate::pac::gpio::vals::Ospeedr;
15use crate::pac::ETH; 15use crate::pac::ETH;
16use crate::peripherals; 16use crate::peripherals;
17use crate::time::Hertz;
17 18
18mod descriptors; 19mod descriptors;
20use super::{StationManagement, PHY};
19use descriptors::DescriptorRing; 21use descriptors::DescriptorRing;
20 22
21/// Station Management Interface (SMI) on an ethernet PHY 23pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> {
22pub trait StationManagement {
23 /// Read a register over SMI.
24 fn smi_read(&mut self, reg: u8) -> u16;
25 /// Write a register over SMI.
26 fn smi_write(&mut self, reg: u8, val: u16);
27}
28
29/// Traits for an Ethernet PHY
30pub trait PHY {
31 /// Reset PHY and wait for it to come out of reset.
32 fn phy_reset(&mut self);
33 /// PHY initialisation.
34 fn phy_init(&mut self);
35}
36
37pub struct Ethernet<'d, T: Instance, const TX: usize, const RX: usize> {
38 state: PeripheralMutex<Inner<'d, T, TX, RX>>, 24 state: PeripheralMutex<Inner<'d, T, TX, RX>>,
39 pins: [AnyPin; 9], 25 pins: [AnyPin; 9],
26 _phy: P,
27 clock_range: u8,
28 phy_addr: u8,
40} 29}
41 30
42impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX> { 31impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> {
43 pub fn new( 32 pub fn new(
44 peri: impl Unborrow<Target = T> + 'd, 33 peri: impl Unborrow<Target = T> + 'd,
45 interrupt: impl Unborrow<Target = T::Interrupt> + 'd, 34 interrupt: impl Unborrow<Target = T::Interrupt> + 'd,
@@ -52,7 +41,10 @@ impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX>
52 tx_d0: impl Unborrow<Target = impl TXD0Pin<T>> + 'd, 41 tx_d0: impl Unborrow<Target = impl TXD0Pin<T>> + 'd,
53 tx_d1: impl Unborrow<Target = impl TXD1Pin<T>> + 'd, 42 tx_d1: impl Unborrow<Target = impl TXD1Pin<T>> + 'd,
54 tx_en: impl Unborrow<Target = impl TXEnPin<T>> + 'd, 43 tx_en: impl Unborrow<Target = impl TXEnPin<T>> + 'd,
44 phy: P,
55 mac_addr: [u8; 6], 45 mac_addr: [u8; 6],
46 hclk: Hertz,
47 phy_addr: u8,
56 ) -> Self { 48 ) -> Self {
57 unborrow!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); 49 unborrow!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
58 50
@@ -116,6 +108,20 @@ impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX>
116 }); 108 });
117 } 109 }
118 110
111 // Set the MDC clock frequency in the range 1MHz - 2.5MHz
112 let hclk_mhz = hclk.0 / 1_000_000;
113 let clock_range = match hclk_mhz {
114 0..=34 => 2, // Divide by 16
115 35..=59 => 3, // Divide by 26
116 60..=99 => 0, // Divide by 42
117 100..=149 => 1, // Divide by 62
118 150..=249 => 4, // Divide by 102
119 250..=310 => 5, // Divide by 124
120 _ => {
121 panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
122 }
123 };
124
119 let pins = [ 125 let pins = [
120 ref_clk.degrade(), 126 ref_clk.degrade(),
121 mdio.degrade(), 127 mdio.degrade(),
@@ -128,7 +134,13 @@ impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX>
128 tx_en.degrade(), 134 tx_en.degrade(),
129 ]; 135 ];
130 136
131 Self { state, pins } 137 Self {
138 state,
139 pins,
140 _phy: phy,
141 clock_range,
142 phy_addr,
143 }
132 } 144 }
133 145
134 pub fn init(self: Pin<&mut Self>) { 146 pub fn init(self: Pin<&mut Self>) {
@@ -163,10 +175,52 @@ impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX>
163 }); 175 });
164 } 176 }
165 }); 177 });
178 P::phy_reset(this);
179 P::phy_init(this);
180 }
181}
182
183unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement
184 for Ethernet<'d, T, P, TX, RX>
185{
186 fn smi_read(&mut self, reg: u8) -> u16 {
187 // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self`
188 unsafe {
189 let mac = ETH.ethernet_mac();
190
191 mac.macmdioar().modify(|w| {
192 w.set_pa(self.phy_addr);
193 w.set_rda(reg);
194 w.set_goc(0b11); // read
195 w.set_cr(self.clock_range);
196 w.set_mb(true);
197 });
198 while mac.macmdioar().read().mb() {}
199 mac.macmdiodr().read().md()
200 }
201 }
202
203 fn smi_write(&mut self, reg: u8, val: u16) {
204 // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self`
205 unsafe {
206 let mac = ETH.ethernet_mac();
207
208 mac.macmdiodr().write(|w| w.set_md(val));
209 mac.macmdioar().modify(|w| {
210 w.set_pa(self.phy_addr);
211 w.set_rda(reg);
212 w.set_goc(0b01); // write
213 w.set_cr(self.clock_range);
214 w.set_mb(true);
215 });
216 while mac.macmdioar().read().mb() {}
217 }
166 } 218 }
167} 219}
168 220
169impl<'d, T: Instance, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, TX, RX> { 221impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop
222 for Ethernet<'d, T, P, TX, RX>
223{
170 fn drop(&mut self) { 224 fn drop(&mut self) {
171 for pin in self.pins.iter_mut() { 225 for pin in self.pins.iter_mut() {
172 // NOTE(unsafe) Exclusive access to the regs 226 // NOTE(unsafe) Exclusive access to the regs