Skip to content

Commit de4995b

Browse files
Johannes DraaijerJohannes Draaijer
authored andcommitted
Move all SMI functionality into MAC module
Add some docs and make LAN87XXA phy implmementation a bit more clear
1 parent d37b57c commit de4995b

File tree

5 files changed

+213
-192
lines changed

5 files changed

+213
-192
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ travis-ci = { repository = "astro/stm32-eth", branch = "master" }
1515
maintenance = { status = "experimental" }
1616

1717
[package.metadata.docs.rs]
18-
features = ["smoltcp-phy", "stm32f429", "smoltcp/socket-tcp"]
18+
features = ["smoltcp-phy", "smoltcp/socket-raw", "stm32f429"]
1919

2020
[dependencies]
2121
volatile-register = "0.2"

src/lib.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ use hal::rcc::Clocks;
2828
use stm32::{Interrupt, ETHERNET_DMA, ETHERNET_MAC, ETHERNET_MMC, NVIC};
2929

3030
mod ring;
31-
pub mod smi;
3231
pub use ring::RingEntry;
3332
mod desc;
3433
mod mac;
@@ -99,11 +98,11 @@ macro_rules! new {
9998
/// This method does not initialise the external PHY.
10099
///
101100
/// Interacting with a PHY can be done through a struct that implementes the
102-
/// [`smi::StationManagement`] trait.
101+
/// [`StationManagement`] trait.
103102
///
104103
/// In the case of `new_borrowed_smi` you may access SMI through the [`EthernetMAC::smi`] function.
105104
///
106-
/// In the case of `new_no_smi`, the SMI may be accessed by constructing a new [`smi::Smi`] before passing
105+
/// In the case of `new_no_smi`, the SMI may be accessed by constructing a new [`Smi`] before passing
107106
/// the `ETHERNET_MAC` to the `new_no_smi` function.
108107
pub fn $name<'rx, 'tx, REFCLK, CRS, TXEN, TXD0, TXD1, RXD0, RXD1>(
109108
eth_mac: ETHERNET_MAC,
@@ -145,7 +144,7 @@ new!(new_borrowed_smi, BorrowedSmi);
145144
/// [new_unchecked](new_unchecked).
146145
///
147146
/// This method does not initialise the external PHY. The SMI for the external PHY
148-
/// can be accessed through a struct that implements the [`smi::StationManagement`] trait,
147+
/// can be accessed through a struct that implements the [`StationManagement`] trait,
149148
/// which [`EthernetMAC<OwnedSmi>`] does.
150149
///
151150
/// # Note
@@ -178,12 +177,12 @@ where
178177
TXD1: RmiiTxD1 + AlternateVeryHighSpeed,
179178
RXD0: RmiiRxD0 + AlternateVeryHighSpeed,
180179
RXD1: RmiiRxD1 + AlternateVeryHighSpeed,
181-
MDIO: smi::MdioPin,
182-
MDC: smi::MdcPin,
180+
MDIO: MdioPin,
181+
MDC: MdcPin,
183182
{
184183
pins.setup_pins();
185184

186-
let eth_mac = EthernetMAC::new_owned(eth_mac, mdio, mdc);
185+
let eth_mac = EthernetMAC::<OwnedSmi<MDIO, MDC>>::new(eth_mac, mdio, mdc);
187186

188187
unsafe { new_unchecked(eth_mac, eth_mmc, eth_dma, rx_buffer, tx_buffer, clocks) }
189188
}
@@ -192,7 +191,7 @@ where
192191
///
193192
/// This method does not initialise the external PHY. However it does return an
194193
/// [EthernetMAC](EthernetMAC) which implements the
195-
/// [StationManagement](smi::StationManagement) trait. This can be used to
194+
/// [StationManagement](StationManagement) trait. This can be used to
196195
/// communicate with the external PHY.
197196
///
198197
/// # Note

src/mac.rs

Lines changed: 170 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,155 @@
1-
use crate::smi::{self, read, write, StationManagement};
2-
use crate::stm32::ETHERNET_MAC;
1+
use crate::stm32::{
2+
ethernet_mac::{MACMIIAR, MACMIIDR},
3+
ETHERNET_MAC,
4+
};
35

4-
pub struct NoSmi;
6+
/// MDIO pin types.
7+
pub unsafe trait MdioPin {}
8+
/// MDC pin types.
9+
pub unsafe trait MdcPin {}
10+
11+
pub trait StationManagement {
12+
fn read(&self, phy: u8, reg: u8) -> u16;
13+
fn write(&mut self, phy: u8, reg: u8, data: u16);
14+
}
15+
16+
#[inline(always)]
17+
fn smi_wait_ready(iar: &MACMIIAR) {
18+
while iar.read().mb().bit_is_set() {}
19+
}
20+
21+
#[inline(always)]
22+
fn smi_write(iar: &MACMIIAR, dr: &MACMIIDR, phy: u8, reg: u8, data: u16) {
23+
dr.write(|w| {
24+
#[cfg_attr(not(feature = "stm32f107"), allow(unused_unsafe))]
25+
unsafe {
26+
w.md().bits(data)
27+
}
28+
});
29+
30+
iar.modify(|_, w| {
31+
#[cfg_attr(not(feature = "stm32f107"), allow(unused_unsafe))]
32+
unsafe {
33+
w.pa()
34+
.bits(phy)
35+
.mr()
36+
.bits(reg)
37+
/* Write operation MW=1*/
38+
.mw()
39+
.set_bit()
40+
.mb()
41+
.set_bit()
42+
}
43+
});
44+
smi_wait_ready(iar);
45+
}
46+
47+
#[inline(always)]
48+
fn smi_read(iar: &MACMIIAR, dr: &MACMIIDR, phy: u8, reg: u8) -> u16 {
49+
iar.modify(|_, w| {
50+
#[cfg_attr(not(feature = "stm32f107"), allow(unused_unsafe))]
51+
unsafe {
52+
w.pa()
53+
.bits(phy)
54+
.mr()
55+
.bits(reg)
56+
/* Read operation MW=0 */
57+
.mw()
58+
.clear_bit()
59+
.mb()
60+
.set_bit()
61+
}
62+
});
63+
smi_wait_ready(iar);
64+
65+
// Return value:
66+
dr.read().md().bits()
67+
}
68+
69+
/// Station Management Interface with pins and registers borrowed from [`EthernetMAC`].
70+
///
71+
/// Can also be constructed by borrowing from [`ETHERNET_MAC`] and borrowing the pins manually.
72+
///
73+
/// Provides access to the MIIM implementation exposed by the MCU's MAC API.
74+
pub struct Smi<'eth, 'pins, Mdio, Mdc> {
75+
macmiiar: &'eth MACMIIAR,
76+
macmiidr: &'eth MACMIIDR,
77+
_mdio: &'pins mut Mdio,
78+
_mdc: &'pins mut Mdc,
79+
}
80+
81+
impl<'eth, 'pins, Mdio, Mdc> StationManagement for Smi<'eth, 'pins, Mdio, Mdc>
82+
where
83+
Mdio: MdioPin,
84+
Mdc: MdcPin,
85+
{
86+
/// Read an SMI register
87+
fn read(&self, phy: u8, reg: u8) -> u16 {
88+
smi_read(&self.macmiiar, &self.macmiidr, phy, reg)
89+
}
90+
91+
/// Write an SMI register
92+
fn write(&mut self, phy: u8, reg: u8, data: u16) {
93+
smi_write(&self.macmiiar, &self.macmiidr, phy, reg, data)
94+
}
95+
}
96+
97+
impl<'eth, 'pins, Mdio, Mdc> Smi<'eth, 'pins, Mdio, Mdc>
98+
where
99+
Mdio: MdioPin,
100+
Mdc: MdcPin,
101+
{
102+
/// Create the temporary `Smi` instance.
103+
///
104+
/// Temporarily take exclusive access to the MDIO and MDC pins to ensure they are not used
105+
/// elsewhere for the duration of SMI communication.
106+
pub fn new(
107+
macmiiar: &'eth MACMIIAR,
108+
macmiidr: &'eth MACMIIDR,
109+
_mdio: &'pins mut Mdio,
110+
_mdc: &'pins mut Mdc,
111+
) -> Self {
112+
Self {
113+
macmiiar,
114+
macmiidr,
115+
_mdio,
116+
_mdc,
117+
}
118+
}
119+
120+
/// Read an SMI register
121+
pub fn read(&self, phy: u8, reg: u8) -> u16 {
122+
smi_read(&self.macmiiar, &self.macmiidr, phy, reg)
123+
}
124+
}
125+
126+
#[cfg(feature = "stm32f4xx-hal")]
127+
mod pin_impls {
128+
use crate::hal::gpio::{gpioa::PA2, gpioc::PC1, Alternate};
129+
130+
const AF11: u8 = 11;
131+
132+
unsafe impl super::MdioPin for PA2<Alternate<AF11>> {}
133+
unsafe impl super::MdcPin for PC1<Alternate<AF11>> {}
134+
}
135+
136+
#[cfg(feature = "stm32f7xx-hal")]
137+
mod pin_impls {
138+
use crate::hal::gpio::{gpioa::PA2, gpioc::PC1, Alternate};
139+
140+
const AF11: u8 = 11;
141+
142+
unsafe impl super::MdioPin for PA2<Alternate<AF11>> {}
143+
unsafe impl super::MdcPin for PC1<Alternate<AF11>> {}
144+
}
145+
146+
#[cfg(feature = "stm32f1xx-hal")]
147+
mod pin_impls {
148+
use crate::hal::gpio::{gpioa::PA2, gpioc::PC1, Alternate, PushPull};
149+
150+
unsafe impl super::MdioPin for PA2<Alternate<PushPull>> {}
151+
unsafe impl super::MdcPin for PC1<Alternate<PushPull>> {}
152+
}
5153

6154
/// Ethernet media access control (MAC).
7155
pub struct EthernetMAC<SMI> {
@@ -18,12 +166,18 @@ impl EthernetMAC<NoSmi> {
18166
}
19167
}
20168

169+
/// The MAC does not have and does not provide access to its SMI
170+
pub struct NoSmi;
171+
172+
/// Access to the MAC's SMI can be obtained by borrowing [`MdioPin`] and [`MdcPin`] to it. For [`StationManagement`], see [`EthernetMAC::smi`]
21173
pub struct BorrowedSmi;
22174

175+
/// The SMI (and its pins) are owned. An [`EthernetMAC`] that owns
176+
/// its SMI is [`StationManagement`]
23177
pub struct OwnedSmi<MDIO, MDC>
24178
where
25-
MDIO: smi::MdioPin,
26-
MDC: smi::MdcPin,
179+
MDIO: MdioPin,
180+
MDC: MdcPin,
27181
{
28182
_mdio: MDIO,
29183
_mdc: MDC,
@@ -48,21 +202,21 @@ impl EthernetMAC<BorrowedSmi> {
48202
&'eth mut self,
49203
mdio: &'pins mut Mdio,
50204
mdc: &'pins mut Mdc,
51-
) -> smi::Smi<'eth, 'pins, Mdio, Mdc>
205+
) -> Smi<'eth, 'pins, Mdio, Mdc>
52206
where
53-
Mdio: smi::MdioPin,
54-
Mdc: smi::MdcPin,
207+
Mdio: MdioPin,
208+
Mdc: MdcPin,
55209
{
56-
smi::Smi::new(&self.eth_mac.macmiiar, &self.eth_mac.macmiidr, mdio, mdc)
210+
Smi::new(&self.eth_mac.macmiiar, &self.eth_mac.macmiidr, mdio, mdc)
57211
}
58212
}
59213

60214
impl<MDIO, MDC> EthernetMAC<OwnedSmi<MDIO, MDC>>
61215
where
62-
MDIO: smi::MdioPin,
63-
MDC: smi::MdcPin,
216+
MDIO: MdioPin,
217+
MDC: MdcPin,
64218
{
65-
pub fn new_owned(eth_mac: ETHERNET_MAC, mdio: MDIO, mdc: MDC) -> Self {
219+
pub fn new(eth_mac: ETHERNET_MAC, mdio: MDIO, mdc: MDC) -> Self {
66220
Self {
67221
eth_mac,
68222
_state: OwnedSmi {
@@ -75,15 +229,15 @@ where
75229

76230
impl<MDIO, MDC> StationManagement for EthernetMAC<OwnedSmi<MDIO, MDC>>
77231
where
78-
MDIO: smi::MdioPin,
79-
MDC: smi::MdcPin,
232+
MDIO: MdioPin,
233+
MDC: MdcPin,
80234
{
81235
fn read(&self, phy: u8, reg: u8) -> u16 {
82-
read(&self.eth_mac.macmiiar, &self.eth_mac.macmiidr, phy, reg)
236+
smi_read(&self.eth_mac.macmiiar, &self.eth_mac.macmiidr, phy, reg)
83237
}
84238

85239
fn write(&mut self, phy: u8, reg: u8, data: u16) {
86-
write(
240+
smi_write(
87241
&self.eth_mac.macmiiar,
88242
&self.eth_mac.macmiidr,
89243
phy,

src/phy/lan87xxa.rs

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
use core::convert::TryFrom;
44
use num_enum::{IntoPrimitive, TryFromPrimitive};
55

6-
use crate::smi::StationManagement;
6+
use crate::mac::StationManagement;
7+
8+
/// SMSC LAN8720A Ethernet PHY
9+
pub type LAN8720A<SMI> = LAN87xxA<SMI, false>;
10+
/// SMSC LAN8742A Ethernet PHY
11+
pub type LAN8742A<SMI> = LAN87xxA<SMI, true>;
712

813
/// The link speeds supported by this PHY
914
#[derive(Clone, Copy, Debug, IntoPrimitive, TryFromPrimitive)]
@@ -65,17 +70,22 @@ mod phy_consts {
6570
pub const PHY_REG_SSR_100BASE_FD: u16 = 0b110 << 2;
6671
}
6772

68-
/// SMSC LAN8742A Ethernet PHY
69-
pub struct LAN87xxA<S> {
73+
/// An SMSC LAN87XXA Ethernet PHY.
74+
///
75+
/// EXT_WUCSR_CLEAR is used to determine if the "WU CSR" bit
76+
/// in extended registers should be cleared
77+
///
78+
/// This type should not be used directly. Use [`LAN8720A`] or [`LAN8742A`] instead.
79+
pub struct LAN87xxA<S, const EXT_WUCSR_CLEAR: bool> {
7080
phy_addr: u8,
7181
smi: S,
7282
}
7383

74-
impl<S> LAN87xxA<S>
84+
impl<S, const EXT_WUCSR_CLEAR: bool> LAN87xxA<S, EXT_WUCSR_CLEAR>
7585
where
7686
S: StationManagement,
7787
{
78-
///
88+
/// Create a new LAN87XXA based PHY
7989
pub fn new(smi: S, phy_addr: u8) -> Self {
8090
LAN87xxA { smi, phy_addr }
8191
}
@@ -84,6 +94,16 @@ where
8494
self.smi.write(self.phy_addr, reg, data)
8595
}
8696

97+
/// Writes a value to an extended PHY register in MMD address space.
98+
///
99+
/// Only available in `LAN8742A` PHYs
100+
fn smi_write_ext(&mut self, reg_addr: u16, reg_data: u16) {
101+
self.write(PHY_REG_CTL, 0x0003); // set address
102+
self.write(PHY_REG_ADDAR, reg_addr);
103+
self.write(PHY_REG_CTL, 0x4003); // set data
104+
self.write(PHY_REG_ADDAR, reg_data);
105+
}
106+
87107
fn read(&mut self, reg: u8) -> u16 {
88108
self.smi.read(self.phy_addr, reg)
89109
}
@@ -94,11 +114,12 @@ where
94114
while self.read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}
95115
}
96116

97-
/// PHY initialisation.
117+
/// Initialize the PHY
98118
pub fn phy_init(&mut self) {
99-
// Clear WU CSR
100-
#[cfg(feature = "phy-lan8742a")]
101-
self.smi_write_ext(PHY_REG_WUCSR, 0);
119+
if EXT_WUCSR_CLEAR {
120+
// Clear WU CSR
121+
self.smi_write_ext(PHY_REG_WUCSR, 0);
122+
}
102123

103124
// Enable auto-negotiation
104125
self.write(
@@ -147,25 +168,18 @@ where
147168
LinkSpeed::try_from(link_data).ok()
148169
}
149170

150-
///
171+
/// Check if the link is up
151172
pub fn link_established(&mut self) -> bool {
152173
self.poll_link()
153174
}
154175

155-
///
176+
/// Block until a link is established
156177
pub fn block_until_link(&mut self) {
157178
while !self.link_established() {}
158179
}
159180

160-
#[cfg_attr(
161-
feature = "phy-lan8742a",
162-
doc = "Writes a value to an extended PHY register in MMD address space"
163-
)]
164-
#[cfg(feature = "phy-lan8742a")]
165-
fn smi_write_ext(&mut self, reg_addr: u16, reg_data: u16) {
166-
self.write(PHY_REG_CTL, 0x0003); // set address
167-
self.write(PHY_REG_ADDAR, reg_addr);
168-
self.write(PHY_REG_CTL, 0x4003); // set data
169-
self.write(PHY_REG_ADDAR, reg_data);
181+
/// Release the underlying [`StationManagement`]
182+
pub fn release(self) -> S {
183+
self.smi
170184
}
171185
}

0 commit comments

Comments
 (0)