aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/eth/sma
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/eth/sma')
-rw-r--r--embassy-stm32/src/eth/sma/mod.rs42
-rw-r--r--embassy-stm32/src/eth/sma/v1.rs102
-rw-r--r--embassy-stm32/src/eth/sma/v2.rs94
3 files changed, 238 insertions, 0 deletions
diff --git a/embassy-stm32/src/eth/sma/mod.rs b/embassy-stm32/src/eth/sma/mod.rs
new file mode 100644
index 000000000..6c851911d
--- /dev/null
+++ b/embassy-stm32/src/eth/sma/mod.rs
@@ -0,0 +1,42 @@
1//! Station Management Agent (also known as MDIO or SMI).
2
3#![macro_use]
4
5#[cfg_attr(eth_v2, path = "v2.rs")]
6#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1.rs")]
7mod _version;
8
9use embassy_hal_internal::PeripheralType;
10use stm32_metapac::common::{RW, Reg};
11
12pub use self::_version::*;
13
14/// Station Management Interface (SMI).
15pub trait StationManagement {
16 /// Read a register over SMI.
17 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16;
18 /// Write a register over SMI.
19 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16);
20}
21
22trait SealedInstance {
23 fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>);
24}
25
26/// MDIO instance.
27#[allow(private_bounds)]
28pub trait Instance: SealedInstance + PeripheralType + Send + 'static {}
29
30impl SealedInstance for crate::peripherals::ETH_SMA {
31 fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>) {
32 let mac = crate::pac::ETH.ethernet_mac();
33
34 #[cfg(any(eth_v1a, eth_v1b, eth_v1c))]
35 return (mac.macmiiar(), mac.macmiidr());
36
37 #[cfg(eth_v2)]
38 return (mac.macmdioar(), mac.macmdiodr());
39 }
40}
41
42impl Instance for crate::peripherals::ETH_SMA {}
diff --git a/embassy-stm32/src/eth/sma/v1.rs b/embassy-stm32/src/eth/sma/v1.rs
new file mode 100644
index 000000000..db64a6c78
--- /dev/null
+++ b/embassy-stm32/src/eth/sma/v1.rs
@@ -0,0 +1,102 @@
1use embassy_hal_internal::Peri;
2pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister};
3use stm32_metapac::eth::regs;
4use stm32_metapac::eth::vals::{Cr, MbProgress, Mw};
5
6use super::{Instance, StationManagement};
7use crate::eth::{MDCPin, MDIOPin};
8use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
9
10/// Station Management Agent.
11///
12/// This peripheral is used for SMI reads and writes to the connected
13/// ethernet PHY/device(s).
14pub struct Sma<'d, T: Instance> {
15 _peri: Peri<'d, T>,
16 clock_range: Cr,
17 pins: [Peri<'d, AnyPin>; 2],
18}
19
20impl<'d, T: Instance> Sma<'d, T> {
21 /// Create a new instance of this peripheral.
22 pub fn new<#[cfg(afio)] A>(
23 peri: Peri<'d, T>,
24 mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>,
25 mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>,
26 ) -> Self {
27 set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh));
28 set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh));
29
30 // Enable necessary clocks.
31 critical_section::with(|_| {
32 #[cfg(eth_v1a)]
33 let reg = crate::pac::RCC.ahbenr();
34
35 #[cfg(any(eth_v1b, eth_v1c))]
36 let reg = crate::pac::RCC.ahb1enr();
37
38 reg.modify(|w| {
39 w.set_ethen(true);
40 })
41 });
42
43 let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() };
44 let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not.");
45 let hclk_mhz = hclk.0 / 1_000_000;
46
47 // Set the MDC clock frequency in the range 1MHz - 2.5MHz
48 let clock_range = match hclk_mhz {
49 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."),
50 25..=34 => Cr::CR_20_35, // Divide by 16
51 35..=59 => Cr::CR_35_60, // Divide by 26
52 60..=99 => Cr::CR_60_100, // Divide by 42
53 100..=149 => Cr::CR_100_150, // Divide by 62
54 150..=216 => Cr::CR_150_168, // Divide by 102
55 _ => {
56 panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
57 }
58 };
59
60 Self {
61 _peri: peri,
62 clock_range,
63 pins: [mdio.into(), mdc.into()],
64 }
65 }
66}
67
68impl<T: Instance> StationManagement for Sma<'_, T> {
69 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
70 let (macmiiar, macmiidr) = T::regs();
71
72 macmiiar.modify(|w| {
73 w.set_pa(phy_addr);
74 w.set_mr(reg);
75 w.set_mw(Mw::READ); // read operation
76 w.set_cr(self.clock_range);
77 w.set_mb(MbProgress::BUSY); // indicate that operation is in progress
78 });
79 while macmiiar.read().mb() == MbProgress::BUSY {}
80 macmiidr.read().md()
81 }
82
83 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
84 let (macmiiar, macmiidr) = T::regs();
85
86 macmiidr.write(|w| w.set_md(val));
87 macmiiar.modify(|w| {
88 w.set_pa(phy_addr);
89 w.set_mr(reg);
90 w.set_mw(Mw::WRITE); // write
91 w.set_cr(self.clock_range);
92 w.set_mb(MbProgress::BUSY);
93 });
94 while macmiiar.read().mb() == MbProgress::BUSY {}
95 }
96}
97
98impl<T: Instance> Drop for Sma<'_, T> {
99 fn drop(&mut self) {
100 self.pins.iter_mut().for_each(|p| p.set_as_disconnected());
101 }
102}
diff --git a/embassy-stm32/src/eth/sma/v2.rs b/embassy-stm32/src/eth/sma/v2.rs
new file mode 100644
index 000000000..6bc5230b5
--- /dev/null
+++ b/embassy-stm32/src/eth/sma/v2.rs
@@ -0,0 +1,94 @@
1use embassy_hal_internal::Peri;
2pub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister};
3use stm32_metapac::eth::regs;
4
5use super::{Instance, StationManagement};
6use crate::eth::{MDCPin, MDIOPin};
7use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
8
9/// Station Management Agent.
10///
11/// This peripheral is used for SMI reads and writes to the connected
12/// ethernet PHY/device(s).
13pub struct Sma<'d, T: Instance> {
14 _peri: Peri<'d, T>,
15 pins: [Peri<'d, AnyPin>; 2],
16 clock_range: u8,
17}
18
19impl<'d, T: Instance> Sma<'d, T> {
20 /// Create a new instance of this peripheral.
21 pub fn new(peri: Peri<'d, T>, mdio: Peri<'d, impl MDIOPin<T>>, mdc: Peri<'d, impl MDCPin<T>>) -> Self {
22 set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh));
23 set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh));
24
25 // Enable necessary clocks.
26 critical_section::with(|_| {
27 crate::pac::RCC.ahb1enr().modify(|w| {
28 w.set_ethen(true);
29 })
30 });
31
32 let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() };
33 let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not.");
34 let hclk_mhz = hclk.0 / 1_000_000;
35
36 // Set the MDC clock frequency in the range 1MHz - 2.5MHz
37 let clock_range = match hclk_mhz {
38 0..=34 => 2, // Divide by 16
39 35..=59 => 3, // Divide by 26
40 60..=99 => 0, // Divide by 42
41 100..=149 => 1, // Divide by 62
42 150..=249 => 4, // Divide by 102
43 250..=310 => 5, // Divide by 124
44 _ => {
45 panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
46 }
47 };
48
49 Self {
50 _peri: peri,
51 clock_range,
52 pins: [mdio.into(), mdc.into()],
53 }
54 }
55}
56
57impl<T: Instance> StationManagement for Sma<'_, T> {
58 fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
59 let (macmdioar, macmdiodr) = T::regs();
60
61 macmdioar.modify(|w| {
62 w.set_pa(phy_addr);
63 w.set_rda(reg);
64 w.set_goc(0b11); // read
65 w.set_cr(self.clock_range);
66 w.set_mb(true);
67 });
68
69 while macmdioar.read().mb() {}
70
71 macmdiodr.read().md()
72 }
73
74 fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
75 let (macmdioar, macmdiodr) = T::regs();
76
77 macmdiodr.write(|w| w.set_md(val));
78 macmdioar.modify(|w| {
79 w.set_pa(phy_addr);
80 w.set_rda(reg);
81 w.set_goc(0b01); // write
82 w.set_cr(self.clock_range);
83 w.set_mb(true);
84 });
85
86 while macmdioar.read().mb() {}
87 }
88}
89
90impl<T: Instance> Drop for Sma<'_, T> {
91 fn drop(&mut self) {
92 self.pins.iter_mut().for_each(|p| p.set_as_disconnected());
93 }
94}