aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2025-11-18 16:06:19 +0000
committerGitHub <[email protected]>2025-11-18 16:06:19 +0000
commita72ba1e0571e6bef9c93c1d98b67724c685a37cc (patch)
tree604068e12694e9f3cb6e449b2544c38b6a17620b
parentaad02db7c59467374526ffbb484dbacf2a7b6e5e (diff)
parent0102bc3f82a4a92f3ba7594bc788f6345acc8d17 (diff)
Merge pull request #4871 from datdenkikniet/sma
Standalone ethernet station management
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/build.rs10
-rw-r--r--embassy-stm32/src/eth/generic_phy.rs50
-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.rs259
-rw-r--r--embassy-stm32/src/eth/v2/mod.rs197
-rw-r--r--examples/stm32f4/src/bin/eth.rs12
-rw-r--r--examples/stm32f4/src/bin/eth_compliance_test.rs12
-rw-r--r--examples/stm32f7/src/bin/eth.rs12
-rw-r--r--examples/stm32h5/src/bin/eth.rs12
-rw-r--r--examples/stm32h7/src/bin/eth.rs12
-rw-r--r--examples/stm32h7/src/bin/eth_client.rs12
-rw-r--r--examples/stm32h7/src/bin/eth_client_mii.rs12
-rw-r--r--examples/stm32h7rs/src/bin/eth.rs12
-rw-r--r--tests/stm32/src/bin/eth.rs13
18 files changed, 551 insertions, 342 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index 8e3e802a4..b6caf8f65 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871))
10- feat: allow embassy_executor::main for low power 11- feat: allow embassy_executor::main for low power
11- feat: Add waveform methods to ComplementaryPwm 12- feat: Add waveform methods to ComplementaryPwm
12- fix: Avoid generating timer update events when updating the frequency ([#4890](https://github.com/embassy-rs/embassy/pull/4890)) 13- fix: Avoid generating timer update events when updating the frequency ([#4890](https://github.com/embassy-rs/embassy/pull/4890))
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 1e11eb8dc..8cbd38e10 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -170,6 +170,11 @@ fn main() {
170 } 170 }
171 singletons.push(p.name.to_string()); 171 singletons.push(p.name.to_string());
172 } 172 }
173
174 "eth" => {
175 singletons.push(p.name.to_string());
176 singletons.push("ETH_SMA".to_string());
177 }
173 //"dbgmcu" => {} 178 //"dbgmcu" => {}
174 //"syscfg" => {} 179 //"syscfg" => {}
175 //"dma" => {} 180 //"dma" => {}
@@ -1399,6 +1404,11 @@ fn main() {
1399 } 1404 }
1400 } 1405 }
1401 1406
1407 // MDIO and MDC are special
1408 if pin.signal == "MDIO" || pin.signal == "MDC" {
1409 peri = format_ident!("{}", "ETH_SMA");
1410 }
1411
1402 // XSPI NCS pin to CSSEL mapping 1412 // XSPI NCS pin to CSSEL mapping
1403 if pin.signal.ends_with("NCS1") { 1413 if pin.signal.ends_with("NCS1") {
1404 g.extend(quote! { 1414 g.extend(quote! {
diff --git a/embassy-stm32/src/eth/generic_phy.rs b/embassy-stm32/src/eth/generic_phy.rs
index 947874d7f..0a5f41de0 100644
--- a/embassy-stm32/src/eth/generic_phy.rs
+++ b/embassy-stm32/src/eth/generic_phy.rs
@@ -44,21 +44,23 @@ mod phy_consts {
44use self::phy_consts::*; 44use self::phy_consts::*;
45 45
46/// Generic SMI Ethernet PHY implementation 46/// Generic SMI Ethernet PHY implementation
47pub struct GenericPhy { 47pub struct GenericPhy<SM: StationManagement> {
48 phy_addr: u8, 48 phy_addr: u8,
49 sm: SM,
49 #[cfg(feature = "time")] 50 #[cfg(feature = "time")]
50 poll_interval: Duration, 51 poll_interval: Duration,
51} 52}
52 53
53impl GenericPhy { 54impl<SM: StationManagement> GenericPhy<SM> {
54 /// 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
55 /// 56 ///
56 /// # Panics 57 /// # Panics
57 /// `phy_addr` must be in range `0..32` 58 /// `phy_addr` must be in range `0..32`
58 pub fn new(phy_addr: u8) -> Self { 59 pub fn new(sm: SM, phy_addr: u8) -> Self {
59 assert!(phy_addr < 32); 60 assert!(phy_addr < 32);
60 Self { 61 Self {
61 phy_addr, 62 phy_addr,
63 sm,
62 #[cfg(feature = "time")] 64 #[cfg(feature = "time")]
63 poll_interval: Duration::from_millis(500), 65 poll_interval: Duration::from_millis(500),
64 } 66 }
@@ -68,8 +70,9 @@ impl GenericPhy {
68 /// 70 ///
69 /// # Panics 71 /// # Panics
70 /// Initialization panics if PHY didn't respond on any address 72 /// Initialization panics if PHY didn't respond on any address
71 pub fn new_auto() -> Self { 73 pub fn new_auto(sm: SM) -> Self {
72 Self { 74 Self {
75 sm,
73 phy_addr: 0xFF, 76 phy_addr: 0xFF,
74 #[cfg(feature = "time")] 77 #[cfg(feature = "time")]
75 poll_interval: Duration::from_millis(500), 78 poll_interval: Duration::from_millis(500),
@@ -77,14 +80,14 @@ impl GenericPhy {
77 } 80 }
78} 81}
79 82
80impl Phy for GenericPhy { 83impl<SM: StationManagement> Phy for GenericPhy<SM> {
81 fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) { 84 fn phy_reset(&mut self) {
82 // Detect SMI address 85 // Detect SMI address
83 if self.phy_addr == 0xFF { 86 if self.phy_addr == 0xFF {
84 for addr in 0..32 { 87 for addr in 0..32 {
85 sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); 88 self.sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET);
86 for _ in 0..10 { 89 for _ in 0..10 {
87 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 {
88 trace!("Found ETH PHY on address {}", addr); 91 trace!("Found ETH PHY on address {}", addr);
89 self.phy_addr = addr; 92 self.phy_addr = addr;
90 return; 93 return;
@@ -96,30 +99,30 @@ impl Phy for GenericPhy {
96 panic!("PHY did not respond"); 99 panic!("PHY did not respond");
97 } 100 }
98 101
99 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);
100 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 {}
101 } 104 }
102 105
103 fn phy_init<S: StationManagement>(&mut self, sm: &mut S) { 106 fn phy_init(&mut self) {
104 // Clear WU CSR 107 // Clear WU CSR
105 self.smi_write_ext(sm, PHY_REG_WUCSR, 0); 108 self.smi_write_ext(PHY_REG_WUCSR, 0);
106 109
107 // Enable auto-negotiation 110 // Enable auto-negotiation
108 sm.smi_write( 111 self.sm.smi_write(
109 self.phy_addr, 112 self.phy_addr,
110 PHY_REG_BCR, 113 PHY_REG_BCR,
111 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,
112 ); 115 );
113 } 116 }
114 117
115 fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool { 118 fn poll_link(&mut self, cx: &mut Context) -> bool {
116 #[cfg(not(feature = "time"))] 119 #[cfg(not(feature = "time"))]
117 cx.waker().wake_by_ref(); 120 cx.waker().wake_by_ref();
118 121
119 #[cfg(feature = "time")] 122 #[cfg(feature = "time")]
120 let _ = Timer::after(self.poll_interval).poll_unpin(cx); 123 let _ = Timer::after(self.poll_interval).poll_unpin(cx);
121 124
122 let bsr = sm.smi_read(self.phy_addr, PHY_REG_BSR); 125 let bsr = self.sm.smi_read(self.phy_addr, PHY_REG_BSR);
123 126
124 // No link without autonegotiate 127 // No link without autonegotiate
125 if bsr & PHY_REG_BSR_ANDONE == 0 { 128 if bsr & PHY_REG_BSR_ANDONE == 0 {
@@ -136,7 +139,7 @@ impl Phy for GenericPhy {
136} 139}
137 140
138/// Public functions for the PHY 141/// Public functions for the PHY
139impl GenericPhy { 142impl<SM: StationManagement> GenericPhy<SM> {
140 /// Set the SMI polling interval. 143 /// Set the SMI polling interval.
141 #[cfg(feature = "time")] 144 #[cfg(feature = "time")]
142 pub fn set_poll_interval(&mut self, poll_interval: Duration) { 145 pub fn set_poll_interval(&mut self, poll_interval: Duration) {
@@ -144,10 +147,15 @@ impl GenericPhy {
144 } 147 }
145 148
146 // 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
147 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) {
148 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
149 sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); 152 self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr);
150 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
151 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
152 } 160 }
153} 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 a77eb8719..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;
7use core::sync::atomic::{Ordering, fence}; 6use core::sync::atomic::{Ordering, fence};
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
@@ -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,58 +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!( 364 config_pins!(
354 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 365 rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en
355 ); 366 );
356 367
357 let pins = Pins::Mii([ 368 let pins = Pins::Mii([
358 rx_clk.into(), 369 rx_clk.into(),
359 tx_clk.into(), 370 tx_clk.into(),
360 mdio.into(),
361 mdc.into(),
362 rxdv.into(), 371 rxdv.into(),
363 rx_d0.into(), 372 rx_d0.into(),
364 rx_d1.into(), 373 rx_d1.into(),
@@ -371,43 +380,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
371 tx_en.into(), 380 tx_en.into(),
372 ]); 381 ]);
373 382
374 Self::new_inner(queue, peri, irq, pins, phy, mac_addr) 383 Self::new_inner(queue, peri, irq, pins, phy, mac_addr, false)
375 }
376}
377
378/// Ethernet station management interface.
379pub(crate) struct EthernetStationManagement<T: Instance> {
380 peri: PhantomData<T>,
381 clock_range: Cr,
382}
383
384impl<T: Instance> StationManagement for EthernetStationManagement<T> {
385 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
386 let mac = T::regs().ethernet_mac();
387
388 mac.macmiiar().modify(|w| {
389 w.set_pa(phy_addr);
390 w.set_mr(reg);
391 w.set_mw(Mw::READ); // read operation
392 w.set_cr(self.clock_range);
393 w.set_mb(MbProgress::BUSY); // indicate that operation is in progress
394 });
395 while mac.macmiiar().read().mb() == MbProgress::BUSY {}
396 mac.macmiidr().read().md()
397 }
398
399 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
400 let mac = T::regs().ethernet_mac();
401
402 mac.macmiidr().write(|w| w.set_md(val));
403 mac.macmiiar().modify(|w| {
404 w.set_pa(phy_addr);
405 w.set_mr(reg);
406 w.set_mw(Mw::WRITE); // write
407 w.set_cr(self.clock_range);
408 w.set_mb(MbProgress::BUSY);
409 });
410 while mac.macmiiar().read().mb() == MbProgress::BUSY {}
411 } 384 }
412} 385}
413 386
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs
index 39a6e8b0f..7f92e351c 100644
--- a/embassy-stm32/src/eth/v2/mod.rs
+++ b/embassy-stm32/src/eth/v2/mod.rs
@@ -1,6 +1,5 @@
1mod descriptors; 1mod descriptors;
2 2
3use core::marker::PhantomData;
4use core::sync::atomic::{Ordering, fence}; 3use core::sync::atomic::{Ordering, fence};
5 4
6use embassy_hal_internal::Peri; 5use embassy_hal_internal::Peri;
@@ -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,31 +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
135 critical_section::with(|_| {
136 crate::pac::RCC.ahb1enr().modify(|w| {
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!( 184 config_pins!(
148 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 185 rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en
149 ); 186 );
150 187
151 let pins = Pins::Mii([ 188 let pins = Pins::Mii([
152 rx_clk.into(), 189 rx_clk.into(),
153 tx_clk.into(), 190 tx_clk.into(),
154 mdio.into(),
155 mdc.into(),
156 rxdv.into(), 191 rxdv.into(),
157 rx_d0.into(), 192 rx_d0.into(),
158 rx_d1.into(), 193 rx_d1.into(),
@@ -165,7 +200,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
165 tx_en.into(), 200 tx_en.into(),
166 ]); 201 ]);
167 202
168 Self::new_inner(queue, peri, irq, pins, phy, mac_addr) 203 Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::MII_GMII)
169 } 204 }
170 205
171 fn new_inner<const TX: usize, const RX: usize>( 206 fn new_inner<const TX: usize, const RX: usize>(
@@ -175,7 +210,19 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
175 pins: Pins<'d>, 210 pins: Pins<'d>,
176 phy: P, 211 phy: P,
177 mac_addr: [u8; 6], 212 mac_addr: [u8; 6],
213 eth_sel_phy: EthSelPhy,
178 ) -> 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
179 let dma = T::regs().ethernet_dma(); 226 let dma = T::regs().ethernet_dma();
180 let mac = T::regs().ethernet_mac(); 227 let mac = T::regs().ethernet_mac();
181 let mtl = T::regs().ethernet_mtl(); 228 let mtl = T::regs().ethernet_mtl();
@@ -237,32 +284,12 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
237 w.set_rbsz(RX_BUFFER_SIZE as u16); 284 w.set_rbsz(RX_BUFFER_SIZE as u16);
238 }); 285 });
239 286
240 let hclk = <T as SealedRccPeripheral>::frequency();
241 let hclk_mhz = hclk.0 / 1_000_000;
242
243 // Set the MDC clock frequency in the range 1MHz - 2.5MHz
244 let clock_range = match hclk_mhz {
245 0..=34 => 2, // Divide by 16
246 35..=59 => 3, // Divide by 26
247 60..=99 => 0, // Divide by 42
248 100..=149 => 1, // Divide by 62
249 150..=249 => 4, // Divide by 102
250 250..=310 => 5, // Divide by 124
251 _ => {
252 panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
253 }
254 };
255
256 let mut this = Self { 287 let mut this = Self {
257 _peri: peri, 288 _peri: peri,
258 tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), 289 tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
259 rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), 290 rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
260 pins, 291 pins,
261 phy, 292 phy,
262 station_management: EthernetStationManagement {
263 peri: PhantomData,
264 clock_range: clock_range,
265 },
266 mac_addr, 293 mac_addr,
267 }; 294 };
268 295
@@ -288,8 +315,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
288 w.set_tie(true); 315 w.set_tie(true);
289 }); 316 });
290 317
291 this.phy.phy_reset(&mut this.station_management); 318 this.phy.phy_reset();
292 this.phy.phy_init(&mut this.station_management); 319 this.phy.phy_init();
293 320
294 interrupt::ETH.unpend(); 321 interrupt::ETH.unpend();
295 unsafe { interrupt::ETH.enable() }; 322 unsafe { interrupt::ETH.enable() };
@@ -298,42 +325,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
298 } 325 }
299} 326}
300 327
301/// Ethernet SMI driver.
302pub struct EthernetStationManagement<T: Instance> {
303 peri: PhantomData<T>,
304 clock_range: u8,
305}
306
307impl<T: Instance> StationManagement for EthernetStationManagement<T> {
308 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
309 let mac = T::regs().ethernet_mac();
310
311 mac.macmdioar().modify(|w| {
312 w.set_pa(phy_addr);
313 w.set_rda(reg);
314 w.set_goc(0b11); // read
315 w.set_cr(self.clock_range);
316 w.set_mb(true);
317 });
318 while mac.macmdioar().read().mb() {}
319 mac.macmdiodr().read().md()
320 }
321
322 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
323 let mac = T::regs().ethernet_mac();
324
325 mac.macmdiodr().write(|w| w.set_md(val));
326 mac.macmdioar().modify(|w| {
327 w.set_pa(phy_addr);
328 w.set_rda(reg);
329 w.set_goc(0b01); // write
330 w.set_cr(self.clock_range);
331 w.set_mb(true);
332 });
333 while mac.macmdioar().read().mb() {}
334 }
335}
336
337impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { 328impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> {
338 fn drop(&mut self) { 329 fn drop(&mut self) {
339 let dma = T::regs().ethernet_dma(); 330 let dma = T::regs().ethernet_dma();
diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs
index 2d72b6b0b..8dfa0916d 100644
--- a/examples/stm32f4/src/bin/eth.rs
+++ b/examples/stm32f4/src/bin/eth.rs
@@ -5,8 +5,8 @@ use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::tcp::TcpSocket; 6use embassy_net::tcp::TcpSocket;
7use embassy_net::{Ipv4Address, StackResources}; 7use embassy_net::{Ipv4Address, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rng::Rng; 10use embassy_stm32::rng::Rng;
11use embassy_stm32::time::Hertz; 11use embassy_stm32::time::Hertz;
12use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 12use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
@@ -20,7 +20,7 @@ bind_interrupts!(struct Irqs {
20 HASH_RNG => rng::InterruptHandler<peripherals::RNG>; 20 HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
21}); 21});
22 22
23type Device = Ethernet<'static, ETH, GenericPhy>; 23type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
24 24
25#[embassy_executor::task] 25#[embassy_executor::task]
26async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 26async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {
@@ -67,16 +67,16 @@ async fn main(spawner: Spawner) -> ! {
67 p.ETH, 67 p.ETH,
68 Irqs, 68 Irqs,
69 p.PA1, 69 p.PA1,
70 p.PA2,
71 p.PC1,
72 p.PA7, 70 p.PA7,
73 p.PC4, 71 p.PC4,
74 p.PC5, 72 p.PC5,
75 p.PG13, 73 p.PG13,
76 p.PB13, 74 p.PB13,
77 p.PG11, 75 p.PG11,
78 GenericPhy::new_auto(),
79 mac_addr, 76 mac_addr,
77 p.ETH_SMA,
78 p.PA2,
79 p.PC1,
80 ); 80 );
81 81
82 let config = embassy_net::Config::dhcpv4(Default::default()); 82 let config = embassy_net::Config::dhcpv4(Default::default());
diff --git a/examples/stm32f4/src/bin/eth_compliance_test.rs b/examples/stm32f4/src/bin/eth_compliance_test.rs
index 734a14c2c..dc5d7dbb6 100644
--- a/examples/stm32f4/src/bin/eth_compliance_test.rs
+++ b/examples/stm32f4/src/bin/eth_compliance_test.rs
@@ -3,7 +3,7 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, StationManagement}; 6use embassy_stm32::eth::{Ethernet, PacketQueue, StationManagement};
7use embassy_stm32::time::Hertz; 7use embassy_stm32::time::Hertz;
8use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 8use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
9use embassy_time::Timer; 9use embassy_time::Timer;
@@ -43,27 +43,27 @@ async fn main(_spawner: Spawner) -> ! {
43 43
44 let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; 44 let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
45 45
46 const PHY_ADDR: u8 = 0;
47 static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new(); 46 static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();
48 let mut device = Ethernet::new( 47 let mut device = Ethernet::new(
49 PACKETS.init(PacketQueue::<4, 4>::new()), 48 PACKETS.init(PacketQueue::<4, 4>::new()),
50 p.ETH, 49 p.ETH,
51 Irqs, 50 Irqs,
52 p.PA1, 51 p.PA1,
53 p.PA2,
54 p.PC1,
55 p.PA7, 52 p.PA7,
56 p.PC4, 53 p.PC4,
57 p.PC5, 54 p.PC5,
58 p.PG13, 55 p.PG13,
59 p.PB13, 56 p.PB13,
60 p.PG11, 57 p.PG11,
61 GenericPhy::new(PHY_ADDR),
62 mac_addr, 58 mac_addr,
59 p.ETH_SMA,
60 p.PA2,
61 p.PC1,
63 ); 62 );
64 63
65 let sm = device.station_management(); 64 let sm = device.phy_mut().station_management();
66 65
66 const PHY_ADDR: u8 = 0;
67 // Just an example. Exact register settings depend on the specific PHY and test. 67 // Just an example. Exact register settings depend on the specific PHY and test.
68 sm.smi_write(PHY_ADDR, 0, 0x2100); 68 sm.smi_write(PHY_ADDR, 0, 0x2100);
69 sm.smi_write(PHY_ADDR, 11, 0xA000); 69 sm.smi_write(PHY_ADDR, 11, 0xA000);
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs
index f8a129239..8613376b8 100644
--- a/examples/stm32f7/src/bin/eth.rs
+++ b/examples/stm32f7/src/bin/eth.rs
@@ -5,8 +5,8 @@ use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::tcp::TcpSocket; 6use embassy_net::tcp::TcpSocket;
7use embassy_net::{Ipv4Address, StackResources}; 7use embassy_net::{Ipv4Address, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rng::Rng; 10use embassy_stm32::rng::Rng;
11use embassy_stm32::time::Hertz; 11use embassy_stm32::time::Hertz;
12use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 12use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
@@ -20,7 +20,7 @@ bind_interrupts!(struct Irqs {
20 HASH_RNG => rng::InterruptHandler<peripherals::RNG>; 20 HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
21}); 21});
22 22
23type Device = Ethernet<'static, ETH, GenericPhy>; 23type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
24 24
25#[embassy_executor::task] 25#[embassy_executor::task]
26async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 26async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {
@@ -67,16 +67,16 @@ async fn main(spawner: Spawner) -> ! {
67 p.ETH, 67 p.ETH,
68 Irqs, 68 Irqs,
69 p.PA1, 69 p.PA1,
70 p.PA2,
71 p.PC1,
72 p.PA7, 70 p.PA7,
73 p.PC4, 71 p.PC4,
74 p.PC5, 72 p.PC5,
75 p.PG13, 73 p.PG13,
76 p.PB13, 74 p.PB13,
77 p.PG11, 75 p.PG11,
78 GenericPhy::new_auto(),
79 mac_addr, 76 mac_addr,
77 p.ETH_SMA,
78 p.PA2,
79 p.PC1,
80 ); 80 );
81 81
82 let config = embassy_net::Config::dhcpv4(Default::default()); 82 let config = embassy_net::Config::dhcpv4(Default::default());
diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs
index a5c6cee26..6a3afb2d1 100644
--- a/examples/stm32h5/src/bin/eth.rs
+++ b/examples/stm32h5/src/bin/eth.rs
@@ -5,8 +5,8 @@ use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::tcp::TcpSocket; 6use embassy_net::tcp::TcpSocket;
7use embassy_net::{Ipv4Address, StackResources}; 7use embassy_net::{Ipv4Address, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rcc::{ 10use embassy_stm32::rcc::{
11 AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, 11 AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,
12}; 12};
@@ -23,7 +23,7 @@ bind_interrupts!(struct Irqs {
23 RNG => rng::InterruptHandler<peripherals::RNG>; 23 RNG => rng::InterruptHandler<peripherals::RNG>;
24}); 24});
25 25
26type Device = Ethernet<'static, ETH, GenericPhy>; 26type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
27 27
28#[embassy_executor::task] 28#[embassy_executor::task]
29async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 29async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {
@@ -70,16 +70,16 @@ async fn main(spawner: Spawner) -> ! {
70 p.ETH, 70 p.ETH,
71 Irqs, 71 Irqs,
72 p.PA1, 72 p.PA1,
73 p.PA2,
74 p.PC1,
75 p.PA7, 73 p.PA7,
76 p.PC4, 74 p.PC4,
77 p.PC5, 75 p.PC5,
78 p.PG13, 76 p.PG13,
79 p.PB15, 77 p.PB15,
80 p.PG11, 78 p.PG11,
81 GenericPhy::new_auto(),
82 mac_addr, 79 mac_addr,
80 p.ETH_SMA,
81 p.PA2,
82 p.PC1,
83 ); 83 );
84 84
85 let config = embassy_net::Config::dhcpv4(Default::default()); 85 let config = embassy_net::Config::dhcpv4(Default::default());
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs
index 589f4426e..09915799b 100644
--- a/examples/stm32h7/src/bin/eth.rs
+++ b/examples/stm32h7/src/bin/eth.rs
@@ -5,8 +5,8 @@ use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::tcp::TcpSocket; 6use embassy_net::tcp::TcpSocket;
7use embassy_net::{Ipv4Address, StackResources}; 7use embassy_net::{Ipv4Address, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rng::Rng; 10use embassy_stm32::rng::Rng;
11use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 11use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
12use embassy_time::Timer; 12use embassy_time::Timer;
@@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs {
19 RNG => rng::InterruptHandler<peripherals::RNG>; 19 RNG => rng::InterruptHandler<peripherals::RNG>;
20}); 20});
21 21
22type Device = Ethernet<'static, ETH, GenericPhy>; 22type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
23 23
24#[embassy_executor::task] 24#[embassy_executor::task]
25async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 25async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {
@@ -69,16 +69,16 @@ async fn main(spawner: Spawner) -> ! {
69 p.ETH, 69 p.ETH,
70 Irqs, 70 Irqs,
71 p.PA1, // ref_clk 71 p.PA1, // ref_clk
72 p.PA2, // mdio
73 p.PC1, // eth_mdc
74 p.PA7, // CRS_DV: Carrier Sense 72 p.PA7, // CRS_DV: Carrier Sense
75 p.PC4, // RX_D0: Received Bit 0 73 p.PC4, // RX_D0: Received Bit 0
76 p.PC5, // RX_D1: Received Bit 1 74 p.PC5, // RX_D1: Received Bit 1
77 p.PG13, // TX_D0: Transmit Bit 0 75 p.PG13, // TX_D0: Transmit Bit 0
78 p.PB13, // TX_D1: Transmit Bit 1 76 p.PB13, // TX_D1: Transmit Bit 1
79 p.PG11, // TX_EN: Transmit Enable 77 p.PG11, // TX_EN: Transmit Enable
80 GenericPhy::new_auto(),
81 mac_addr, 78 mac_addr,
79 p.ETH_SMA,
80 p.PA2, // mdio
81 p.PC1, // mdc
82 ); 82 );
83 83
84 let config = embassy_net::Config::dhcpv4(Default::default()); 84 let config = embassy_net::Config::dhcpv4(Default::default());
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs
index fed8f1a9c..189c99686 100644
--- a/examples/stm32h7/src/bin/eth_client.rs
+++ b/examples/stm32h7/src/bin/eth_client.rs
@@ -7,8 +7,8 @@ use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_net::StackResources; 8use embassy_net::StackResources;
9use embassy_net::tcp::client::{TcpClient, TcpClientState}; 9use embassy_net::tcp::client::{TcpClient, TcpClientState};
10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
11use embassy_stm32::peripherals::ETH; 11use embassy_stm32::peripherals::{ETH, ETH_SMA};
12use embassy_stm32::rng::Rng; 12use embassy_stm32::rng::Rng;
13use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 13use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
14use embassy_time::Timer; 14use embassy_time::Timer;
@@ -22,7 +22,7 @@ bind_interrupts!(struct Irqs {
22 RNG => rng::InterruptHandler<peripherals::RNG>; 22 RNG => rng::InterruptHandler<peripherals::RNG>;
23}); 23});
24 24
25type Device = Ethernet<'static, ETH, GenericPhy>; 25type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
26 26
27#[embassy_executor::task] 27#[embassy_executor::task]
28async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 28async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {
@@ -71,16 +71,16 @@ async fn main(spawner: Spawner) -> ! {
71 p.ETH, 71 p.ETH,
72 Irqs, 72 Irqs,
73 p.PA1, 73 p.PA1,
74 p.PA2,
75 p.PC1,
76 p.PA7, 74 p.PA7,
77 p.PC4, 75 p.PC4,
78 p.PC5, 76 p.PC5,
79 p.PG13, 77 p.PG13,
80 p.PB13, 78 p.PB13,
81 p.PG11, 79 p.PG11,
82 GenericPhy::new_auto(),
83 mac_addr, 80 mac_addr,
81 p.ETH_SMA,
82 p.PA2,
83 p.PC1,
84 ); 84 );
85 85
86 let config = embassy_net::Config::dhcpv4(Default::default()); 86 let config = embassy_net::Config::dhcpv4(Default::default());
diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs
index c3c631f0f..92c823567 100644
--- a/examples/stm32h7/src/bin/eth_client_mii.rs
+++ b/examples/stm32h7/src/bin/eth_client_mii.rs
@@ -7,8 +7,8 @@ use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_net::StackResources; 8use embassy_net::StackResources;
9use embassy_net::tcp::client::{TcpClient, TcpClientState}; 9use embassy_net::tcp::client::{TcpClient, TcpClientState};
10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
11use embassy_stm32::peripherals::ETH; 11use embassy_stm32::peripherals::{ETH, ETH_SMA};
12use embassy_stm32::rng::Rng; 12use embassy_stm32::rng::Rng;
13use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 13use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
14use embassy_time::Timer; 14use embassy_time::Timer;
@@ -22,7 +22,7 @@ bind_interrupts!(struct Irqs {
22 RNG => rng::InterruptHandler<peripherals::RNG>; 22 RNG => rng::InterruptHandler<peripherals::RNG>;
23}); 23});
24 24
25type Device = Ethernet<'static, ETH, GenericPhy>; 25type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
26 26
27#[embassy_executor::task] 27#[embassy_executor::task]
28async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 28async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {
@@ -72,8 +72,6 @@ async fn main(spawner: Spawner) -> ! {
72 Irqs, 72 Irqs,
73 p.PA1, 73 p.PA1,
74 p.PC3, 74 p.PC3,
75 p.PA2,
76 p.PC1,
77 p.PA7, 75 p.PA7,
78 p.PC4, 76 p.PC4,
79 p.PC5, 77 p.PC5,
@@ -84,8 +82,10 @@ async fn main(spawner: Spawner) -> ! {
84 p.PC2, 82 p.PC2,
85 p.PE2, 83 p.PE2,
86 p.PG11, 84 p.PG11,
87 GenericPhy::new_auto(),
88 mac_addr, 85 mac_addr,
86 p.ETH_SMA,
87 p.PA2,
88 p.PC1,
89 ); 89 );
90 info!("Device created"); 90 info!("Device created");
91 91
diff --git a/examples/stm32h7rs/src/bin/eth.rs b/examples/stm32h7rs/src/bin/eth.rs
index 5ce1d4765..8e07d0a67 100644
--- a/examples/stm32h7rs/src/bin/eth.rs
+++ b/examples/stm32h7rs/src/bin/eth.rs
@@ -5,8 +5,8 @@ use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_net::udp::{PacketMetadata, UdpSocket}; 6use embassy_net::udp::{PacketMetadata, UdpSocket};
7use embassy_net::{Ipv4Address, Ipv4Cidr, StackResources}; 7use embassy_net::{Ipv4Address, Ipv4Cidr, StackResources};
8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 8use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
9use embassy_stm32::peripherals::ETH; 9use embassy_stm32::peripherals::{ETH, ETH_SMA};
10use embassy_stm32::rng::Rng; 10use embassy_stm32::rng::Rng;
11use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; 11use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};
12use embassy_time::Timer; 12use embassy_time::Timer;
@@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs {
19 RNG => rng::InterruptHandler<peripherals::RNG>; 19 RNG => rng::InterruptHandler<peripherals::RNG>;
20}); 20});
21 21
22type Device = Ethernet<'static, ETH, GenericPhy>; 22type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
23 23
24#[embassy_executor::task] 24#[embassy_executor::task]
25async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 25async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {
@@ -69,16 +69,16 @@ async fn main(spawner: Spawner) -> ! {
69 p.ETH, 69 p.ETH,
70 Irqs, 70 Irqs,
71 p.PB6, 71 p.PB6,
72 p.PA2,
73 p.PG6,
74 p.PA7, 72 p.PA7,
75 p.PG4, 73 p.PG4,
76 p.PG5, 74 p.PG5,
77 p.PG13, 75 p.PG13,
78 p.PG12, 76 p.PG12,
79 p.PG11, 77 p.PG11,
80 GenericPhy::new(0),
81 mac_addr, 78 mac_addr,
79 p.ETH_SMA,
80 p.PA2,
81 p.PG6,
82 ); 82 );
83 83
84 // Have to use UDP w/ static config to fit in internal flash 84 // Have to use UDP w/ static config to fit in internal flash
diff --git a/tests/stm32/src/bin/eth.rs b/tests/stm32/src/bin/eth.rs
index a65682a02..ffc76b96f 100644
--- a/tests/stm32/src/bin/eth.rs
+++ b/tests/stm32/src/bin/eth.rs
@@ -7,8 +7,8 @@ mod common;
7use common::*; 7use common::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_net::StackResources; 9use embassy_net::StackResources;
10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; 10use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};
11use embassy_stm32::peripherals::ETH; 11use embassy_stm32::peripherals::{ETH, ETH_SMA};
12use embassy_stm32::rng::Rng; 12use embassy_stm32::rng::Rng;
13use embassy_stm32::{bind_interrupts, eth, peripherals, rng}; 13use embassy_stm32::{bind_interrupts, eth, peripherals, rng};
14use static_cell::StaticCell; 14use static_cell::StaticCell;
@@ -27,7 +27,7 @@ bind_interrupts!(struct Irqs {
27 RNG => rng::InterruptHandler<peripherals::RNG>; 27 RNG => rng::InterruptHandler<peripherals::RNG>;
28}); 28});
29 29
30type Device = Ethernet<'static, ETH, GenericPhy>; 30type Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;
31 31
32#[embassy_executor::task] 32#[embassy_executor::task]
33async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { 33async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {
@@ -69,13 +69,12 @@ async fn main(spawner: Spawner) {
69 const PACKET_QUEUE_SIZE: usize = 4; 69 const PACKET_QUEUE_SIZE: usize = 4;
70 70
71 static PACKETS: StaticCell<PacketQueue<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>> = StaticCell::new(); 71 static PACKETS: StaticCell<PacketQueue<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>> = StaticCell::new();
72
72 let device = Ethernet::new( 73 let device = Ethernet::new(
73 PACKETS.init(PacketQueue::<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>::new()), 74 PACKETS.init(PacketQueue::<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>::new()),
74 p.ETH, 75 p.ETH,
75 Irqs, 76 Irqs,
76 p.PA1, 77 p.PA1,
77 p.PA2,
78 p.PC1,
79 p.PA7, 78 p.PA7,
80 p.PC4, 79 p.PC4,
81 p.PC5, 80 p.PC5,
@@ -85,8 +84,10 @@ async fn main(spawner: Spawner) {
85 #[cfg(feature = "stm32h563zi")] 84 #[cfg(feature = "stm32h563zi")]
86 p.PB15, 85 p.PB15,
87 p.PG11, 86 p.PG11,
88 GenericPhy::new_auto(),
89 mac_addr, 87 mac_addr,
88 p.ETH_SMA,
89 p.PA2,
90 p.PC1,
90 ); 91 );
91 92
92 let config = embassy_net::Config::dhcpv4(Default::default()); 93 let config = embassy_net::Config::dhcpv4(Default::default());