aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/eth/sma/v1.rs
blob: db64a6c78b22ad442396814010134291f2a93ff7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use embassy_hal_internal::Peri;
pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister};
use stm32_metapac::eth::regs;
use stm32_metapac::eth::vals::{Cr, MbProgress, Mw};

use super::{Instance, StationManagement};
use crate::eth::{MDCPin, MDIOPin};
use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};

/// Station Management Agent.
///
/// This peripheral is used for SMI reads and writes to the connected
/// ethernet PHY/device(s).
pub struct Sma<'d, T: Instance> {
    _peri: Peri<'d, T>,
    clock_range: Cr,
    pins: [Peri<'d, AnyPin>; 2],
}

impl<'d, T: Instance> Sma<'d, T> {
    /// Create a new instance of this peripheral.
    pub fn new<#[cfg(afio)] A>(
        peri: Peri<'d, T>,
        mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>,
        mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>,
    ) -> Self {
        set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh));
        set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh));

        // Enable necessary clocks.
        critical_section::with(|_| {
            #[cfg(eth_v1a)]
            let reg = crate::pac::RCC.ahbenr();

            #[cfg(any(eth_v1b, eth_v1c))]
            let reg = crate::pac::RCC.ahb1enr();

            reg.modify(|w| {
                w.set_ethen(true);
            })
        });

        let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() };
        let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not.");
        let hclk_mhz = hclk.0 / 1_000_000;

        // Set the MDC clock frequency in the range 1MHz - 2.5MHz
        let clock_range = match hclk_mhz {
            0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."),
            25..=34 => Cr::CR_20_35,     // Divide by 16
            35..=59 => Cr::CR_35_60,     // Divide by 26
            60..=99 => Cr::CR_60_100,    // Divide by 42
            100..=149 => Cr::CR_100_150, // Divide by 62
            150..=216 => Cr::CR_150_168, // Divide by 102
            _ => {
                panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
            }
        };

        Self {
            _peri: peri,
            clock_range,
            pins: [mdio.into(), mdc.into()],
        }
    }
}

impl<T: Instance> StationManagement for Sma<'_, T> {
    fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
        let (macmiiar, macmiidr) = T::regs();

        macmiiar.modify(|w| {
            w.set_pa(phy_addr);
            w.set_mr(reg);
            w.set_mw(Mw::READ); // read operation
            w.set_cr(self.clock_range);
            w.set_mb(MbProgress::BUSY); // indicate that operation is in progress
        });
        while macmiiar.read().mb() == MbProgress::BUSY {}
        macmiidr.read().md()
    }

    fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
        let (macmiiar, macmiidr) = T::regs();

        macmiidr.write(|w| w.set_md(val));
        macmiiar.modify(|w| {
            w.set_pa(phy_addr);
            w.set_mr(reg);
            w.set_mw(Mw::WRITE); // write
            w.set_cr(self.clock_range);
            w.set_mb(MbProgress::BUSY);
        });
        while macmiiar.read().mb() == MbProgress::BUSY {}
    }
}

impl<T: Instance> Drop for Sma<'_, T> {
    fn drop(&mut self) {
        self.pins.iter_mut().for_each(|p| p.set_as_disconnected());
    }
}