aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/eth
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/eth')
-rw-r--r--embassy-stm32/src/eth/generic_phy.rs64
-rw-r--r--embassy-stm32/src/eth/mod.rs29
-rw-r--r--embassy-stm32/src/eth/sma/mod.rs42
-rw-r--r--embassy-stm32/src/eth/sma/v1.rs102
-rw-r--r--embassy-stm32/src/eth/sma/v2.rs94
-rw-r--r--embassy-stm32/src/eth/v1/mod.rs265
-rw-r--r--embassy-stm32/src/eth/v1/rx_desc.rs2
-rw-r--r--embassy-stm32/src/eth/v1/tx_desc.rs2
-rw-r--r--embassy-stm32/src/eth/v2/descriptors.rs2
-rw-r--r--embassy-stm32/src/eth/v2/mod.rs201
10 files changed, 496 insertions, 307 deletions
diff --git a/embassy-stm32/src/eth/generic_phy.rs b/embassy-stm32/src/eth/generic_phy.rs
index 774beef80..0a5f41de0 100644
--- a/embassy-stm32/src/eth/generic_phy.rs
+++ b/embassy-stm32/src/eth/generic_phy.rs
@@ -8,6 +8,7 @@ use embassy_time::{Duration, Timer};
8use futures_util::FutureExt; 8use futures_util::FutureExt;
9 9
10use super::{Phy, StationManagement}; 10use super::{Phy, StationManagement};
11use crate::block_for_us as blocking_delay_us;
11 12
12#[allow(dead_code)] 13#[allow(dead_code)]
13mod phy_consts { 14mod phy_consts {
@@ -43,21 +44,23 @@ mod phy_consts {
43use self::phy_consts::*; 44use self::phy_consts::*;
44 45
45/// Generic SMI Ethernet PHY implementation 46/// Generic SMI Ethernet PHY implementation
46pub struct GenericPhy { 47pub struct GenericPhy<SM: StationManagement> {
47 phy_addr: u8, 48 phy_addr: u8,
49 sm: SM,
48 #[cfg(feature = "time")] 50 #[cfg(feature = "time")]
49 poll_interval: Duration, 51 poll_interval: Duration,
50} 52}
51 53
52impl GenericPhy { 54impl<SM: StationManagement> GenericPhy<SM> {
53 /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication 55 /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication
54 /// 56 ///
55 /// # Panics 57 /// # Panics
56 /// `phy_addr` must be in range `0..32` 58 /// `phy_addr` must be in range `0..32`
57 pub fn new(phy_addr: u8) -> Self { 59 pub fn new(sm: SM, phy_addr: u8) -> Self {
58 assert!(phy_addr < 32); 60 assert!(phy_addr < 32);
59 Self { 61 Self {
60 phy_addr, 62 phy_addr,
63 sm,
61 #[cfg(feature = "time")] 64 #[cfg(feature = "time")]
62 poll_interval: Duration::from_millis(500), 65 poll_interval: Duration::from_millis(500),
63 } 66 }
@@ -67,8 +70,9 @@ impl GenericPhy {
67 /// 70 ///
68 /// # Panics 71 /// # Panics
69 /// Initialization panics if PHY didn't respond on any address 72 /// Initialization panics if PHY didn't respond on any address
70 pub fn new_auto() -> Self { 73 pub fn new_auto(sm: SM) -> Self {
71 Self { 74 Self {
75 sm,
72 phy_addr: 0xFF, 76 phy_addr: 0xFF,
73 #[cfg(feature = "time")] 77 #[cfg(feature = "time")]
74 poll_interval: Duration::from_millis(500), 78 poll_interval: Duration::from_millis(500),
@@ -76,27 +80,14 @@ impl GenericPhy {
76 } 80 }
77} 81}
78 82
79// TODO: Factor out to shared functionality 83impl<SM: StationManagement> Phy for GenericPhy<SM> {
80fn blocking_delay_us(us: u32) { 84 fn phy_reset(&mut self) {
81 #[cfg(feature = "time")]
82 embassy_time::block_for(Duration::from_micros(us as u64));
83 #[cfg(not(feature = "time"))]
84 {
85 let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64;
86 let us = us as u64;
87 let cycles = freq * us / 1_000_000;
88 cortex_m::asm::delay(cycles as u32);
89 }
90}
91
92impl Phy for GenericPhy {
93 fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) {
94 // Detect SMI address 85 // Detect SMI address
95 if self.phy_addr == 0xFF { 86 if self.phy_addr == 0xFF {
96 for addr in 0..32 { 87 for addr in 0..32 {
97 sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); 88 self.sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET);
98 for _ in 0..10 { 89 for _ in 0..10 {
99 if sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { 90 if self.sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET {
100 trace!("Found ETH PHY on address {}", addr); 91 trace!("Found ETH PHY on address {}", addr);
101 self.phy_addr = addr; 92 self.phy_addr = addr;
102 return; 93 return;
@@ -108,30 +99,30 @@ impl Phy for GenericPhy {
108 panic!("PHY did not respond"); 99 panic!("PHY did not respond");
109 } 100 }
110 101
111 sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); 102 self.sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET);
112 while sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} 103 while self.sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}
113 } 104 }
114 105
115 fn phy_init<S: StationManagement>(&mut self, sm: &mut S) { 106 fn phy_init(&mut self) {
116 // Clear WU CSR 107 // Clear WU CSR
117 self.smi_write_ext(sm, PHY_REG_WUCSR, 0); 108 self.smi_write_ext(PHY_REG_WUCSR, 0);
118 109
119 // Enable auto-negotiation 110 // Enable auto-negotiation
120 sm.smi_write( 111 self.sm.smi_write(
121 self.phy_addr, 112 self.phy_addr,
122 PHY_REG_BCR, 113 PHY_REG_BCR,
123 PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, 114 PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M,
124 ); 115 );
125 } 116 }
126 117
127 fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool { 118 fn poll_link(&mut self, cx: &mut Context) -> bool {
128 #[cfg(not(feature = "time"))] 119 #[cfg(not(feature = "time"))]
129 cx.waker().wake_by_ref(); 120 cx.waker().wake_by_ref();
130 121
131 #[cfg(feature = "time")] 122 #[cfg(feature = "time")]
132 let _ = Timer::after(self.poll_interval).poll_unpin(cx); 123 let _ = Timer::after(self.poll_interval).poll_unpin(cx);
133 124
134 let bsr = sm.smi_read(self.phy_addr, PHY_REG_BSR); 125 let bsr = self.sm.smi_read(self.phy_addr, PHY_REG_BSR);
135 126
136 // No link without autonegotiate 127 // No link without autonegotiate
137 if bsr & PHY_REG_BSR_ANDONE == 0 { 128 if bsr & PHY_REG_BSR_ANDONE == 0 {
@@ -148,7 +139,7 @@ impl Phy for GenericPhy {
148} 139}
149 140
150/// Public functions for the PHY 141/// Public functions for the PHY
151impl GenericPhy { 142impl<SM: StationManagement> GenericPhy<SM> {
152 /// Set the SMI polling interval. 143 /// Set the SMI polling interval.
153 #[cfg(feature = "time")] 144 #[cfg(feature = "time")]
154 pub fn set_poll_interval(&mut self, poll_interval: Duration) { 145 pub fn set_poll_interval(&mut self, poll_interval: Duration) {
@@ -156,10 +147,15 @@ impl GenericPhy {
156 } 147 }
157 148
158 // Writes a value to an extended PHY register in MMD address space 149 // Writes a value to an extended PHY register in MMD address space
159 fn smi_write_ext<S: StationManagement>(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) { 150 fn smi_write_ext(&mut self, reg_addr: u16, reg_data: u16) {
160 sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address 151 self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address
161 sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); 152 self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr);
162 sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data 153 self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data
163 sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); 154 self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data);
155 }
156
157 /// Access the underlying station management.
158 pub fn station_management(&mut self) -> &mut SM {
159 &mut self.sm
164 } 160 }
165} 161}
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs
index 10b3a0517..c8bce0e8a 100644
--- a/embassy-stm32/src/eth/mod.rs
+++ b/embassy-stm32/src/eth/mod.rs
@@ -5,6 +5,7 @@
5#[cfg_attr(eth_v2, path = "v2/mod.rs")] 5#[cfg_attr(eth_v2, path = "v2/mod.rs")]
6mod _version; 6mod _version;
7mod generic_phy; 7mod generic_phy;
8mod sma;
8 9
9use core::mem::MaybeUninit; 10use core::mem::MaybeUninit;
10use core::task::Context; 11use core::task::Context;
@@ -15,6 +16,7 @@ use embassy_sync::waitqueue::AtomicWaker;
15 16
16pub use self::_version::{InterruptHandler, *}; 17pub use self::_version::{InterruptHandler, *};
17pub use self::generic_phy::*; 18pub use self::generic_phy::*;
19pub use self::sma::{Sma, StationManagement};
18use crate::rcc::RccPeripheral; 20use crate::rcc::RccPeripheral;
19 21
20#[allow(unused)] 22#[allow(unused)]
@@ -109,7 +111,7 @@ impl<'d, T: Instance, P: Phy> embassy_net_driver::Driver for Ethernet<'d, T, P>
109 } 111 }
110 112
111 fn link_state(&mut self, cx: &mut Context) -> LinkState { 113 fn link_state(&mut self, cx: &mut Context) -> LinkState {
112 if self.phy.poll_link(&mut self.station_management, cx) { 114 if self.phy.poll_link(cx) {
113 LinkState::Up 115 LinkState::Up
114 } else { 116 } else {
115 LinkState::Down 117 LinkState::Down
@@ -157,32 +159,17 @@ impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> {
157 } 159 }
158} 160}
159 161
160/// Station Management Interface (SMI) on an ethernet PHY
161pub trait StationManagement {
162 /// Read a register over SMI.
163 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16;
164 /// Write a register over SMI.
165 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16);
166}
167
168/// Trait for an Ethernet PHY 162/// Trait for an Ethernet PHY
169pub trait Phy { 163pub trait Phy {
170 /// Reset PHY and wait for it to come out of reset. 164 /// Reset PHY and wait for it to come out of reset.
171 fn phy_reset<S: StationManagement>(&mut self, sm: &mut S); 165 fn phy_reset(&mut self);
172 /// PHY initialisation. 166 /// PHY initialisation.
173 fn phy_init<S: StationManagement>(&mut self, sm: &mut S); 167 fn phy_init(&mut self);
174 /// Poll link to see if it is up and FD with 100Mbps 168 /// Poll link to see if it is up and FD with 100Mbps
175 fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool; 169 fn poll_link(&mut self, cx: &mut Context) -> bool;
176} 170}
177 171
178impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { 172impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
179 /// Directly expose the SMI interface used by the Ethernet driver.
180 ///
181 /// This can be used to for example configure special PHY registers for compliance testing.
182 pub fn station_management(&mut self) -> &mut impl StationManagement {
183 &mut self.station_management
184 }
185
186 /// Access the user-supplied `Phy`. 173 /// Access the user-supplied `Phy`.
187 pub fn phy(&self) -> &P { 174 pub fn phy(&self) -> &P {
188 &self.phy 175 &self.phy
@@ -212,8 +199,8 @@ impl Instance for crate::peripherals::ETH {}
212pin_trait!(RXClkPin, Instance, @A); 199pin_trait!(RXClkPin, Instance, @A);
213pin_trait!(TXClkPin, Instance, @A); 200pin_trait!(TXClkPin, Instance, @A);
214pin_trait!(RefClkPin, Instance, @A); 201pin_trait!(RefClkPin, Instance, @A);
215pin_trait!(MDIOPin, Instance, @A); 202pin_trait!(MDIOPin, sma::Instance, @A);
216pin_trait!(MDCPin, Instance, @A); 203pin_trait!(MDCPin, sma::Instance, @A);
217pin_trait!(RXDVPin, Instance, @A); 204pin_trait!(RXDVPin, Instance, @A);
218pin_trait!(CRSPin, Instance, @A); 205pin_trait!(CRSPin, Instance, @A);
219pin_trait!(RXD0Pin, Instance, @A); 206pin_trait!(RXD0Pin, Instance, @A);
diff --git a/embassy-stm32/src/eth/sma/mod.rs b/embassy-stm32/src/eth/sma/mod.rs
new file mode 100644
index 000000000..6c851911d
--- /dev/null
+++ b/embassy-stm32/src/eth/sma/mod.rs
@@ -0,0 +1,42 @@
1//! Station Management Agent (also known as MDIO or SMI).
2
3#![macro_use]
4
5#[cfg_attr(eth_v2, path = "v2.rs")]
6#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1.rs")]
7mod _version;
8
9use embassy_hal_internal::PeripheralType;
10use stm32_metapac::common::{RW, Reg};
11
12pub use self::_version::*;
13
14/// Station Management Interface (SMI).
15pub trait StationManagement {
16 /// Read a register over SMI.
17 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16;
18 /// Write a register over SMI.
19 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16);
20}
21
22trait SealedInstance {
23 fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>);
24}
25
26/// MDIO instance.
27#[allow(private_bounds)]
28pub trait Instance: SealedInstance + PeripheralType + Send + 'static {}
29
30impl SealedInstance for crate::peripherals::ETH_SMA {
31 fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>) {
32 let mac = crate::pac::ETH.ethernet_mac();
33
34 #[cfg(any(eth_v1a, eth_v1b, eth_v1c))]
35 return (mac.macmiiar(), mac.macmiidr());
36
37 #[cfg(eth_v2)]
38 return (mac.macmdioar(), mac.macmdiodr());
39 }
40}
41
42impl Instance for crate::peripherals::ETH_SMA {}
diff --git a/embassy-stm32/src/eth/sma/v1.rs b/embassy-stm32/src/eth/sma/v1.rs
new file mode 100644
index 000000000..db64a6c78
--- /dev/null
+++ b/embassy-stm32/src/eth/sma/v1.rs
@@ -0,0 +1,102 @@
1use embassy_hal_internal::Peri;
2pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister};
3use stm32_metapac::eth::regs;
4use stm32_metapac::eth::vals::{Cr, MbProgress, Mw};
5
6use super::{Instance, StationManagement};
7use crate::eth::{MDCPin, MDIOPin};
8use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
9
10/// Station Management Agent.
11///
12/// This peripheral is used for SMI reads and writes to the connected
13/// ethernet PHY/device(s).
14pub struct Sma<'d, T: Instance> {
15 _peri: Peri<'d, T>,
16 clock_range: Cr,
17 pins: [Peri<'d, AnyPin>; 2],
18}
19
20impl<'d, T: Instance> Sma<'d, T> {
21 /// Create a new instance of this peripheral.
22 pub fn new<#[cfg(afio)] A>(
23 peri: Peri<'d, T>,
24 mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>,
25 mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>,
26 ) -> Self {
27 set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh));
28 set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh));
29
30 // Enable necessary clocks.
31 critical_section::with(|_| {
32 #[cfg(eth_v1a)]
33 let reg = crate::pac::RCC.ahbenr();
34
35 #[cfg(any(eth_v1b, eth_v1c))]
36 let reg = crate::pac::RCC.ahb1enr();
37
38 reg.modify(|w| {
39 w.set_ethen(true);
40 })
41 });
42
43 let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() };
44 let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not.");
45 let hclk_mhz = hclk.0 / 1_000_000;
46
47 // Set the MDC clock frequency in the range 1MHz - 2.5MHz
48 let clock_range = match hclk_mhz {
49 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."),
50 25..=34 => Cr::CR_20_35, // Divide by 16
51 35..=59 => Cr::CR_35_60, // Divide by 26
52 60..=99 => Cr::CR_60_100, // Divide by 42
53 100..=149 => Cr::CR_100_150, // Divide by 62
54 150..=216 => Cr::CR_150_168, // Divide by 102
55 _ => {
56 panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
57 }
58 };
59
60 Self {
61 _peri: peri,
62 clock_range,
63 pins: [mdio.into(), mdc.into()],
64 }
65 }
66}
67
68impl<T: Instance> StationManagement for Sma<'_, T> {
69 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
70 let (macmiiar, macmiidr) = T::regs();
71
72 macmiiar.modify(|w| {
73 w.set_pa(phy_addr);
74 w.set_mr(reg);
75 w.set_mw(Mw::READ); // read operation
76 w.set_cr(self.clock_range);
77 w.set_mb(MbProgress::BUSY); // indicate that operation is in progress
78 });
79 while macmiiar.read().mb() == MbProgress::BUSY {}
80 macmiidr.read().md()
81 }
82
83 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
84 let (macmiiar, macmiidr) = T::regs();
85
86 macmiidr.write(|w| w.set_md(val));
87 macmiiar.modify(|w| {
88 w.set_pa(phy_addr);
89 w.set_mr(reg);
90 w.set_mw(Mw::WRITE); // write
91 w.set_cr(self.clock_range);
92 w.set_mb(MbProgress::BUSY);
93 });
94 while macmiiar.read().mb() == MbProgress::BUSY {}
95 }
96}
97
98impl<T: Instance> Drop for Sma<'_, T> {
99 fn drop(&mut self) {
100 self.pins.iter_mut().for_each(|p| p.set_as_disconnected());
101 }
102}
diff --git a/embassy-stm32/src/eth/sma/v2.rs b/embassy-stm32/src/eth/sma/v2.rs
new file mode 100644
index 000000000..6bc5230b5
--- /dev/null
+++ b/embassy-stm32/src/eth/sma/v2.rs
@@ -0,0 +1,94 @@
1use embassy_hal_internal::Peri;
2pub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister};
3use stm32_metapac::eth::regs;
4
5use super::{Instance, StationManagement};
6use crate::eth::{MDCPin, MDIOPin};
7use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
8
9/// Station Management Agent.
10///
11/// This peripheral is used for SMI reads and writes to the connected
12/// ethernet PHY/device(s).
13pub struct Sma<'d, T: Instance> {
14 _peri: Peri<'d, T>,
15 pins: [Peri<'d, AnyPin>; 2],
16 clock_range: u8,
17}
18
19impl<'d, T: Instance> Sma<'d, T> {
20 /// Create a new instance of this peripheral.
21 pub fn new(peri: Peri<'d, T>, mdio: Peri<'d, impl MDIOPin<T>>, mdc: Peri<'d, impl MDCPin<T>>) -> Self {
22 set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh));
23 set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh));
24
25 // Enable necessary clocks.
26 critical_section::with(|_| {
27 crate::pac::RCC.ahb1enr().modify(|w| {
28 w.set_ethen(true);
29 })
30 });
31
32 let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() };
33 let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not.");
34 let hclk_mhz = hclk.0 / 1_000_000;
35
36 // Set the MDC clock frequency in the range 1MHz - 2.5MHz
37 let clock_range = match hclk_mhz {
38 0..=34 => 2, // Divide by 16
39 35..=59 => 3, // Divide by 26
40 60..=99 => 0, // Divide by 42
41 100..=149 => 1, // Divide by 62
42 150..=249 => 4, // Divide by 102
43 250..=310 => 5, // Divide by 124
44 _ => {
45 panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
46 }
47 };
48
49 Self {
50 _peri: peri,
51 clock_range,
52 pins: [mdio.into(), mdc.into()],
53 }
54 }
55}
56
57impl<T: Instance> StationManagement for Sma<'_, T> {
58 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
59 let (macmdioar, macmdiodr) = T::regs();
60
61 macmdioar.modify(|w| {
62 w.set_pa(phy_addr);
63 w.set_rda(reg);
64 w.set_goc(0b11); // read
65 w.set_cr(self.clock_range);
66 w.set_mb(true);
67 });
68
69 while macmdioar.read().mb() {}
70
71 macmdiodr.read().md()
72 }
73
74 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
75 let (macmdioar, macmdiodr) = T::regs();
76
77 macmdiodr.write(|w| w.set_md(val));
78 macmdioar.modify(|w| {
79 w.set_pa(phy_addr);
80 w.set_rda(reg);
81 w.set_goc(0b01); // write
82 w.set_cr(self.clock_range);
83 w.set_mb(true);
84 });
85
86 while macmdioar.read().mb() {}
87 }
88}
89
90impl<T: Instance> Drop for Sma<'_, T> {
91 fn drop(&mut self) {
92 self.pins.iter_mut().for_each(|p| p.set_as_disconnected());
93 }
94}
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs
index 5be1c9739..8de26ce9d 100644
--- a/embassy-stm32/src/eth/v1/mod.rs
+++ b/embassy-stm32/src/eth/v1/mod.rs
@@ -3,11 +3,10 @@
3mod rx_desc; 3mod rx_desc;
4mod tx_desc; 4mod tx_desc;
5 5
6use core::marker::PhantomData; 6use core::sync::atomic::{Ordering, fence};
7use core::sync::atomic::{fence, Ordering};
8 7
9use embassy_hal_internal::Peri; 8use embassy_hal_internal::Peri;
10use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; 9use stm32_metapac::eth::vals::{Apcs, Dm, DmaomrSr, Fes, Ftf, Ifg, Pbl, Rsf, St, Tsf};
11 10
12pub(crate) use self::rx_desc::{RDes, RDesRing}; 11pub(crate) use self::rx_desc::{RDes, RDesRing};
13pub(crate) use self::tx_desc::{TDes, TDesRing}; 12pub(crate) use self::tx_desc::{TDes, TDesRing};
@@ -22,7 +21,6 @@ use crate::pac::AFIO;
22#[cfg(any(eth_v1b, eth_v1c))] 21#[cfg(any(eth_v1b, eth_v1c))]
23use crate::pac::SYSCFG; 22use crate::pac::SYSCFG;
24use crate::pac::{ETH, RCC}; 23use crate::pac::{ETH, RCC};
25use crate::rcc::SealedRccPeripheral;
26 24
27/// Interrupt handler. 25/// Interrupt handler.
28pub struct InterruptHandler {} 26pub struct InterruptHandler {}
@@ -53,14 +51,13 @@ pub struct Ethernet<'d, T: Instance, P: Phy> {
53 51
54 pins: Pins<'d>, 52 pins: Pins<'d>,
55 pub(crate) phy: P, 53 pub(crate) phy: P,
56 pub(crate) station_management: EthernetStationManagement<T>,
57 pub(crate) mac_addr: [u8; 6], 54 pub(crate) mac_addr: [u8; 6],
58} 55}
59 56
60/// Pins of ethernet driver. 57/// Pins of ethernet driver.
61enum Pins<'d> { 58enum Pins<'d> {
62 Rmii([Peri<'d, AnyPin>; 9]), 59 Rmii([Peri<'d, AnyPin>; 7]),
63 Mii([Peri<'d, AnyPin>; 14]), 60 Mii([Peri<'d, AnyPin>; 12]),
64} 61}
65 62
66#[cfg(eth_v1a)] 63#[cfg(eth_v1a)]
@@ -97,68 +94,105 @@ macro_rules! config_pins {
97 }; 94 };
98} 95}
99 96
100impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { 97impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> {
98 /// Create a new RMII ethernet driver using 7 pins.
99 ///
100 /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the
101 /// provided [`SMA`](sma::Instance), and MDIO and MDC pins.
102 ///
103 /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet
104 /// river with a non-standard PHY.
105 ///
101 /// safety: the returned instance is not leak-safe 106 /// safety: the returned instance is not leak-safe
102 pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>( 107 pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>(
103 queue: &'d mut PacketQueue<TX, RX>, 108 queue: &'d mut PacketQueue<TX, RX>,
104 peri: Peri<'d, T>, 109 peri: Peri<'d, T>,
105 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, 110 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
106 ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>, 111 ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>,
107 mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>,
108 mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>,
109 crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>, 112 crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>,
110 rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, 113 rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,
111 rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, 114 rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,
112 tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>, 115 tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>,
113 tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>, 116 tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>,
114 tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, 117 tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,
115 phy: P,
116 mac_addr: [u8; 6], 118 mac_addr: [u8; 6],
119 sma: Peri<'d, SMA>,
120 mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>,
121 mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>,
117 ) -> Self { 122 ) -> Self {
118 // Enable the necessary Clocks 123 let sma = Sma::new(sma, mdio, mdc);
119 #[cfg(eth_v1a)] 124 let phy = GenericPhy::new_auto(sma);
120 critical_section::with(|_| {
121 RCC.apb2enr().modify(|w| w.set_afioen(true));
122
123 // Select RMII (Reduced Media Independent Interface)
124 // Must be done prior to enabling peripheral clock
125 AFIO.mapr().modify(|w| {
126 w.set_mii_rmii_sel(true);
127 w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP);
128 });
129 125
130 RCC.ahbenr().modify(|w| { 126 Self::new_with_phy(
131 w.set_ethen(true); 127 queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy,
132 w.set_ethtxen(true); 128 )
133 w.set_ethrxen(true); 129 }
134 });
135 });
136 130
137 #[cfg(any(eth_v1b, eth_v1c))] 131 /// Create a new MII ethernet driver using 14 pins.
138 critical_section::with(|_| { 132 ///
139 RCC.ahb1enr().modify(|w| { 133 /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the
140 w.set_ethen(true); 134 /// provided [`SMA`](sma::Instance), and MDIO and MDC pins.
141 w.set_ethtxen(true); 135 ///
142 w.set_ethrxen(true); 136 /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet
143 }); 137 /// river with a non-standard PHY.
138 pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>(
139 queue: &'d mut PacketQueue<TX, RX>,
140 peri: Peri<'d, T>,
141 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
142 rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>,
143 tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>,
144 rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>,
145 rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,
146 rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,
147 rx_d2: Peri<'d, if_afio!(impl RXD2Pin<T, A>)>,
148 rx_d3: Peri<'d, if_afio!(impl RXD3Pin<T, A>)>,
149 tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>,
150 tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>,
151 tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>,
152 tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>,
153 tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,
154 mac_addr: [u8; 6],
155 sma: Peri<'d, SMA>,
156 mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>,
157 mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>,
158 ) -> Self {
159 let sma = Sma::new(sma, mdio, mdc);
160 let phy = GenericPhy::new_auto(sma);
144 161
145 // RMII (Reduced Media Independent Interface) 162 Self::new_mii_with_phy(
146 SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); 163 queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en,
147 }); 164 mac_addr, phy,
165 )
166 }
167}
148 168
169impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
170 /// safety: the returned instance is not leak-safe
171 pub fn new_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>(
172 queue: &'d mut PacketQueue<TX, RX>,
173 peri: Peri<'d, T>,
174 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
175 ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>,
176 crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>,
177 rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,
178 rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,
179 tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>,
180 tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>,
181 tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,
182 mac_addr: [u8; 6],
183 phy: P,
184 ) -> Self {
149 #[cfg(eth_v1a)] 185 #[cfg(eth_v1a)]
150 { 186 {
151 config_in_pins!(ref_clk, rx_d0, rx_d1); 187 config_in_pins!(ref_clk, rx_d0, rx_d1);
152 config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); 188 config_af_pins!(tx_d0, tx_d1, tx_en);
153 } 189 }
154 190
155 #[cfg(any(eth_v1b, eth_v1c))] 191 #[cfg(any(eth_v1b, eth_v1c))]
156 config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); 192 config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
157 193
158 let pins = Pins::Rmii([ 194 let pins = Pins::Rmii([
159 ref_clk.into(), 195 ref_clk.into(),
160 mdio.into(),
161 mdc.into(),
162 crs.into(), 196 crs.into(),
163 rx_d0.into(), 197 rx_d0.into(),
164 rx_d1.into(), 198 rx_d1.into(),
@@ -167,7 +201,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
167 tx_en.into(), 201 tx_en.into(),
168 ]); 202 ]);
169 203
170 Self::new_inner(queue, peri, irq, pins, phy, mac_addr) 204 Self::new_inner(queue, peri, irq, pins, phy, mac_addr, true)
171 } 205 }
172 206
173 fn new_inner<const TX: usize, const RX: usize>( 207 fn new_inner<const TX: usize, const RX: usize>(
@@ -177,7 +211,39 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
177 pins: Pins<'d>, 211 pins: Pins<'d>,
178 phy: P, 212 phy: P,
179 mac_addr: [u8; 6], 213 mac_addr: [u8; 6],
214 rmii_mii_sel: bool,
180 ) -> Self { 215 ) -> Self {
216 // Enable the necessary Clocks
217 #[cfg(eth_v1a)]
218 critical_section::with(|_| {
219 RCC.apb2enr().modify(|w| w.set_afioen(true));
220
221 // Select (R)MII (Reduced Media Independent Interface)
222 // Must be done prior to enabling peripheral clock
223 AFIO.mapr().modify(|w| {
224 w.set_mii_rmii_sel(rmii_mii_sel);
225 w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP);
226 });
227
228 RCC.ahbenr().modify(|w| {
229 w.set_ethen(true);
230 w.set_ethtxen(true);
231 w.set_ethrxen(true);
232 });
233 });
234
235 #[cfg(any(eth_v1b, eth_v1c))]
236 critical_section::with(|_| {
237 RCC.ahb1enr().modify(|w| {
238 w.set_ethen(true);
239 w.set_ethtxen(true);
240 w.set_ethrxen(true);
241 });
242
243 // (R)MII ((Reduced) Media Independent Interface)
244 SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(rmii_mii_sel));
245 });
246
181 let dma = T::regs().ethernet_dma(); 247 let dma = T::regs().ethernet_dma();
182 let mac = T::regs().ethernet_mac(); 248 let mac = T::regs().ethernet_mac();
183 249
@@ -190,7 +256,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
190 w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping 256 w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping
191 w.set_fes(Fes::FES100); // fast ethernet speed 257 w.set_fes(Fes::FES100); // fast ethernet speed
192 w.set_dm(Dm::FULL_DUPLEX); // full duplex 258 w.set_dm(Dm::FULL_DUPLEX); // full duplex
193 // TODO: Carrier sense ? ECRSFD 259 // TODO: Carrier sense ? ECRSFD
194 }); 260 });
195 261
196 // Set the mac to pass all multicast packets 262 // Set the mac to pass all multicast packets
@@ -226,30 +292,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
226 292
227 // TODO MTU size setting not found for v1 ethernet, check if correct 293 // TODO MTU size setting not found for v1 ethernet, check if correct
228 294
229 let hclk = <T as SealedRccPeripheral>::frequency();
230 let hclk_mhz = hclk.0 / 1_000_000;
231
232 // Set the MDC clock frequency in the range 1MHz - 2.5MHz
233 let clock_range = match hclk_mhz {
234 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."),
235 25..=34 => Cr::CR_20_35, // Divide by 16
236 35..=59 => Cr::CR_35_60, // Divide by 26
237 60..=99 => Cr::CR_60_100, // Divide by 42
238 100..=149 => Cr::CR_100_150, // Divide by 62
239 150..=216 => Cr::CR_150_168, // Divide by 102
240 _ => {
241 panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
242 }
243 };
244
245 let mut this = Self { 295 let mut this = Self {
246 _peri: peri, 296 _peri: peri,
247 pins, 297 pins,
248 phy: phy, 298 phy: phy,
249 station_management: EthernetStationManagement {
250 peri: PhantomData,
251 clock_range: clock_range,
252 },
253 mac_addr, 299 mac_addr,
254 tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), 300 tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
255 rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), 301 rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
@@ -279,8 +325,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
279 w.set_tie(true); 325 w.set_tie(true);
280 }); 326 });
281 327
282 this.phy.phy_reset(&mut this.station_management); 328 this.phy.phy_reset();
283 this.phy.phy_init(&mut this.station_management); 329 this.phy.phy_init();
284 330
285 interrupt::ETH.unpend(); 331 interrupt::ETH.unpend();
286 unsafe { interrupt::ETH.enable() }; 332 unsafe { interrupt::ETH.enable() };
@@ -288,15 +334,13 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
288 this 334 this
289 } 335 }
290 336
291 /// Create a new MII ethernet driver using 14 pins. 337 /// Create a new MII ethernet driver using 12 pins.
292 pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>( 338 pub fn new_mii_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>(
293 queue: &'d mut PacketQueue<TX, RX>, 339 queue: &'d mut PacketQueue<TX, RX>,
294 peri: Peri<'d, T>, 340 peri: Peri<'d, T>,
295 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, 341 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
296 rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>, 342 rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>,
297 tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>, 343 tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>,
298 mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>,
299 mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>,
300 rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>, 344 rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>,
301 rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>, 345 rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,
302 rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>, 346 rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,
@@ -307,56 +351,23 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
307 tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>, 351 tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>,
308 tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>, 352 tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>,
309 tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>, 353 tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,
310 phy: P,
311 mac_addr: [u8; 6], 354 mac_addr: [u8; 6],
355 phy: P,
312 ) -> Self { 356 ) -> Self {
313 // TODO: Handle optional signals like CRS, MII_COL, RX_ER?
314
315 // Enable the necessary Clocks
316 #[cfg(eth_v1a)]
317 critical_section::with(|_| {
318 RCC.apb2enr().modify(|w| w.set_afioen(true));
319
320 // Select MII (Media Independent Interface)
321 // Must be done prior to enabling peripheral clock
322 AFIO.mapr().modify(|w| {
323 w.set_mii_rmii_sel(false);
324 w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP);
325 });
326
327 RCC.ahbenr().modify(|w| {
328 w.set_ethen(true);
329 w.set_ethtxen(true);
330 w.set_ethrxen(true);
331 });
332 });
333
334 #[cfg(any(eth_v1b, eth_v1c))]
335 critical_section::with(|_| {
336 RCC.ahb1enr().modify(|w| {
337 w.set_ethen(true);
338 w.set_ethtxen(true);
339 w.set_ethrxen(true);
340 });
341
342 // MII (Media Independent Interface)
343 SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(false));
344 });
345
346 #[cfg(eth_v1a)] 357 #[cfg(eth_v1a)]
347 { 358 {
348 config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); 359 config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv);
349 config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); 360 config_af_pins!(tx_d0, tx_d1, tx_d2, tx_d3, tx_en);
350 } 361 }
351 362
352 #[cfg(any(eth_v1b, eth_v1c))] 363 #[cfg(any(eth_v1b, eth_v1c))]
353 config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); 364 config_pins!(
365 rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en
366 );
354 367
355 let pins = Pins::Mii([ 368 let pins = Pins::Mii([
356 rx_clk.into(), 369 rx_clk.into(),
357 tx_clk.into(), 370 tx_clk.into(),
358 mdio.into(),
359 mdc.into(),
360 rxdv.into(), 371 rxdv.into(),
361 rx_d0.into(), 372 rx_d0.into(),
362 rx_d1.into(), 373 rx_d1.into(),
@@ -369,43 +380,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
369 tx_en.into(), 380 tx_en.into(),
370 ]); 381 ]);
371 382
372 Self::new_inner(queue, peri, irq, pins, phy, mac_addr) 383 Self::new_inner(queue, peri, irq, pins, phy, mac_addr, false)
373 }
374}
375
376/// Ethernet station management interface.
377pub(crate) struct EthernetStationManagement<T: Instance> {
378 peri: PhantomData<T>,
379 clock_range: Cr,
380}
381
382impl<T: Instance> StationManagement for EthernetStationManagement<T> {
383 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
384 let mac = T::regs().ethernet_mac();
385
386 mac.macmiiar().modify(|w| {
387 w.set_pa(phy_addr);
388 w.set_mr(reg);
389 w.set_mw(Mw::READ); // read operation
390 w.set_cr(self.clock_range);
391 w.set_mb(MbProgress::BUSY); // indicate that operation is in progress
392 });
393 while mac.macmiiar().read().mb() == MbProgress::BUSY {}
394 mac.macmiidr().read().md()
395 }
396
397 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
398 let mac = T::regs().ethernet_mac();
399
400 mac.macmiidr().write(|w| w.set_md(val));
401 mac.macmiiar().modify(|w| {
402 w.set_pa(phy_addr);
403 w.set_mr(reg);
404 w.set_mw(Mw::WRITE); // write
405 w.set_cr(self.clock_range);
406 w.set_mb(MbProgress::BUSY);
407 });
408 while mac.macmiiar().read().mb() == MbProgress::BUSY {}
409 } 384 }
410} 385}
411 386
diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs
index 2a46c1895..6ade1f29c 100644
--- a/embassy-stm32/src/eth/v1/rx_desc.rs
+++ b/embassy-stm32/src/eth/v1/rx_desc.rs
@@ -1,4 +1,4 @@
1use core::sync::atomic::{compiler_fence, fence, Ordering}; 1use core::sync::atomic::{Ordering, compiler_fence, fence};
2 2
3use stm32_metapac::eth::vals::{Rpd, Rps}; 3use stm32_metapac::eth::vals::{Rpd, Rps};
4use vcell::VolatileCell; 4use vcell::VolatileCell;
diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs
index 1317d20f4..ba99b66cb 100644
--- a/embassy-stm32/src/eth/v1/tx_desc.rs
+++ b/embassy-stm32/src/eth/v1/tx_desc.rs
@@ -1,4 +1,4 @@
1use core::sync::atomic::{compiler_fence, fence, Ordering}; 1use core::sync::atomic::{Ordering, compiler_fence, fence};
2 2
3use vcell::VolatileCell; 3use vcell::VolatileCell;
4 4
diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs
index 645bfdb14..e335ed8f3 100644
--- a/embassy-stm32/src/eth/v2/descriptors.rs
+++ b/embassy-stm32/src/eth/v2/descriptors.rs
@@ -1,4 +1,4 @@
1use core::sync::atomic::{fence, Ordering}; 1use core::sync::atomic::{Ordering, fence};
2 2
3use vcell::VolatileCell; 3use vcell::VolatileCell;
4 4
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs
index cf7a9901b..7f92e351c 100644
--- a/embassy-stm32/src/eth/v2/mod.rs
+++ b/embassy-stm32/src/eth/v2/mod.rs
@@ -1,7 +1,6 @@
1mod descriptors; 1mod descriptors;
2 2
3use core::marker::PhantomData; 3use core::sync::atomic::{Ordering, fence};
4use core::sync::atomic::{fence, Ordering};
5 4
6use embassy_hal_internal::Peri; 5use embassy_hal_internal::Peri;
7use stm32_metapac::syscfg::vals::EthSelPhy; 6use stm32_metapac::syscfg::vals::EthSelPhy;
@@ -12,7 +11,6 @@ use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed};
12use crate::interrupt; 11use crate::interrupt;
13use crate::interrupt::InterruptExt; 12use crate::interrupt::InterruptExt;
14use crate::pac::ETH; 13use crate::pac::ETH;
15use crate::rcc::SealedRccPeripheral;
16 14
17/// Interrupt handler. 15/// Interrupt handler.
18pub struct InterruptHandler {} 16pub struct InterruptHandler {}
@@ -42,14 +40,13 @@ pub struct Ethernet<'d, T: Instance, P: Phy> {
42 pub(crate) rx: RDesRing<'d>, 40 pub(crate) rx: RDesRing<'d>,
43 pins: Pins<'d>, 41 pins: Pins<'d>,
44 pub(crate) phy: P, 42 pub(crate) phy: P,
45 pub(crate) station_management: EthernetStationManagement<T>,
46 pub(crate) mac_addr: [u8; 6], 43 pub(crate) mac_addr: [u8; 6],
47} 44}
48 45
49/// Pins of ethernet driver. 46/// Pins of ethernet driver.
50enum Pins<'d> { 47enum Pins<'d> {
51 Rmii([Peri<'d, AnyPin>; 9]), 48 Rmii([Peri<'d, AnyPin>; 7]),
52 Mii([Peri<'d, AnyPin>; 14]), 49 Mii([Peri<'d, AnyPin>; 12]),
53} 50}
54 51
55macro_rules! config_pins { 52macro_rules! config_pins {
@@ -63,41 +60,96 @@ macro_rules! config_pins {
63 }; 60 };
64} 61}
65 62
66impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { 63impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> {
67 /// Create a new RMII ethernet driver using 9 pins. 64 /// Create a new RMII ethernet driver using 7 pins.
65 ///
66 /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the
67 /// provided [`SMA`](sma::Instance), and MDIO and MDC pins.
68 ///
69 /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet
70 /// river with a non-standard PHY.
68 pub fn new<const TX: usize, const RX: usize>( 71 pub fn new<const TX: usize, const RX: usize>(
69 queue: &'d mut PacketQueue<TX, RX>, 72 queue: &'d mut PacketQueue<TX, RX>,
70 peri: Peri<'d, T>, 73 peri: Peri<'d, T>,
71 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, 74 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
72 ref_clk: Peri<'d, impl RefClkPin<T>>, 75 ref_clk: Peri<'d, impl RefClkPin<T>>,
73 mdio: Peri<'d, impl MDIOPin<T>>,
74 mdc: Peri<'d, impl MDCPin<T>>,
75 crs: Peri<'d, impl CRSPin<T>>, 76 crs: Peri<'d, impl CRSPin<T>>,
76 rx_d0: Peri<'d, impl RXD0Pin<T>>, 77 rx_d0: Peri<'d, impl RXD0Pin<T>>,
77 rx_d1: Peri<'d, impl RXD1Pin<T>>, 78 rx_d1: Peri<'d, impl RXD1Pin<T>>,
78 tx_d0: Peri<'d, impl TXD0Pin<T>>, 79 tx_d0: Peri<'d, impl TXD0Pin<T>>,
79 tx_d1: Peri<'d, impl TXD1Pin<T>>, 80 tx_d1: Peri<'d, impl TXD1Pin<T>>,
80 tx_en: Peri<'d, impl TXEnPin<T>>, 81 tx_en: Peri<'d, impl TXEnPin<T>>,
81 phy: P,
82 mac_addr: [u8; 6], 82 mac_addr: [u8; 6],
83 sma: Peri<'d, SMA>,
84 mdio: Peri<'d, impl MDIOPin<SMA>>,
85 mdc: Peri<'d, impl MDCPin<SMA>>,
83 ) -> Self { 86 ) -> Self {
84 // Enable the necessary clocks 87 let sma = Sma::new(sma, mdio, mdc);
85 critical_section::with(|_| { 88 let phy = GenericPhy::new_auto(sma);
86 crate::pac::RCC.ahb1enr().modify(|w| {
87 w.set_ethen(true);
88 w.set_ethtxen(true);
89 w.set_ethrxen(true);
90 });
91 89
92 crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(EthSelPhy::RMII)); 90 Self::new_with_phy(
93 }); 91 queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy,
92 )
93 }
94
95 /// Create a new MII ethernet driver using 14 pins.
96 ///
97 /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the
98 /// provided [`SMA`](sma::Instance), and MDIO and MDC pins.
99 ///
100 /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet
101 /// river with a non-standard PHY.
102 pub fn new_mii<const TX: usize, const RX: usize>(
103 queue: &'d mut PacketQueue<TX, RX>,
104 peri: Peri<'d, T>,
105 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
106 rx_clk: Peri<'d, impl RXClkPin<T>>,
107 tx_clk: Peri<'d, impl TXClkPin<T>>,
108 rxdv: Peri<'d, impl RXDVPin<T>>,
109 rx_d0: Peri<'d, impl RXD0Pin<T>>,
110 rx_d1: Peri<'d, impl RXD1Pin<T>>,
111 rx_d2: Peri<'d, impl RXD2Pin<T>>,
112 rx_d3: Peri<'d, impl RXD3Pin<T>>,
113 tx_d0: Peri<'d, impl TXD0Pin<T>>,
114 tx_d1: Peri<'d, impl TXD1Pin<T>>,
115 tx_d2: Peri<'d, impl TXD2Pin<T>>,
116 tx_d3: Peri<'d, impl TXD3Pin<T>>,
117 tx_en: Peri<'d, impl TXEnPin<T>>,
118 mac_addr: [u8; 6],
119 sma: Peri<'d, SMA>,
120 mdio: Peri<'d, impl MDIOPin<SMA>>,
121 mdc: Peri<'d, impl MDCPin<SMA>>,
122 ) -> Self {
123 let sma = Sma::new(sma, mdio, mdc);
124 let phy = GenericPhy::new_auto(sma);
125
126 Self::new_mii_with_phy(
127 queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en,
128 mac_addr, phy,
129 )
130 }
131}
94 132
95 config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); 133impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
134 /// Create a new RMII ethernet driver using 7 pins.
135 pub fn new_with_phy<const TX: usize, const RX: usize>(
136 queue: &'d mut PacketQueue<TX, RX>,
137 peri: Peri<'d, T>,
138 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
139 ref_clk: Peri<'d, impl RefClkPin<T>>,
140 crs: Peri<'d, impl CRSPin<T>>,
141 rx_d0: Peri<'d, impl RXD0Pin<T>>,
142 rx_d1: Peri<'d, impl RXD1Pin<T>>,
143 tx_d0: Peri<'d, impl TXD0Pin<T>>,
144 tx_d1: Peri<'d, impl TXD1Pin<T>>,
145 tx_en: Peri<'d, impl TXEnPin<T>>,
146 mac_addr: [u8; 6],
147 phy: P,
148 ) -> Self {
149 config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
96 150
97 let pins = Pins::Rmii([ 151 let pins = Pins::Rmii([
98 ref_clk.into(), 152 ref_clk.into(),
99 mdio.into(),
100 mdc.into(),
101 crs.into(), 153 crs.into(),
102 rx_d0.into(), 154 rx_d0.into(),
103 rx_d1.into(), 155 rx_d1.into(),
@@ -106,18 +158,16 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
106 tx_en.into(), 158 tx_en.into(),
107 ]); 159 ]);
108 160
109 Self::new_inner(queue, peri, irq, pins, phy, mac_addr) 161 Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::RMII)
110 } 162 }
111 163
112 /// Create a new MII ethernet driver using 14 pins. 164 /// Create a new MII ethernet driver using 12 pins.
113 pub fn new_mii<const TX: usize, const RX: usize>( 165 pub fn new_mii_with_phy<const TX: usize, const RX: usize>(
114 queue: &'d mut PacketQueue<TX, RX>, 166 queue: &'d mut PacketQueue<TX, RX>,
115 peri: Peri<'d, T>, 167 peri: Peri<'d, T>,
116 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, 168 irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,
117 rx_clk: Peri<'d, impl RXClkPin<T>>, 169 rx_clk: Peri<'d, impl RXClkPin<T>>,
118 tx_clk: Peri<'d, impl TXClkPin<T>>, 170 tx_clk: Peri<'d, impl TXClkPin<T>>,
119 mdio: Peri<'d, impl MDIOPin<T>>,
120 mdc: Peri<'d, impl MDCPin<T>>,
121 rxdv: Peri<'d, impl RXDVPin<T>>, 171 rxdv: Peri<'d, impl RXDVPin<T>>,
122 rx_d0: Peri<'d, impl RXD0Pin<T>>, 172 rx_d0: Peri<'d, impl RXD0Pin<T>>,
123 rx_d1: Peri<'d, impl RXD1Pin<T>>, 173 rx_d1: Peri<'d, impl RXD1Pin<T>>,
@@ -128,29 +178,16 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
128 tx_d2: Peri<'d, impl TXD2Pin<T>>, 178 tx_d2: Peri<'d, impl TXD2Pin<T>>,
129 tx_d3: Peri<'d, impl TXD3Pin<T>>, 179 tx_d3: Peri<'d, impl TXD3Pin<T>>,
130 tx_en: Peri<'d, impl TXEnPin<T>>, 180 tx_en: Peri<'d, impl TXEnPin<T>>,
131 phy: P,
132 mac_addr: [u8; 6], 181 mac_addr: [u8; 6],
182 phy: P,
133 ) -> Self { 183 ) -> Self {
134 // Enable the necessary clocks 184 config_pins!(
135 critical_section::with(|_| { 185 rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en
136 crate::pac::RCC.ahb1enr().modify(|w| { 186 );
137 w.set_ethen(true);
138 w.set_ethtxen(true);
139 w.set_ethrxen(true);
140 });
141
142 crate::pac::SYSCFG
143 .pmcr()
144 .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII));
145 });
146
147 config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en);
148 187
149 let pins = Pins::Mii([ 188 let pins = Pins::Mii([
150 rx_clk.into(), 189 rx_clk.into(),
151 tx_clk.into(), 190 tx_clk.into(),
152 mdio.into(),
153 mdc.into(),
154 rxdv.into(), 191 rxdv.into(),
155 rx_d0.into(), 192 rx_d0.into(),
156 rx_d1.into(), 193 rx_d1.into(),
@@ -163,7 +200,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
163 tx_en.into(), 200 tx_en.into(),
164 ]); 201 ]);
165 202
166 Self::new_inner(queue, peri, irq, pins, phy, mac_addr) 203 Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::MII_GMII)
167 } 204 }
168 205
169 fn new_inner<const TX: usize, const RX: usize>( 206 fn new_inner<const TX: usize, const RX: usize>(
@@ -173,7 +210,19 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
173 pins: Pins<'d>, 210 pins: Pins<'d>,
174 phy: P, 211 phy: P,
175 mac_addr: [u8; 6], 212 mac_addr: [u8; 6],
213 eth_sel_phy: EthSelPhy,
176 ) -> Self { 214 ) -> Self {
215 // Enable the necessary clocks
216 critical_section::with(|_| {
217 crate::pac::RCC.ahb1enr().modify(|w| {
218 w.set_ethen(true);
219 w.set_ethtxen(true);
220 w.set_ethrxen(true);
221 });
222
223 crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(eth_sel_phy));
224 });
225
177 let dma = T::regs().ethernet_dma(); 226 let dma = T::regs().ethernet_dma();
178 let mac = T::regs().ethernet_mac(); 227 let mac = T::regs().ethernet_mac();
179 let mtl = T::regs().ethernet_mtl(); 228 let mtl = T::regs().ethernet_mtl();
@@ -235,32 +284,12 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
235 w.set_rbsz(RX_BUFFER_SIZE as u16); 284 w.set_rbsz(RX_BUFFER_SIZE as u16);
236 }); 285 });
237 286
238 let hclk = <T as SealedRccPeripheral>::frequency();
239 let hclk_mhz = hclk.0 / 1_000_000;
240
241 // Set the MDC clock frequency in the range 1MHz - 2.5MHz
242 let clock_range = match hclk_mhz {
243 0..=34 => 2, // Divide by 16
244 35..=59 => 3, // Divide by 26
245 60..=99 => 0, // Divide by 42
246 100..=149 => 1, // Divide by 62
247 150..=249 => 4, // Divide by 102
248 250..=310 => 5, // Divide by 124
249 _ => {
250 panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
251 }
252 };
253
254 let mut this = Self { 287 let mut this = Self {
255 _peri: peri, 288 _peri: peri,
256 tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), 289 tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
257 rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), 290 rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
258 pins, 291 pins,
259 phy, 292 phy,
260 station_management: EthernetStationManagement {
261 peri: PhantomData,
262 clock_range: clock_range,
263 },
264 mac_addr, 293 mac_addr,
265 }; 294 };
266 295
@@ -286,8 +315,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
286 w.set_tie(true); 315 w.set_tie(true);
287 }); 316 });
288 317
289 this.phy.phy_reset(&mut this.station_management); 318 this.phy.phy_reset();
290 this.phy.phy_init(&mut this.station_management); 319 this.phy.phy_init();
291 320
292 interrupt::ETH.unpend(); 321 interrupt::ETH.unpend();
293 unsafe { interrupt::ETH.enable() }; 322 unsafe { interrupt::ETH.enable() };
@@ -296,42 +325,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
296 } 325 }
297} 326}
298 327
299/// Ethernet SMI driver.
300pub struct EthernetStationManagement<T: Instance> {
301 peri: PhantomData<T>,
302 clock_range: u8,
303}
304
305impl<T: Instance> StationManagement for EthernetStationManagement<T> {
306 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
307 let mac = T::regs().ethernet_mac();
308
309 mac.macmdioar().modify(|w| {
310 w.set_pa(phy_addr);
311 w.set_rda(reg);
312 w.set_goc(0b11); // read
313 w.set_cr(self.clock_range);
314 w.set_mb(true);
315 });
316 while mac.macmdioar().read().mb() {}
317 mac.macmdiodr().read().md()
318 }
319
320 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
321 let mac = T::regs().ethernet_mac();
322
323 mac.macmdiodr().write(|w| w.set_md(val));
324 mac.macmdioar().modify(|w| {
325 w.set_pa(phy_addr);
326 w.set_rda(reg);
327 w.set_goc(0b01); // write
328 w.set_cr(self.clock_range);
329 w.set_mb(true);
330 });
331 while mac.macmdioar().read().mb() {}
332 }
333}
334
335impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { 328impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> {
336 fn drop(&mut self) { 329 fn drop(&mut self) {
337 let dma = T::regs().ethernet_dma(); 330 let dma = T::regs().ethernet_dma();