aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThales Fragoso <[email protected]>2021-06-07 02:30:38 -0300
committerDario Nieuwenhuis <[email protected]>2021-06-16 16:48:35 +0200
commit46e1bae9e36917c2e763082730d99df302c1c625 (patch)
tree5e181740db8bdb8500808cc9b03889de8d3af05e
parent6386c34079913732466046194f79a683a4aefce4 (diff)
eth-v2: Start Ethernet peripheral implementation
-rw-r--r--embassy-net/src/lib.rs2
-rw-r--r--embassy-net/src/packet_pool.rs3
-rw-r--r--embassy-stm32/Cargo.toml3
-rw-r--r--embassy-stm32/src/eth/mod.rs7
-rw-r--r--embassy-stm32/src/eth/v1.rs1
-rw-r--r--embassy-stm32/src/eth/v2/descriptors.rs371
-rw-r--r--embassy-stm32/src/eth/v2/mod.rs350
-rw-r--r--embassy-stm32/src/lib.rs2
8 files changed, 737 insertions, 2 deletions
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index 88dcf0aa5..51eb97a2e 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -13,7 +13,7 @@ pub use config::DhcpConfigurator;
13pub use config::{Config, Configurator, Event as ConfigEvent, StaticConfigurator}; 13pub use config::{Config, Configurator, Event as ConfigEvent, StaticConfigurator};
14 14
15pub use device::{Device, LinkState}; 15pub use device::{Device, LinkState};
16pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf}; 16pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU};
17pub use stack::{init, is_config_up, is_init, is_link_up, run}; 17pub use stack::{init, is_config_up, is_init, is_link_up, run};
18 18
19#[cfg(feature = "tcp")] 19#[cfg(feature = "tcp")]
diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs
index 2c27d4013..0ec88e649 100644
--- a/embassy-net/src/packet_pool.rs
+++ b/embassy-net/src/packet_pool.rs
@@ -3,12 +3,13 @@ use core::ops::{Deref, DerefMut, Range};
3 3
4use atomic_pool::{pool, Box}; 4use atomic_pool::{pool, Box};
5 5
6pub const MTU: usize = 1514; 6pub const MTU: usize = 1516;
7pub const PACKET_POOL_SIZE: usize = 4; 7pub const PACKET_POOL_SIZE: usize = 4;
8 8
9pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]); 9pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]);
10pub type PacketBox = Box<PacketPool>; 10pub type PacketBox = Box<PacketPool>;
11 11
12#[repr(align(4))]
12pub struct Packet(pub [u8; MTU]); 13pub struct Packet(pub [u8; MTU]);
13 14
14impl Packet { 15impl Packet {
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 682ebaefc..cf45fe21f 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -10,6 +10,7 @@ embassy = { version = "0.1.0", path = "../embassy" }
10embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } 10embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] }
11embassy-extras = {version = "0.1.0", path = "../embassy-extras" } 11embassy-extras = {version = "0.1.0", path = "../embassy-extras" }
12embassy-traits = {version = "0.1.0", path = "../embassy-traits" } 12embassy-traits = {version = "0.1.0", path = "../embassy-traits" }
13embassy-net = { version = "0.1.0", path = "../embassy-net", features = ["tcp", "medium-ip"] }
13 14
14defmt = { version = "0.2.0", optional = true } 15defmt = { version = "0.2.0", optional = true }
15log = { version = "0.4.11", optional = true } 16log = { version = "0.4.11", optional = true }
@@ -24,6 +25,8 @@ critical-section = "0.2.1"
24bare-metal = "1.0.0" 25bare-metal = "1.0.0"
25atomic-polyfill = "0.1.2" 26atomic-polyfill = "0.1.2"
26stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } 27stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] }
28vcell = "0.1.3"
29
27cfg-if = "1.0.0" 30cfg-if = "1.0.0"
28 31
29[build-dependencies] 32[build-dependencies]
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs
new file mode 100644
index 000000000..8791e1552
--- /dev/null
+++ b/embassy-stm32/src/eth/mod.rs
@@ -0,0 +1,7 @@
1#![macro_use]
2
3#[cfg_attr(eth_v1, path = "v1.rs")]
4#[cfg_attr(eth_v2, path = "v2/mod.rs")]
5mod _version;
6
7pub use _version::*;
diff --git a/embassy-stm32/src/eth/v1.rs b/embassy-stm32/src/eth/v1.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/embassy-stm32/src/eth/v1.rs
@@ -0,0 +1 @@
diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs
new file mode 100644
index 000000000..095eef20d
--- /dev/null
+++ b/embassy-stm32/src/eth/v2/descriptors.rs
@@ -0,0 +1,371 @@
1use core::sync::atomic::{fence, Ordering};
2
3use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf};
4use vcell::VolatileCell;
5
6use crate::pac::ETH;
7
8#[non_exhaustive]
9#[derive(Debug, Copy, Clone)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11pub enum Error {
12 NoBufferAvailable,
13 // TODO: Break down this error into several others
14 TransmissionError,
15}
16
17/// Transmit and Receive Descriptor fields
18#[allow(dead_code)]
19mod emac_consts {
20 pub const EMAC_DES3_OWN: u32 = 0x8000_0000;
21 pub const EMAC_DES3_CTXT: u32 = 0x4000_0000;
22 pub const EMAC_DES3_FD: u32 = 0x2000_0000;
23 pub const EMAC_DES3_LD: u32 = 0x1000_0000;
24 pub const EMAC_DES3_ES: u32 = 0x0000_8000;
25 pub const EMAC_DES0_BUF1AP: u32 = 0xFFFF_FFFF;
26
27 pub const EMAC_TDES2_IOC: u32 = 0x8000_0000;
28 pub const EMAC_TDES2_B1L: u32 = 0x0000_3FFF;
29
30 pub const EMAC_RDES3_IOC: u32 = 0x4000_0000;
31 pub const EMAC_RDES3_PL: u32 = 0x0000_7FFF;
32 pub const EMAC_RDES3_BUF1V: u32 = 0x0100_0000;
33 pub const EMAC_RDES3_PKTLEN: u32 = 0x0000_7FFF;
34}
35use emac_consts::*;
36
37/// Transmit Descriptor representation
38///
39/// * tdes0: transmit buffer address
40/// * tdes1:
41/// * tdes2: buffer lengths
42/// * tdes3: control and payload/frame length
43#[repr(C)]
44struct TDes {
45 tdes0: VolatileCell<u32>,
46 tdes1: VolatileCell<u32>,
47 tdes2: VolatileCell<u32>,
48 tdes3: VolatileCell<u32>,
49}
50
51impl TDes {
52 pub const fn new() -> Self {
53 Self {
54 tdes0: VolatileCell::new(0),
55 tdes1: VolatileCell::new(0),
56 tdes2: VolatileCell::new(0),
57 tdes3: VolatileCell::new(0),
58 }
59 }
60
61 /// Return true if this TDes is not currently owned by the DMA
62 pub fn available(&self) -> bool {
63 self.tdes3.get() & EMAC_DES3_OWN == 0
64 }
65}
66
67pub(crate) struct TDesRing<const N: usize> {
68 td: [TDes; N],
69 buffers: [Option<PacketBuf>; N],
70 tdidx: usize,
71}
72
73impl<const N: usize> TDesRing<N> {
74 pub const fn new() -> Self {
75 const TDES: TDes = TDes::new();
76 const BUFFERS: Option<PacketBuf> = None;
77
78 Self {
79 td: [TDES; N],
80 buffers: [BUFFERS; N],
81 tdidx: 0,
82 }
83 }
84
85 /// Initialise this TDesRing. Assume TDesRing is corrupt
86 ///
87 /// The current memory address of the buffers inside this TDesRing
88 /// will be stored in the descriptors, so ensure the TDesRing is
89 /// not moved after initialisation.
90 pub(crate) fn init(&mut self) {
91 assert!(N > 0);
92
93 for td in self.td.iter_mut() {
94 *td = TDes::new();
95 }
96 self.tdidx = 0;
97
98 // Initialize the pointers in the DMA engine. (There will be a memory barrier later
99 // before the DMA engine is enabled.)
100 // NOTE (unsafe) Used for atomic writes
101 unsafe {
102 let dma = ETH.ethernet_dma();
103
104 dma.dmactx_dlar()
105 .write(|w| w.set_tdesla(&self.td as *const _ as u32));
106 dma.dmactx_rlr().write(|w| w.set_tdrl((N as u16) - 1));
107 dma.dmactx_dtpr()
108 .write(|w| w.set_tdt(&self.td[0] as *const _ as u32));
109 }
110 }
111
112 /// Return true if a TDes is available for use
113 pub(crate) fn available(&self) -> bool {
114 self.td[self.tdidx].available()
115 }
116
117 pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> {
118 if !self.available() {
119 return Err(Error::NoBufferAvailable);
120 }
121 let x = self.tdidx;
122 let td = &mut self.td[x];
123
124 let pkt_len = pkt.len();
125 assert!(pkt_len as u32 <= EMAC_TDES2_B1L);
126 let address = pkt.as_ptr() as u32;
127
128 // Read format
129 td.tdes0.set(address);
130 td.tdes2
131 .set(pkt_len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC);
132
133 // FD: Contains first buffer of packet
134 // LD: Contains last buffer of packet
135 // Give the DMA engine ownership
136 td.tdes3.set(EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_OWN);
137
138 self.buffers[x].replace(pkt);
139
140 // Ensure changes to the descriptor are committed before DMA engine sees tail pointer store.
141 // This will generate an DMB instruction.
142 // "Preceding reads and writes cannot be moved past subsequent writes."
143 fence(Ordering::Release);
144
145 // Move the tail pointer (TPR) to the next descriptor
146 let x = (x + 1) % N;
147 // NOTE(unsafe) Atomic write
148 unsafe {
149 ETH.ethernet_dma()
150 .dmactx_dtpr()
151 .write(|w| w.set_tdt(&self.td[x] as *const _ as u32));
152 }
153 self.tdidx = x;
154 Ok(())
155 }
156
157 pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> {
158 let previous = (self.tdidx + N - 1) % N;
159 let td = &self.td[previous];
160
161 // DMB to ensure that we are reading an updated value, probably not needed at the hardware
162 // level, but this is also a hint to the compiler that we're syncing on the buffer.
163 fence(Ordering::SeqCst);
164
165 let tdes3 = td.tdes3.get();
166
167 if tdes3 & EMAC_DES3_OWN != 0 {
168 // Transmission isn't done yet, probably a receive interrupt that fired this
169 return Ok(());
170 }
171 assert!(tdes3 & EMAC_DES3_CTXT == 0);
172
173 // Release the buffer
174 self.buffers[previous].take();
175
176 if tdes3 & EMAC_DES3_ES != 0 {
177 Err(Error::TransmissionError)
178 } else {
179 Ok(())
180 }
181 }
182}
183
184/// Receive Descriptor representation
185///
186/// * rdes0: recieve buffer address
187/// * rdes1:
188/// * rdes2:
189/// * rdes3: OWN and Status
190#[repr(C)]
191struct RDes {
192 rdes0: VolatileCell<u32>,
193 rdes1: VolatileCell<u32>,
194 rdes2: VolatileCell<u32>,
195 rdes3: VolatileCell<u32>,
196}
197
198impl RDes {
199 pub const fn new() -> Self {
200 Self {
201 rdes0: VolatileCell::new(0),
202 rdes1: VolatileCell::new(0),
203 rdes2: VolatileCell::new(0),
204 rdes3: VolatileCell::new(0),
205 }
206 }
207
208 /// Return true if this RDes is acceptable to us
209 #[inline(always)]
210 pub fn valid(&self) -> bool {
211 // Write-back descriptor is valid if:
212 //
213 // Contains first buffer of packet AND contains last buf of
214 // packet AND no errors AND not a context descriptor
215 self.rdes3.get() & (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT)
216 == (EMAC_DES3_FD | EMAC_DES3_LD)
217 }
218
219 /// Return true if this RDes is not currently owned by the DMA
220 #[inline(always)]
221 pub fn available(&self) -> bool {
222 self.rdes3.get() & EMAC_DES3_OWN == 0 // Owned by us
223 }
224
225 #[inline(always)]
226 pub fn set_ready(&mut self, buf_addr: u32) {
227 self.rdes0.set(buf_addr);
228 self.rdes3
229 .set(EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN);
230 }
231}
232
233pub(crate) struct RDesRing<const N: usize> {
234 rd: [RDes; N],
235 buffers: [Option<PacketBox>; N],
236 read_idx: usize,
237 tail_idx: usize,
238}
239
240impl<const N: usize> RDesRing<N> {
241 pub const fn new() -> Self {
242 const RDES: RDes = RDes::new();
243 const BUFFERS: Option<PacketBox> = None;
244
245 Self {
246 rd: [RDES; N],
247 buffers: [BUFFERS; N],
248 read_idx: 0,
249 tail_idx: 0,
250 }
251 }
252
253 pub(crate) fn init(&mut self) {
254 assert!(N > 1);
255
256 for desc in self.rd.iter_mut() {
257 *desc = RDes::new();
258 }
259
260 let mut last_index = 0;
261 for (index, buf) in self.buffers.iter_mut().enumerate() {
262 let pkt = match PacketBox::new(Packet::new()) {
263 Some(p) => p,
264 None => {
265 if index == 0 {
266 panic!("Could not allocate at least one buffer for Ethernet receiving");
267 } else {
268 break;
269 }
270 }
271 };
272 let addr = pkt.as_ptr() as u32;
273 *buf = Some(pkt);
274 self.rd[index].set_ready(addr);
275 last_index = index;
276 }
277 self.tail_idx = (last_index + 1) % N;
278
279 unsafe {
280 let dma = ETH.ethernet_dma();
281
282 dma.dmacrx_dlar()
283 .write(|w| w.set_rdesla(self.rd.as_ptr() as u32));
284 dma.dmacrx_rlr().write(|w| w.set_rdrl((N as u16) - 1));
285
286 // We manage to allocate all buffers, set the index to the last one, that means
287 // that the DMA won't consider the last one as ready, because it (unfortunately)
288 // stops at the tail ptr and wraps at the end of the ring, which means that we
289 // can't tell it to stop after the last buffer.
290 let tail_ptr = &self.rd[last_index] as *const _ as u32;
291 fence(Ordering::Release);
292
293 dma.dmacrx_dtpr().write(|w| w.set_rdt(tail_ptr));
294 }
295 }
296
297 pub(crate) fn on_interrupt(&mut self) {
298 // TODO!
299 }
300
301 pub(crate) fn pop_packet(&mut self) -> Option<PacketBuf> {
302 // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using
303 // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the
304 // buffer (I think .-.)
305 fence(Ordering::SeqCst);
306
307 let read_available = self.rd[self.read_idx].available();
308 if !read_available && self.read_idx == self.tail_idx {
309 // Nothing to do
310 return None;
311 }
312
313 let pkt = if read_available {
314 let pkt = self.buffers[self.read_idx].take();
315 let len = (self.rd[self.read_idx].rdes3.get() & EMAC_RDES3_PKTLEN) as usize;
316
317 assert!(pkt.is_some());
318 let valid = self.rd[self.read_idx].valid();
319
320 self.read_idx = (self.read_idx + 1) % N;
321 if valid {
322 pkt.map(|p| p.slice(0..len))
323 } else {
324 None
325 }
326 } else {
327 None
328 };
329
330 match PacketBox::new(Packet::new()) {
331 Some(b) => {
332 let addr = b.as_ptr() as u32;
333 self.buffers[self.tail_idx].replace(b);
334 self.rd[self.tail_idx].set_ready(addr);
335
336 // "Preceding reads and writes cannot be moved past subsequent writes."
337 fence(Ordering::Release);
338
339 // NOTE(unsafe) atomic write
340 unsafe {
341 ETH.ethernet_dma()
342 .dmacrx_dtpr()
343 .write(|w| w.set_rdt(&self.rd[self.read_idx] as *const _ as u32));
344 }
345
346 self.tail_idx = (self.tail_idx + 1) % N;
347 }
348 None => {}
349 }
350 pkt
351 }
352}
353
354pub struct DescriptorRing<const T: usize, const R: usize> {
355 pub(crate) tx: TDesRing<T>,
356 pub(crate) rx: RDesRing<R>,
357}
358
359impl<const T: usize, const R: usize> DescriptorRing<T, R> {
360 pub const fn new() -> Self {
361 Self {
362 tx: TDesRing::new(),
363 rx: RDesRing::new(),
364 }
365 }
366
367 pub fn init(&mut self) {
368 self.tx.init();
369 self.rx.init();
370 }
371}
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs
new file mode 100644
index 000000000..d54c6e859
--- /dev/null
+++ b/embassy-stm32/src/eth/v2/mod.rs
@@ -0,0 +1,350 @@
1use core::marker::PhantomData;
2use core::pin::Pin;
3use core::sync::atomic::{fence, Ordering};
4
5use embassy::util::{AtomicWaker, Unborrow};
6use embassy_extras::peripheral::{PeripheralMutex, PeripheralState};
7use embassy_extras::unborrow;
8use embassy_net::MTU;
9
10use crate::gpio::sealed::Pin as __GpioPin;
11use crate::gpio::AnyPin;
12use crate::gpio::Pin as GpioPin;
13use crate::interrupt::Interrupt;
14use crate::pac::gpio::vals::Ospeedr;
15use crate::pac::ETH;
16use crate::peripherals;
17
18mod descriptors;
19use descriptors::DescriptorRing;
20
21/// Station Management Interface (SMI) on an ethernet PHY
22pub trait StationManagement {
23 /// Read a register over SMI.
24 fn smi_read(&mut self, reg: u8) -> u16;
25 /// Write a register over SMI.
26 fn smi_write(&mut self, reg: u8, val: u16);
27}
28
29/// Traits for an Ethernet PHY
30pub trait PHY {
31 /// Reset PHY and wait for it to come out of reset.
32 fn phy_reset(&mut self);
33 /// PHY initialisation.
34 fn phy_init(&mut self);
35}
36
37pub struct Ethernet<'d, T: Instance, const TX: usize, const RX: usize> {
38 state: PeripheralMutex<Inner<'d, T, TX, RX>>,
39 pins: [AnyPin; 9],
40}
41
42impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX> {
43 pub fn new(
44 peri: impl Unborrow<Target = T> + 'd,
45 interrupt: impl Unborrow<Target = T::Interrupt> + 'd,
46 ref_clk: impl Unborrow<Target = impl RefClkPin<T>> + 'd,
47 mdio: impl Unborrow<Target = impl MDIOPin<T>> + 'd,
48 mdc: impl Unborrow<Target = impl MDCPin<T>> + 'd,
49 crs: impl Unborrow<Target = impl CRSPin<T>> + 'd,
50 rx_d0: impl Unborrow<Target = impl RXD0Pin<T>> + 'd,
51 rx_d1: impl Unborrow<Target = impl RXD1Pin<T>> + 'd,
52 tx_d0: impl Unborrow<Target = impl TXD0Pin<T>> + 'd,
53 tx_d1: impl Unborrow<Target = impl TXD1Pin<T>> + 'd,
54 tx_en: impl Unborrow<Target = impl TXEnPin<T>> + 'd,
55 mac_addr: [u8; 6],
56 ) -> Self {
57 unborrow!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
58
59 ref_clk.configure();
60 mdio.configure();
61 mdc.configure();
62 crs.configure();
63 rx_d0.configure();
64 rx_d1.configure();
65 tx_d0.configure();
66 tx_d1.configure();
67 tx_en.configure();
68
69 let inner = Inner::new(peri);
70 let state = PeripheralMutex::new(inner, interrupt);
71
72 // NOTE(unsafe) We have exclusive access to the registers
73 unsafe {
74 let dma = ETH.ethernet_dma();
75 let mac = ETH.ethernet_mac();
76 let mtl = ETH.ethernet_mtl();
77
78 // Reset and wait
79 dma.dmamr().modify(|w| w.set_swr(true));
80 while dma.dmamr().read().swr() {}
81
82 // 200 MHz ?
83 mac.mac1ustcr().modify(|w| w.set_tic_1us_cntr(200 - 1));
84
85 mac.maccr().modify(|w| {
86 w.set_ipg(0b000); // 96 bit times
87 w.set_acs(true);
88 w.set_fes(true);
89 w.set_dm(true);
90 // TODO: Carrier sense ? ECRSFD
91 });
92
93 mac.maca0lr().write(|w| {
94 w.set_addrlo(
95 u32::from(mac_addr[0])
96 | (u32::from(mac_addr[1]) << 8)
97 | (u32::from(mac_addr[2]) << 16)
98 | (u32::from(mac_addr[3]) << 24),
99 )
100 });
101 mac.maca0hr()
102 .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));
103
104 // TODO: Enable filtering once we get the basics working
105 mac.macpfr().modify(|w| w.set_ra(true));
106 mac.macqtx_fcr().modify(|w| w.set_pt(0x100));
107
108 mtl.mtlrx_qomr().modify(|w| w.set_rsf(true));
109 mtl.mtltx_qomr().modify(|w| w.set_tsf(true));
110
111 // TODO: Address aligned beats plus fixed burst ?
112 dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ?
113 dma.dmacrx_cr().modify(|w| {
114 w.set_rxpbl(1); // 32 ?
115 w.set_rbsz(MTU as u16);
116 });
117 }
118
119 let pins = [
120 ref_clk.degrade(),
121 mdio.degrade(),
122 mdc.degrade(),
123 crs.degrade(),
124 rx_d0.degrade(),
125 rx_d1.degrade(),
126 tx_d0.degrade(),
127 tx_d1.degrade(),
128 tx_en.degrade(),
129 ];
130
131 Self { state, pins }
132 }
133
134 pub fn init(self: Pin<&mut Self>) {
135 // NOTE(unsafe) We won't move this
136 let this = unsafe { self.get_unchecked_mut() };
137 let mutex = unsafe { Pin::new_unchecked(&mut this.state) };
138
139 mutex.with(|s, _| {
140 s.desc_ring.init();
141
142 fence(Ordering::SeqCst);
143
144 unsafe {
145 let mac = ETH.ethernet_mac();
146 let mtl = ETH.ethernet_mtl();
147 let dma = ETH.ethernet_dma();
148
149 mac.maccr().modify(|w| {
150 w.set_re(true);
151 w.set_te(true);
152 });
153 mtl.mtltx_qomr().modify(|w| w.set_ftq(true));
154
155 dma.dmactx_cr().modify(|w| w.set_st(true));
156 dma.dmacrx_cr().modify(|w| w.set_sr(true));
157
158 // Enable interrupts
159 dma.dmacier().modify(|w| {
160 w.set_nie(true);
161 w.set_rie(true);
162 w.set_tie(true);
163 });
164 }
165 });
166 }
167}
168
169impl<'d, T: Instance, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, TX, RX> {
170 fn drop(&mut self) {
171 for pin in self.pins.iter_mut() {
172 // NOTE(unsafe) Exclusive access to the regs
173 critical_section::with(|_| unsafe {
174 pin.set_as_analog();
175 pin.block()
176 .ospeedr()
177 .modify(|w| w.set_ospeedr(pin.pin() as usize, Ospeedr::LOWSPEED));
178 })
179 }
180 }
181}
182
183//----------------------------------------------------------------------
184
185struct Inner<'d, T: Instance, const TX: usize, const RX: usize> {
186 _peri: PhantomData<&'d mut T>,
187 desc_ring: DescriptorRing<TX, RX>,
188}
189
190impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> {
191 pub fn new(_peri: impl Unborrow<Target = T> + 'd) -> Self {
192 Self {
193 _peri: PhantomData,
194 desc_ring: DescriptorRing::new(),
195 }
196 }
197}
198
199impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> {
200 type Interrupt = T::Interrupt;
201
202 fn on_interrupt(&mut self) {
203 unwrap!(self.desc_ring.tx.on_interrupt());
204 self.desc_ring.rx.on_interrupt();
205
206 T::state().wake();
207
208 // TODO: Check and clear more flags
209 unsafe {
210 let dma = ETH.ethernet_dma();
211
212 dma.dmacsr().modify(|w| {
213 w.set_ti(false);
214 w.set_ri(false);
215 });
216 // Delay two peripheral's clock
217 dma.dmacsr().read();
218 dma.dmacsr().read();
219 }
220 }
221}
222
223mod sealed {
224 use super::*;
225
226 pub trait Instance {
227 type Interrupt: Interrupt;
228
229 fn state() -> &'static AtomicWaker;
230 }
231
232 pub trait RefClkPin<T: Instance>: GpioPin {
233 fn configure(&mut self);
234 }
235
236 pub trait MDIOPin<T: Instance>: GpioPin {
237 fn configure(&mut self);
238 }
239
240 pub trait MDCPin<T: Instance>: GpioPin {
241 fn configure(&mut self);
242 }
243
244 pub trait CRSPin<T: Instance>: GpioPin {
245 fn configure(&mut self);
246 }
247
248 pub trait RXD0Pin<T: Instance>: GpioPin {
249 fn configure(&mut self);
250 }
251
252 pub trait RXD1Pin<T: Instance>: GpioPin {
253 fn configure(&mut self);
254 }
255
256 pub trait TXD0Pin<T: Instance>: GpioPin {
257 fn configure(&mut self);
258 }
259
260 pub trait TXD1Pin<T: Instance>: GpioPin {
261 fn configure(&mut self);
262 }
263
264 pub trait TXEnPin<T: Instance>: GpioPin {
265 fn configure(&mut self);
266 }
267}
268
269pub trait Instance: sealed::Instance + 'static {}
270
271pub trait RefClkPin<T: Instance>: sealed::RefClkPin<T> + 'static {}
272
273pub trait MDIOPin<T: Instance>: sealed::MDIOPin<T> + 'static {}
274
275pub trait MDCPin<T: Instance>: sealed::MDCPin<T> + 'static {}
276
277pub trait CRSPin<T: Instance>: sealed::CRSPin<T> + 'static {}
278
279pub trait RXD0Pin<T: Instance>: sealed::RXD0Pin<T> + 'static {}
280
281pub trait RXD1Pin<T: Instance>: sealed::RXD1Pin<T> + 'static {}
282
283pub trait TXD0Pin<T: Instance>: sealed::TXD0Pin<T> + 'static {}
284
285pub trait TXD1Pin<T: Instance>: sealed::TXD1Pin<T> + 'static {}
286
287pub trait TXEnPin<T: Instance>: sealed::TXEnPin<T> + 'static {}
288
289crate::pac::peripherals!(
290 (eth, $inst:ident) => {
291 impl sealed::Instance for peripherals::$inst {
292 type Interrupt = crate::interrupt::$inst;
293
294 fn state() -> &'static AtomicWaker {
295 static WAKER: AtomicWaker = AtomicWaker::new();
296 &WAKER
297 }
298 }
299
300 impl Instance for peripherals::$inst {}
301 };
302);
303
304macro_rules! impl_pin {
305 ($inst:ident, $pin:ident, $signal:ident, $af:expr) => {
306 impl sealed::$signal<peripherals::$inst> for peripherals::$pin {
307 fn configure(&mut self) {
308 // NOTE(unsafe) Exclusive access to the registers
309 critical_section::with(|_| unsafe {
310 self.set_as_af($af);
311 self.block()
312 .ospeedr()
313 .modify(|w| w.set_ospeedr(self.pin() as usize, Ospeedr::VERYHIGHSPEED));
314 })
315 }
316 }
317
318 impl $signal<peripherals::$inst> for peripherals::$pin {}
319 };
320}
321
322crate::pac::peripheral_pins!(
323 ($inst:ident, eth, ETH, $pin:ident, REF_CLK, $af:expr) => {
324 impl_pin!($inst, $pin, RefClkPin, $af);
325 };
326 ($inst:ident, eth, ETH, $pin:ident, MDIO, $af:expr) => {
327 impl_pin!($inst, $pin, MDIOPin, $af);
328 };
329 ($inst:ident, eth, ETH, $pin:ident, MDC, $af:expr) => {
330 impl_pin!($inst, $pin, MDCPin, $af);
331 };
332 ($inst:ident, eth, ETH, $pin:ident, CRS_DV, $af:expr) => {
333 impl_pin!($inst, $pin, CRSPin, $af);
334 };
335 ($inst:ident, eth, ETH, $pin:ident, RXD0, $af:expr) => {
336 impl_pin!($inst, $pin, RXD0Pin, $af);
337 };
338 ($inst:ident, eth, ETH, $pin:ident, RXD1, $af:expr) => {
339 impl_pin!($inst, $pin, RXD1Pin, $af);
340 };
341 ($inst:ident, eth, ETH, $pin:ident, TXD0, $af:expr) => {
342 impl_pin!($inst, $pin, TXD0Pin, $af);
343 };
344 ($inst:ident, eth, ETH, $pin:ident, TXD1, $af:expr) => {
345 impl_pin!($inst, $pin, TXD1Pin, $af);
346 };
347 ($inst:ident, eth, ETH, $pin:ident, TX_EN, $af:expr) => {
348 impl_pin!($inst, $pin, TXEnPin, $af);
349 };
350);
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 6a08fc580..b99751e6d 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -29,6 +29,8 @@ pub mod clock;
29pub mod dac; 29pub mod dac;
30#[cfg(dma)] 30#[cfg(dma)]
31pub mod dma; 31pub mod dma;
32#[cfg(eth)]
33pub mod eth;
32#[cfg(i2c)] 34#[cfg(i2c)]
33pub mod i2c; 35pub mod i2c;
34#[cfg(pwr)] 36#[cfg(pwr)]