diff options
| author | Raul Alimbekov <[email protected]> | 2025-12-16 09:05:22 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-16 09:05:22 +0300 |
| commit | c9a04b4b732b7a3b696eb8223664c1a7942b1875 (patch) | |
| tree | 6dbe5c02e66eed8d8762f13f95afd24f8db2b38c /embassy-stm32/src/eth/v2/mod.rs | |
| parent | cde24a3ef1117653ba5ed4184102b33f745782fb (diff) | |
| parent | 5ae6e060ec1c90561719aabdc29d5b6e7b8b0a82 (diff) | |
Merge branch 'main' into main
Diffstat (limited to 'embassy-stm32/src/eth/v2/mod.rs')
| -rw-r--r-- | embassy-stm32/src/eth/v2/mod.rs | 201 |
1 files changed, 97 insertions, 104 deletions
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index cf7a9901b..7f92e351c 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | mod descriptors; | 1 | mod descriptors; |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::sync::atomic::{Ordering, fence}; |
| 4 | use core::sync::atomic::{fence, Ordering}; | ||
| 5 | 4 | ||
| 6 | use embassy_hal_internal::Peri; | 5 | use embassy_hal_internal::Peri; |
| 7 | use stm32_metapac::syscfg::vals::EthSelPhy; | 6 | use stm32_metapac::syscfg::vals::EthSelPhy; |
| @@ -12,7 +11,6 @@ use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; | |||
| 12 | use crate::interrupt; | 11 | use crate::interrupt; |
| 13 | use crate::interrupt::InterruptExt; | 12 | use crate::interrupt::InterruptExt; |
| 14 | use crate::pac::ETH; | 13 | use crate::pac::ETH; |
| 15 | use crate::rcc::SealedRccPeripheral; | ||
| 16 | 14 | ||
| 17 | /// Interrupt handler. | 15 | /// Interrupt handler. |
| 18 | pub struct InterruptHandler {} | 16 | pub 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. |
| 50 | enum Pins<'d> { | 47 | enum 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 | ||
| 55 | macro_rules! config_pins { | 52 | macro_rules! config_pins { |
| @@ -63,41 +60,96 @@ macro_rules! config_pins { | |||
| 63 | }; | 60 | }; |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | 63 | impl<'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); | 133 | impl<'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,29 +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 | 184 | config_pins!( |
| 135 | critical_section::with(|_| { | 185 | rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en |
| 136 | crate::pac::RCC.ahb1enr().modify(|w| { | 186 | ); |
| 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!(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); | ||
| 148 | 187 | ||
| 149 | let pins = Pins::Mii([ | 188 | let pins = Pins::Mii([ |
| 150 | rx_clk.into(), | 189 | rx_clk.into(), |
| 151 | tx_clk.into(), | 190 | tx_clk.into(), |
| 152 | mdio.into(), | ||
| 153 | mdc.into(), | ||
| 154 | rxdv.into(), | 191 | rxdv.into(), |
| 155 | rx_d0.into(), | 192 | rx_d0.into(), |
| 156 | rx_d1.into(), | 193 | rx_d1.into(), |
| @@ -163,7 +200,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 163 | tx_en.into(), | 200 | tx_en.into(), |
| 164 | ]); | 201 | ]); |
| 165 | 202 | ||
| 166 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 203 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::MII_GMII) |
| 167 | } | 204 | } |
| 168 | 205 | ||
| 169 | fn new_inner<const TX: usize, const RX: usize>( | 206 | fn new_inner<const TX: usize, const RX: usize>( |
| @@ -173,7 +210,19 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 173 | pins: Pins<'d>, | 210 | pins: Pins<'d>, |
| 174 | phy: P, | 211 | phy: P, |
| 175 | mac_addr: [u8; 6], | 212 | mac_addr: [u8; 6], |
| 213 | eth_sel_phy: EthSelPhy, | ||
| 176 | ) -> 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 | |||
| 177 | let dma = T::regs().ethernet_dma(); | 226 | let dma = T::regs().ethernet_dma(); |
| 178 | let mac = T::regs().ethernet_mac(); | 227 | let mac = T::regs().ethernet_mac(); |
| 179 | let mtl = T::regs().ethernet_mtl(); | 228 | let mtl = T::regs().ethernet_mtl(); |
| @@ -235,32 +284,12 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 235 | w.set_rbsz(RX_BUFFER_SIZE as u16); | 284 | w.set_rbsz(RX_BUFFER_SIZE as u16); |
| 236 | }); | 285 | }); |
| 237 | 286 | ||
| 238 | let hclk = <T as SealedRccPeripheral>::frequency(); | ||
| 239 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 240 | |||
| 241 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 242 | let clock_range = match hclk_mhz { | ||
| 243 | 0..=34 => 2, // Divide by 16 | ||
| 244 | 35..=59 => 3, // Divide by 26 | ||
| 245 | 60..=99 => 0, // Divide by 42 | ||
| 246 | 100..=149 => 1, // Divide by 62 | ||
| 247 | 150..=249 => 4, // Divide by 102 | ||
| 248 | 250..=310 => 5, // Divide by 124 | ||
| 249 | _ => { | ||
| 250 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 251 | } | ||
| 252 | }; | ||
| 253 | |||
| 254 | let mut this = Self { | 287 | let mut this = Self { |
| 255 | _peri: peri, | 288 | _peri: peri, |
| 256 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), | 289 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 257 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | 290 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| 258 | pins, | 291 | pins, |
| 259 | phy, | 292 | phy, |
| 260 | station_management: EthernetStationManagement { | ||
| 261 | peri: PhantomData, | ||
| 262 | clock_range: clock_range, | ||
| 263 | }, | ||
| 264 | mac_addr, | 293 | mac_addr, |
| 265 | }; | 294 | }; |
| 266 | 295 | ||
| @@ -286,8 +315,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 286 | w.set_tie(true); | 315 | w.set_tie(true); |
| 287 | }); | 316 | }); |
| 288 | 317 | ||
| 289 | this.phy.phy_reset(&mut this.station_management); | 318 | this.phy.phy_reset(); |
| 290 | this.phy.phy_init(&mut this.station_management); | 319 | this.phy.phy_init(); |
| 291 | 320 | ||
| 292 | interrupt::ETH.unpend(); | 321 | interrupt::ETH.unpend(); |
| 293 | unsafe { interrupt::ETH.enable() }; | 322 | unsafe { interrupt::ETH.enable() }; |
| @@ -296,42 +325,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | |||
| 296 | } | 325 | } |
| 297 | } | 326 | } |
| 298 | 327 | ||
| 299 | /// Ethernet SMI driver. | ||
| 300 | pub struct EthernetStationManagement<T: Instance> { | ||
| 301 | peri: PhantomData<T>, | ||
| 302 | clock_range: u8, | ||
| 303 | } | ||
| 304 | |||
| 305 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { | ||
| 306 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | ||
| 307 | let mac = T::regs().ethernet_mac(); | ||
| 308 | |||
| 309 | mac.macmdioar().modify(|w| { | ||
| 310 | w.set_pa(phy_addr); | ||
| 311 | w.set_rda(reg); | ||
| 312 | w.set_goc(0b11); // read | ||
| 313 | w.set_cr(self.clock_range); | ||
| 314 | w.set_mb(true); | ||
| 315 | }); | ||
| 316 | while mac.macmdioar().read().mb() {} | ||
| 317 | mac.macmdiodr().read().md() | ||
| 318 | } | ||
| 319 | |||
| 320 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { | ||
| 321 | let mac = T::regs().ethernet_mac(); | ||
| 322 | |||
| 323 | mac.macmdiodr().write(|w| w.set_md(val)); | ||
| 324 | mac.macmdioar().modify(|w| { | ||
| 325 | w.set_pa(phy_addr); | ||
| 326 | w.set_rda(reg); | ||
| 327 | w.set_goc(0b01); // write | ||
| 328 | w.set_cr(self.clock_range); | ||
| 329 | w.set_mb(true); | ||
| 330 | }); | ||
| 331 | while mac.macmdioar().read().mb() {} | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { | 328 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { |
| 336 | fn drop(&mut self) { | 329 | fn drop(&mut self) { |
| 337 | let dma = T::regs().ethernet_dma(); | 330 | let dma = T::regs().ethernet_dma(); |
