#[cfg(feature = "stm32f4xx-hal")]
use stm32f4xx_hal::{
bb,
gpio::{
gpioa::{PA1, PA7},
gpiob::{PB11, PB12, PB13},
gpioc::{PC4, PC5},
gpiog::{PG11, PG13, PG14},
Input,
Speed::VeryHigh,
},
pac::{RCC, SYSCFG},
};
#[cfg(feature = "stm32f7xx-hal")]
use cortex_m::interrupt;
#[cfg(feature = "stm32f7xx-hal")]
use stm32f7xx_hal::{
gpio::{
gpioa::{PA1, PA7},
gpiob::{PB11, PB12, PB13},
gpioc::{PC4, PC5},
gpiog::{PG11, PG13, PG14},
Input,
Speed::VeryHigh,
},
pac::{RCC, SYSCFG},
};
use crate::{
dma::EthernetDMA,
stm32::{ETHERNET_DMA, ETHERNET_MAC, ETHERNET_MMC},
};
#[cfg(feature = "ptp")]
use crate::{ptp::EthernetPTP, stm32::ETHERNET_PTP};
pub(crate) fn setup() {
#[cfg(feature = "stm32f4xx-hal")]
unsafe {
const SYSCFG_BIT: u8 = 14;
const ETH_MAC_BIT: u8 = 25;
const ETH_TX_BIT: u8 = 26;
const ETH_RX_BIT: u8 = 27;
const MII_RMII_BIT: u8 = 23;
let rcc = &*RCC::ptr();
let syscfg = &*SYSCFG::ptr();
bb::set(&rcc.apb2enr, SYSCFG_BIT);
if rcc.ahb1enr.read().ethmacen().bit_is_set() {
bb::clear(&rcc.ahb1enr, ETH_MAC_BIT);
}
bb::set(&syscfg.pmc, MII_RMII_BIT);
bb::set(&rcc.ahb1enr, ETH_MAC_BIT);
bb::set(&rcc.ahb1enr, ETH_TX_BIT);
bb::set(&rcc.ahb1enr, ETH_RX_BIT);
bb::set(&rcc.ahb1rstr, ETH_MAC_BIT);
bb::clear(&rcc.ahb1rstr, ETH_MAC_BIT);
}
#[cfg(feature = "stm32f7xx-hal")]
interrupt::free(|_| unsafe {
let rcc = &*RCC::ptr();
let syscfg = &*SYSCFG::ptr();
rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit());
if rcc.ahb1enr.read().ethmacen().bit_is_set() {
rcc.ahb1enr.modify(|_, w| w.ethmacen().clear_bit());
}
syscfg.pmc.modify(|_, w| w.mii_rmii_sel().set_bit());
rcc.ahb1enr.modify(|_, w| {
w.ethmacen()
.set_bit()
.ethmactxen()
.set_bit()
.ethmacrxen()
.set_bit()
});
rcc.ahb1rstr.modify(|_, w| w.ethmacrst().set_bit());
rcc.ahb1rstr.modify(|_, w| w.ethmacrst().clear_bit());
});
#[cfg(feature = "stm32f1xx-hal")]
cortex_m::interrupt::free(|_| unsafe {
let afio = &*crate::stm32::AFIO::ptr();
let rcc = &*crate::stm32::RCC::ptr();
rcc.apb2enr.modify(|_, w| w.afioen().set_bit());
if rcc.ahbenr.read().ethmacen().bit_is_set() {
rcc.ahbenr.modify(|_, w| w.ethmacen().clear_bit());
}
afio.mapr.modify(|_, w| w.mii_rmii_sel().set_bit());
rcc.ahbenr.modify(|_, w| {
w.ethmacen()
.set_bit()
.ethmactxen()
.set_bit()
.ethmacrxen()
.set_bit()
.ethmacen()
.set_bit()
});
rcc.ahbrstr.modify(|_, w| w.ethmacrst().set_bit());
rcc.ahbrstr.modify(|_, w| w.ethmacrst().clear_bit());
if rcc.ahbenr.read().dma1en().is_disabled() && rcc.ahbenr.read().dma2en().is_disabled() {
rcc.ahbenr.modify(|_, w| w.dma2en().enabled());
while rcc.ahbenr.read().dma2en().is_disabled() {}
}
});
}
macro_rules ! pin_trait {
($([$name:ident, $doc:literal, $rm_name:literal]),*) => {
$(
#[doc = concat!($doc, "\n# Safety\nOnly pins specified as `ETH_RMII_", $rm_name, "` in a part's Reference Manual\nmay implement this trait.")]
pub unsafe trait $name {}
)*
}
}
pin_trait!(
[RmiiRefClk, "RMII Reference Clock", "REF_CLK"],
[RmiiCrsDv, "RMII Rx Data Valid", "CRS_DV"],
[RmiiTxEN, "RMII TX Enable", "TX_EN"],
[RmiiTxD0, "RMII TX Data Pin 0", "TXD0"],
[RmiiTxD1, "RMII TX Data Pin 1", "TXD1"],
[RmiiRxD0, "RMII RX Data Pin 0", "RXD0"],
[RmiiRxD1, "RMII RX Data Pin 1", "RXD1"]
);
pub trait AlternateVeryHighSpeed {
fn into_af11_very_high_speed(self);
}
#[allow(missing_docs)]
pub struct PartsIn {
pub mac: ETHERNET_MAC,
pub mmc: ETHERNET_MMC,
pub dma: ETHERNET_DMA,
#[cfg(feature = "ptp")]
pub ptp: ETHERNET_PTP,
}
#[cfg(feature = "ptp")]
impl From<(ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA, ETHERNET_PTP)> for PartsIn {
fn from(value: (ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA, ETHERNET_PTP)) -> Self {
Self {
mac: value.0,
mmc: value.1,
dma: value.2,
ptp: value.3,
}
}
}
#[cfg(not(feature = "ptp"))]
impl From<(ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA)> for PartsIn {
fn from(value: (ETHERNET_MAC, ETHERNET_MMC, ETHERNET_DMA)) -> Self {
Self {
mac: value.0,
mmc: value.1,
dma: value.2,
}
}
}
pub struct Parts<'rx, 'tx, T> {
pub mac: T,
pub dma: EthernetDMA<'rx, 'tx>,
#[cfg(feature = "ptp")]
pub ptp: EthernetPTP,
}
#[cfg(feature = "ptp")]
impl<'rx, 'tx, T> Parts<'rx, 'tx, T> {
pub fn split(self) -> (T, EthernetDMA<'rx, 'tx>, EthernetPTP) {
(self.mac, self.dma, self.ptp)
}
}
#[cfg(not(feature = "ptp"))]
impl<'rx, 'tx, T> Parts<'rx, 'tx, T> {
pub fn split(self) -> (T, EthernetDMA<'rx, 'tx>) {
(self.mac, self.dma)
}
}
#[allow(missing_docs)]
pub struct EthPins<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1> {
pub ref_clk: REFCLK,
pub crs: CRS,
pub tx_en: TXEN,
pub tx_d0: TXD0,
pub tx_d1: TXD1,
pub rx_d0: RXD0,
pub rx_d1: RXD1,
}
impl<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1> EthPins<REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>
where
REFCLK: RmiiRefClk + AlternateVeryHighSpeed,
CRS: RmiiCrsDv + AlternateVeryHighSpeed,
TXEN: RmiiTxEN + AlternateVeryHighSpeed,
TXD0: RmiiTxD0 + AlternateVeryHighSpeed,
TXD1: RmiiTxD1 + AlternateVeryHighSpeed,
RXD0: RmiiRxD0 + AlternateVeryHighSpeed,
RXD1: RmiiRxD1 + AlternateVeryHighSpeed,
{
pub fn setup_pins(self) {
self.ref_clk.into_af11_very_high_speed();
self.crs.into_af11_very_high_speed();
self.tx_en.into_af11_very_high_speed();
self.tx_d0.into_af11_very_high_speed();
self.tx_d1.into_af11_very_high_speed();
self.rx_d0.into_af11_very_high_speed();
self.rx_d1.into_af11_very_high_speed();
}
}
#[allow(unused_macros)]
macro_rules! impl_pins {
( $($traity:ident: [$($pin:ty,)+],)+ ) => {
$(
$(
unsafe impl $traity for $pin {}
impl AlternateVeryHighSpeed for $pin {
fn into_af11_very_high_speed(self) {
self.into_alternate::<11>().set_speed(VeryHigh);
}
}
)+
)+
};
}
#[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))]
impl_pins!(
RmiiRefClk: [
PA1<Input>,
],
RmiiCrsDv: [
PA7<Input>,
],
RmiiTxEN: [
PB11<Input>,
PG11<Input>,
],
RmiiTxD0: [
PB12<Input>,
PG13<Input>,
],
RmiiTxD1: [
PB13<Input>,
PG14<Input>,
],
RmiiRxD0: [
PC4<Input>,
],
RmiiRxD1: [
PC5<Input>,
],
);
#[cfg(feature = "stm32f1xx-hal")]
mod stm32f1 {
use super::*;
use stm32f1xx_hal::gpio::{
gpioa::*, gpiob::*, gpioc::*, gpiod::*, Alternate, Floating, IOPinSpeed, Input,
OutputSpeed, PushPull,
};
macro_rules! impl_pins {
($($type:ident: [$(($PIN:ty, $is_input:literal)),+]),*) => {
$(
$(
unsafe impl $type for $PIN {}
impl AlternateVeryHighSpeed for $PIN {
fn into_af11_very_high_speed(self) {
// Within this critical section, modifying the `CRL` register can
// only be unsound if this critical section preempts other code
// that is modifying the same register
cortex_m::interrupt::free(|_| {
// SAFETY: this is sound as long as the API of the HAL and structure of the CRL
// struct does not change. In case the size of the `CRL` struct is changed, compilation
// will fail as `mem::transmute` can only convert between types of the same size.
//
// This guards us from unsound behaviour introduced by point releases of the f1 hal
let cr: &mut _ = &mut unsafe { core::mem::transmute(()) };
let mut pin = self.into_alternate_push_pull(cr);
pin.set_speed(cr, IOPinSpeed::Mhz50);
if $is_input {
pin.into_floating_input(cr);
}
});
}
}
)+
)*
};
}
impl_pins!(
RmiiRefClk: [(PA1<Input<Floating>>, true)],
RmiiCrsDv: [(PA7<Input<Floating>>, true), (PD8<Input<Floating>>, true)],
RmiiTxEN: [(PB11<Alternate<PushPull>>, false)],
RmiiTxD0: [(PB12<Alternate<PushPull>>, false)],
RmiiTxD1: [(PB13<Alternate<PushPull>>, false)],
RmiiRxD0: [(PC4<Input<Floating>>, true), (PD9<Input<Floating>>, true)],
RmiiRxD1: [(PC5<Input<Floating>>, true), (PD10<Input<Floating>>, true)]
);
}