stm32_eth/mac/
mod.rs

1//! Ethernet MAC access and configuration.
2
3use core::ops::{Deref, DerefMut};
4
5use crate::{dma::EthernetDMA, hal::rcc::Clocks, peripherals::ETHERNET_MAC, stm32::ETHERNET_MMC};
6
7mod miim;
8pub use miim::*;
9
10/// Speeds at which this MAC can be configured
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Speed {
14    /// 10Base-T half duplex
15    HalfDuplexBase10T,
16    /// 10Base-T full duplex
17    FullDuplexBase10T,
18    /// 100Base-Tx half duplex
19    HalfDuplexBase100Tx,
20    /// 100Base-Tx full duplex
21    FullDuplexBase100Tx,
22}
23
24mod consts {
25    /* For HCLK 60-100 MHz */
26    pub const ETH_MACMIIAR_CR_HCLK_DIV_42: u8 = 0;
27    /* For HCLK 100-150 MHz */
28    pub const ETH_MACMIIAR_CR_HCLK_DIV_62: u8 = 1;
29    /* For HCLK 20-35 MHz */
30    pub const ETH_MACMIIAR_CR_HCLK_DIV_16: u8 = 2;
31    /* For HCLK 35-60 MHz */
32    pub const ETH_MACMIIAR_CR_HCLK_DIV_26: u8 = 3;
33    /* For HCLK over 150 MHz */
34    pub const ETH_MACMIIAR_CR_HCLK_DIV_102: u8 = 4;
35}
36use self::consts::*;
37
38/// HCLK must be at least 25MHz to use the ethernet peripheral.
39/// This (empty) struct is returned to indicate that it is not set
40/// correctly
41#[derive(Debug)]
42pub struct WrongClock;
43
44/// Ethernet media access control (MAC).
45///
46// impl note: access to the MACIMR register should _only_ be performed
47// atomically.
48pub struct EthernetMAC {
49    eth_mac: ETHERNET_MAC,
50}
51
52impl EthernetMAC {
53    /// Create a new EthernetMAC that does not own its MDIO and MDC pins.
54    ///     
55    /// HCLK must be at least 25MHz, else this function will return `Err(WrongClock)`.
56    ///
57    /// This method does not initialise the external PHY. However, you can access SMI
58    /// `read` and `write` functions through the [`Self::mii`] and [`Self::with_miim`] functions.
59    ///
60    /// Additionally, an `impl` of the [`ieee802_3_miim::Miim`] trait is available
61    /// for PHY communication.
62    pub(crate) fn new(
63        eth_mac: ETHERNET_MAC,
64        eth_mmc: ETHERNET_MMC,
65        clocks: Clocks,
66        initial_speed: Speed,
67        // Note(_dma): this field exists to ensure that the MAC is not
68        // initialized before the DMA. If MAC is started before the DMA,
69        // it doesn't work.
70        _dma: &EthernetDMA,
71    ) -> Result<Self, WrongClock> {
72        let clock_frequency = clocks.hclk().to_Hz();
73
74        let clock_range = match clock_frequency {
75            0..=24_999_999 => return Err(WrongClock),
76            25_000_000..=34_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_16,
77            35_000_000..=59_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_26,
78            60_000_000..=99_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_42,
79            100_000_000..=149_999_999 => ETH_MACMIIAR_CR_HCLK_DIV_62,
80            _ => ETH_MACMIIAR_CR_HCLK_DIV_102,
81        };
82
83        // Set clock range in MAC MII address register
84        eth_mac
85            .macmiiar
86            .modify(|_, w| unsafe { w.cr().bits(clock_range) });
87
88        // Configuration Register
89        eth_mac.maccr.modify(|_, w| {
90            // CRC stripping for Type frames. STM32F1xx do not have this bit.
91            #[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))]
92            let w = w.cstf().set_bit();
93
94            // Fast Ethernet speed
95            w.fes()
96                .set_bit()
97                // Duplex mode
98                .dm()
99                .set_bit()
100                // IPv4 checksum offload
101                .ipco()
102                .set_bit()
103                // Automatic pad/CRC stripping
104                .apcs()
105                .set_bit()
106                // Retry disable in half-duplex mode
107                .rd()
108                .set_bit()
109                // Receiver enable
110                .re()
111                .set_bit()
112                // Transmitter enable
113                .te()
114                .set_bit()
115        });
116
117        // Frame filter register
118        eth_mac.macffr.modify(|_, w| {
119            // Receive All
120            w.ra()
121                .set_bit()
122                // Promiscuous mode
123                .pm()
124                .set_bit()
125        });
126
127        // Flow Control Register
128        eth_mac.macfcr.modify(|_, w| {
129            // Pause time
130            w.pt().bits(0x100)
131        });
132
133        // Disable all MMC RX interrupts
134        eth_mmc
135            .mmcrimr
136            .write(|w| w.rgufm().set_bit().rfaem().set_bit().rfcem().set_bit());
137
138        // Disable all MMC TX interrupts
139        eth_mmc
140            .mmctimr
141            .write(|w| w.tgfm().set_bit().tgfmscm().set_bit().tgfscm().set_bit());
142
143        // Fix incorrect TGFM bit position until https://github.com/stm32-rs/stm32-rs/pull/689
144        // is released and used by HALs.
145        eth_mmc
146            .mmctimr
147            .modify(|r, w| unsafe { w.bits(r.bits() | (1 << 21)) });
148
149        let mut me = Self { eth_mac };
150
151        me.set_speed(initial_speed);
152
153        Ok(me)
154    }
155
156    /// Borrow access to the MAC's SMI.
157    ///
158    /// Allows for controlling and monitoring any PHYs that may be accessible via the MDIO/MDC
159    /// pins.
160    ///
161    /// Exclusive access to the `MDIO` and `MDC` is required to ensure that are not used elsewhere
162    /// for the duration of Mii communication.
163    pub fn mii<'eth, 'pins, Mdio, Mdc>(
164        &'eth mut self,
165        mdio: &'pins mut Mdio,
166        mdc: &'pins mut Mdc,
167    ) -> Stm32Mii<'eth, 'pins, Mdio, Mdc>
168    where
169        Mdio: MdioPin,
170        Mdc: MdcPin,
171    {
172        Stm32Mii::new(self, mdio, mdc)
173    }
174
175    /// Turn this [`EthernetMAC`] into an [`EthernetMACWithMii`]
176    pub fn with_mii<MDIO, MDC>(self, mdio: MDIO, mdc: MDC) -> EthernetMACWithMii<MDIO, MDC>
177    where
178        MDIO: MdioPin,
179        MDC: MdcPin,
180    {
181        EthernetMACWithMii {
182            eth_mac: self,
183            mdio,
184            mdc,
185        }
186    }
187
188    /// Set the Ethernet Speed at which the MAC communicates
189    ///
190    /// Note that this does _not_ affect the PHY in any way. To
191    /// configure the PHY, use [`EthernetMACWithMii`] (see: [`Self::with_mii`])
192    /// or [`Stm32Mii`] (see: [`Self::mii`])
193    pub fn set_speed(&mut self, speed: Speed) {
194        self.eth_mac.maccr.modify(|_, w| match speed {
195            Speed::HalfDuplexBase10T => w.fes().clear_bit().dm().clear_bit(),
196            Speed::FullDuplexBase10T => w.fes().clear_bit().dm().set_bit(),
197            Speed::HalfDuplexBase100Tx => w.fes().set_bit().dm().clear_bit(),
198            Speed::FullDuplexBase100Tx => w.fes().set_bit().dm().set_bit(),
199        });
200    }
201
202    /// Get the Ethernet Speed at which the MAC communicates
203    pub fn get_speed(&self) -> Speed {
204        let cr = self.eth_mac.maccr.read();
205        match (cr.fes().bit_is_set(), cr.dm().bit_is_set()) {
206            (false, false) => Speed::HalfDuplexBase10T,
207            (false, true) => Speed::FullDuplexBase10T,
208            (true, false) => Speed::HalfDuplexBase100Tx,
209            (true, true) => Speed::FullDuplexBase100Tx,
210        }
211    }
212
213    #[cfg(feature = "ptp")]
214    pub(crate) fn mask_timestamp_trigger_interrupt() {
215        // SAFETY: MACIMR only receives atomic writes.
216        let mac = &unsafe { &*ETHERNET_MAC::ptr() };
217        mac.macimr.write(|w| w.tstim().set_bit());
218    }
219
220    // NOTE(allow): only used on F4 and F7
221    #[allow(dead_code)]
222    pub(crate) fn unmask_timestamp_trigger_interrupt() {
223        // SAFETY: MACIMR only receives atomic writes.
224        let macimr = &unsafe { &*ETHERNET_MAC::ptr() }.macimr;
225        macimr.write(|w| w.tstim().clear_bit());
226    }
227}
228
229/// Ethernet media access control (MAC) with owned MII
230///
231/// This version of the struct owns it's MII pins,
232/// allowing it to be used directly, instead of requiring
233/// that a  [`Miim`] is created.
234pub struct EthernetMACWithMii<MDIO, MDC>
235where
236    MDIO: MdioPin,
237    MDC: MdcPin,
238{
239    pub(crate) eth_mac: EthernetMAC,
240    mdio: MDIO,
241    mdc: MDC,
242}
243
244impl<MDIO, MDC> EthernetMACWithMii<MDIO, MDC>
245where
246    MDIO: MdioPin,
247    MDC: MdcPin,
248{
249    /// Create a new EthernetMAC with owned MDIO and MDC pins.
250    ///
251    /// To interact with a connected Phy, use the `read` and `write` functions.
252    ///
253    /// Functionality for interacting with PHYs from the `ieee802_3_miim` crate
254    /// is available.
255    pub fn new(eth_mac: EthernetMAC, mdio: MDIO, mdc: MDC) -> Self {
256        Self { eth_mac, mdio, mdc }
257    }
258
259    /// Release the owned MDIO and MDC pins, and return an EthernetMAC that
260    /// has to borrow the MDIO and MDC pins.
261    pub fn release_pins(self) -> (EthernetMAC, MDIO, MDC) {
262        (self.eth_mac, self.mdio, self.mdc)
263    }
264}
265
266impl<MDIO, MDC> Deref for EthernetMACWithMii<MDIO, MDC>
267where
268    MDIO: MdioPin,
269    MDC: MdcPin,
270{
271    type Target = EthernetMAC;
272
273    fn deref(&self) -> &Self::Target {
274        &self.eth_mac
275    }
276}
277
278impl<MDIO, MDC> DerefMut for EthernetMACWithMii<MDIO, MDC>
279where
280    MDIO: MdioPin,
281    MDC: MdcPin,
282{
283    fn deref_mut(&mut self) -> &mut Self::Target {
284        &mut self.eth_mac
285    }
286}
287
288impl<MDIO, MDC> EthernetMACWithMii<MDIO, MDC>
289where
290    MDIO: MdioPin,
291    MDC: MdcPin,
292{
293    /// Read MII register `reg` from the PHY at address `phy`
294    pub fn read(&mut self, phy: u8, reg: u8) -> u16 {
295        self.eth_mac
296            .mii(&mut self.mdio, &mut self.mdc)
297            .read(phy, reg)
298    }
299
300    /// Write the value `data` to MII register `reg` to the PHY at address `phy`
301    pub fn write(&mut self, phy: u8, reg: u8, data: u16) {
302        self.eth_mac
303            .mii(&mut self.mdio, &mut self.mdc)
304            .write(phy, reg, data)
305    }
306}
307
308impl<MDIO, MDC> miim::Miim for EthernetMACWithMii<MDIO, MDC>
309where
310    MDIO: MdioPin,
311    MDC: MdcPin,
312{
313    fn read(&mut self, phy: u8, reg: u8) -> u16 {
314        self.read(phy, reg)
315    }
316
317    fn write(&mut self, phy: u8, reg: u8, data: u16) {
318        self.write(phy, reg, data)
319    }
320}