stm32_eth/
setup.rs

1//! Pin definitions and setup functionality.
2//!
3//! This module contains the unsafe traits that determine
4//! which pins can have a specific function, and provides
5//! functionality for setting up clocks and the MAC peripheral
6
7#[cfg(feature = "stm32f4xx-hal")]
8use stm32f4xx_hal::{
9    bb,
10    gpio::{
11        gpioa::{PA1, PA7},
12        gpiob::{PB11, PB12, PB13},
13        gpioc::{PC4, PC5},
14        gpiog::{PG11, PG13, PG14},
15        Input,
16        Speed::VeryHigh,
17    },
18    pac::{RCC, SYSCFG},
19};
20
21#[cfg(feature = "stm32f7xx-hal")]
22use cortex_m::interrupt;
23
24#[cfg(feature = "stm32f7xx-hal")]
25use stm32f7xx_hal::{
26    gpio::{
27        gpioa::{PA1, PA7},
28        gpiob::{PB11, PB12, PB13},
29        gpioc::{PC4, PC5},
30        gpiog::{PG11, PG13, PG14},
31        Input,
32        Speed::VeryHigh,
33    },
34    pac::{RCC, SYSCFG},
35};
36
37use crate::{
38    dma::EthernetDMA,
39    stm32::{ETHERNET_DMA, ETHERNET_MAC, ETHERNET_MMC},
40};
41
42#[cfg(feature = "ptp")]
43use crate::{ptp::EthernetPTP, stm32::ETHERNET_PTP};
44
45// Enable syscfg and ethernet clocks. Reset the Ethernet MAC.
46pub(crate) fn setup() {
47    #[cfg(feature = "stm32f4xx-hal")]
48    unsafe {
49        const SYSCFG_BIT: u8 = 14;
50        const ETH_MAC_BIT: u8 = 25;
51        const ETH_TX_BIT: u8 = 26;
52        const ETH_RX_BIT: u8 = 27;
53        const MII_RMII_BIT: u8 = 23;
54
55        //NOTE(unsafe) This will only be used for atomic writes with no side-effects
56        let rcc = &*RCC::ptr();
57        let syscfg = &*SYSCFG::ptr();
58
59        // Enable syscfg clock
60        bb::set(&rcc.apb2enr, SYSCFG_BIT);
61
62        if rcc.ahb1enr.read().ethmacen().bit_is_set() {
63            // pmc must be changed with the ethernet controller disabled or under reset
64            bb::clear(&rcc.ahb1enr, ETH_MAC_BIT);
65        }
66        // select MII or RMII mode
67        // 0 = MII, 1 = RMII
68        bb::set(&syscfg.pmc, MII_RMII_BIT);
69
70        // enable ethernet clocks
71        bb::set(&rcc.ahb1enr, ETH_MAC_BIT);
72        bb::set(&rcc.ahb1enr, ETH_TX_BIT);
73        bb::set(&rcc.ahb1enr, ETH_RX_BIT);
74
75        // reset pulse
76        bb::set(&rcc.ahb1rstr, ETH_MAC_BIT);
77        bb::clear(&rcc.ahb1rstr, ETH_MAC_BIT);
78    }
79    #[cfg(feature = "stm32f7xx-hal")]
80    //stm32f7xx-hal does not currently have bitbanding
81    interrupt::free(|_| unsafe {
82        //NOTE(unsafe) Interrupt free and we only modify mac bits
83        let rcc = &*RCC::ptr();
84        let syscfg = &*SYSCFG::ptr();
85        // enable syscfg clock
86        rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit());
87
88        if rcc.ahb1enr.read().ethmacen().bit_is_set() {
89            // pmc must be changed with the ethernet controller disabled or under reset
90            rcc.ahb1enr.modify(|_, w| w.ethmacen().clear_bit());
91        }
92
93        // select MII or RMII mode
94        // 0 = MII, 1 = RMII
95        syscfg.pmc.modify(|_, w| w.mii_rmii_sel().set_bit());
96
97        // enable ethernet clocks
98        rcc.ahb1enr.modify(|_, w| {
99            w.ethmacen()
100                .set_bit()
101                .ethmactxen()
102                .set_bit()
103                .ethmacrxen()
104                .set_bit()
105        });
106
107        //reset pulse
108        rcc.ahb1rstr.modify(|_, w| w.ethmacrst().set_bit());
109        rcc.ahb1rstr.modify(|_, w| w.ethmacrst().clear_bit());
110    });
111
112    #[cfg(feature = "stm32f1xx-hal")]
113    cortex_m::interrupt::free(|_| unsafe {
114        let afio = &*crate::stm32::AFIO::ptr();
115        let rcc = &*crate::stm32::RCC::ptr();
116
117        // enable AFIO clock
118        rcc.apb2enr.modify(|_, w| w.afioen().set_bit());
119
120        if rcc.ahbenr.read().ethmacen().bit_is_set() {
121            // ethernet controller must be disabled when configuring mapr
122            rcc.ahbenr.modify(|_, w| w.ethmacen().clear_bit());
123        }
124
125        // select MII or RMII mode
126        // 0 = MII, 1 = RMII
127        afio.mapr.modify(|_, w| w.mii_rmii_sel().set_bit());
128
129        // enable ethernet clocks
130        rcc.ahbenr.modify(|_, w| {
131            w.ethmacen()
132                .set_bit()
133                .ethmactxen()
134                .set_bit()
135                .ethmacrxen()
136                .set_bit()
137                .ethmacen()
138                .set_bit()
139        });
140
141        // Reset pulse.
142        rcc.ahbrstr.modify(|_, w| w.ethmacrst().set_bit());
143        rcc.ahbrstr.modify(|_, w| w.ethmacrst().clear_bit());
144
145        // Workaround for the issue mentioned in the Errata (2.20.11) related to wfi:
146        //
147        // "
148        // If a WFI/WFE instruction is executed to put the system in sleep mode while the Ethernet
149        // MAC master clock on the AHB bus matrix is ON and all remaining masters clocks are OFF,
150        // the Ethernet DMA is unable to perform any AHB master accesses during sleep mode.
151        //
152        // Workaround: Enable DMA1 or DMA2 clocks in the RCC_AHBENR register before executing the
153        // WFI/WFE instruction.
154        // "
155        if rcc.ahbenr.read().dma1en().is_disabled() && rcc.ahbenr.read().dma2en().is_disabled() {
156            rcc.ahbenr.modify(|_, w| w.dma2en().enabled());
157            while rcc.ahbenr.read().dma2en().is_disabled() {}
158        }
159    });
160}
161
162macro_rules ! pin_trait {
163    ($([$name:ident, $doc:literal, $rm_name:literal]),*) => {
164        $(
165        #[doc = concat!($doc, "\n# Safety\nOnly pins specified as `ETH_RMII_", $rm_name, "` in a part's Reference Manual\nmay implement this trait.")]
166        pub unsafe trait $name {}
167        )*
168    }
169}
170
171pin_trait!(
172    [RmiiRefClk, "RMII Reference Clock", "REF_CLK"],
173    [RmiiCrsDv, "RMII Rx Data Valid", "CRS_DV"],
174    [RmiiTxEN, "RMII TX Enable", "TX_EN"],
175    [RmiiTxD0, "RMII TX Data Pin 0", "TXD0"],
176    [RmiiTxD1, "RMII TX Data Pin 1", "TXD1"],
177    [RmiiRxD0, "RMII RX Data Pin 0", "RXD0"],
178    [RmiiRxD1, "RMII RX Data Pin 1", "RXD1"]
179);
180
181/// Trait needed to setup the pins for the Ethernet peripheral.
182pub trait AlternateVeryHighSpeed {
183    /// Puts the pin in the Alternate Function 11 with Very High Speed.
184    fn into_af11_very_high_speed(self);
185}
186
187/// A struct that contains all peripheral parts required to configure
188/// the ethernet peripheral.
189#[allow(missing_docs)]
190pub struct PartsIn {
191    pub mac: ETHERNET_MAC,
192    pub mmc: ETHERNET_MMC,
193    pub dma: ETHERNET_DMA,
194    #[cfg(feature = "ptp")]
195    pub ptp: ETHERNET_PTP,
196}
197
198#[cfg(feature = "ptp")]
199impl From<(ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA, ETHERNET_PTP)> for PartsIn {
200    fn from(value: (ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA, ETHERNET_PTP)) -> Self {
201        Self {
202            mac: value.0,
203            mmc: value.1,
204            dma: value.2,
205            ptp: value.3,
206        }
207    }
208}
209
210#[cfg(not(feature = "ptp"))]
211impl From<(ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA)> for PartsIn {
212    fn from(value: (ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA)) -> Self {
213        Self {
214            mac: value.0,
215            mmc: value.1,
216            dma: value.2,
217        }
218    }
219}
220
221/// Access to all configured parts of the ethernet peripheral.
222pub struct Parts<'rx, 'tx, T> {
223    /// Access to and control over the ethernet MAC.
224    pub mac: T,
225    /// Access to and control over the ethernet DMA.
226    pub dma: EthernetDMA<'rx, 'tx>,
227    /// Access to and control over the ethernet PTP module.
228    #[cfg(feature = "ptp")]
229    pub ptp: EthernetPTP,
230}
231
232#[cfg(feature = "ptp")]
233impl<'rx, 'tx, T> Parts<'rx, 'tx, T> {
234    /// Split this [`Parts`] into its components.
235    pub fn split(self) -> (T, EthernetDMA<'rx, 'tx>, EthernetPTP) {
236        (self.mac, self.dma, self.ptp)
237    }
238}
239
240#[cfg(not(feature = "ptp"))]
241impl<'rx, 'tx, T> Parts<'rx, 'tx, T> {
242    /// Split this [`Parts`] into its components.
243    pub fn split(self) -> (T, EthernetDMA<'rx, 'tx>) {
244        (self.mac, self.dma)
245    }
246}
247
248/// A struct that represents a combination of pins to be used
249/// as RMII pins for the ethernet peripheral(s)
250// NOTE(missing_docs): all fields of this struct are self-explanatory
251#[allow(missing_docs)]
252pub struct EthPins<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1> {
253    pub ref_clk: REFCLK,
254    pub crs: CRS,
255    pub tx_en: TXEN,
256    pub tx_d0: TXD0,
257    pub tx_d1: TXD1,
258    pub rx_d0: RXD0,
259    pub rx_d1: RXD1,
260}
261
262impl<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1> EthPins<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>
263where
264    REFCLK: RmiiRefClk + AlternateVeryHighSpeed,
265    CRS: RmiiCrsDv + AlternateVeryHighSpeed,
266    TXEN: RmiiTxEN + AlternateVeryHighSpeed,
267    TXD0: RmiiTxD0 + AlternateVeryHighSpeed,
268    TXD1: RmiiTxD1 + AlternateVeryHighSpeed,
269    RXD0: RmiiRxD0 + AlternateVeryHighSpeed,
270    RXD1: RmiiRxD1 + AlternateVeryHighSpeed,
271{
272    /// Pin setup.
273    ///
274    /// Set RMII pins to
275    /// * Alternate function 11
276    /// * High-speed
277    ///
278    /// This function consumes the pins so that you cannot use them
279    /// anywhere else by accident.
280    pub fn setup_pins(self) {
281        self.ref_clk.into_af11_very_high_speed();
282        self.crs.into_af11_very_high_speed();
283        self.tx_en.into_af11_very_high_speed();
284        self.tx_d0.into_af11_very_high_speed();
285        self.tx_d1.into_af11_very_high_speed();
286        self.rx_d0.into_af11_very_high_speed();
287        self.rx_d1.into_af11_very_high_speed();
288    }
289}
290
291#[allow(unused_macros)]
292macro_rules! impl_pins {
293    ( $($traity:ident: [$($pin:ty,)+],)+ ) => {
294        $(
295            $(
296                unsafe impl $traity for $pin {}
297
298                impl AlternateVeryHighSpeed for $pin {
299                    fn into_af11_very_high_speed(self) {
300                        self.into_alternate::<11>().set_speed(VeryHigh);
301                    }
302                }
303            )+
304        )+
305    };
306}
307
308#[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))]
309impl_pins!(
310    RmiiRefClk: [
311        PA1<Input>,
312    ],
313    RmiiCrsDv: [
314        PA7<Input>,
315    ],
316    RmiiTxEN: [
317        PB11<Input>,
318        PG11<Input>,
319    ],
320    RmiiTxD0: [
321        PB12<Input>,
322        PG13<Input>,
323    ],
324    RmiiTxD1: [
325        PB13<Input>,
326        PG14<Input>,
327    ],
328    RmiiRxD0: [
329        PC4<Input>,
330    ],
331    RmiiRxD1: [
332        PC5<Input>,
333    ],
334);
335
336#[cfg(feature = "stm32f1xx-hal")]
337mod stm32f1 {
338    use super::*;
339    use stm32f1xx_hal::gpio::{
340        gpioa::*, gpiob::*, gpioc::*, gpiod::*, Alternate, Floating, IOPinSpeed, Input,
341        OutputSpeed, PushPull,
342    };
343
344    // STM32F1xx's require access to the CRL/CRH registers to change pin mode. As a result, we
345    // require that pins are already in the necessary mode before constructing `EthPins` as it
346    // would be inconvenient to pass CRL and CRH through to the `AlternateVeryHighSpeed` callsite.
347
348    macro_rules! impl_pins {
349        ($($type:ident: [$(($PIN:ty, $is_input:literal)),+]),*) => {
350            $(
351                $(
352                    unsafe impl $type for $PIN {}
353                    impl AlternateVeryHighSpeed for $PIN {
354                        fn into_af11_very_high_speed(self) {
355                            // Within this critical section, modifying the `CRL` register can
356                            // only be unsound if this critical section preempts other code
357                            // that is modifying the same register
358                            cortex_m::interrupt::free(|_| {
359                                // SAFETY: this is sound as long as the API of the HAL and structure of the CRL
360                                // struct does not change. In case the size of the `CRL` struct is changed, compilation
361                                // will fail as `mem::transmute` can only convert between types of the same size.
362                                //
363                                // This guards us from unsound behaviour introduced by point releases of the f1 hal
364                                let cr: &mut _ = &mut unsafe { core::mem::transmute(()) };
365                                // The speed can only be changed on output pins
366                                let mut pin = self.into_alternate_push_pull(cr);
367                                pin.set_speed(cr, IOPinSpeed::Mhz50);
368
369                                if $is_input {
370                                    pin.into_floating_input(cr);
371                                }
372                            });
373                        }
374                    }
375                )+
376            )*
377        };
378    }
379
380    impl_pins!(
381        RmiiRefClk: [(PA1<Input<Floating>>, true)],
382        RmiiCrsDv: [(PA7<Input<Floating>>, true), (PD8<Input<Floating>>, true)],
383        RmiiTxEN: [(PB11<Alternate<PushPull>>, false)],
384        RmiiTxD0: [(PB12<Alternate<PushPull>>, false)],
385        RmiiTxD1: [(PB13<Alternate<PushPull>>, false)],
386        RmiiRxD0: [(PC4<Input<Floating>>, true), (PD9<Input<Floating>>, true)],
387        RmiiRxD1: [(PC5<Input<Floating>>, true), (PD10<Input<Floating>>, true)]
388    );
389}