diff options
| author | Thales Fragoso <[email protected]> | 2021-06-10 02:38:59 -0300 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2021-06-16 16:48:35 +0200 |
| commit | 4cffa200bd976fd415e3419426f5029b2c72cfd2 (patch) | |
| tree | 45e8814f4fb324de38a3b8315b81e33ca3a3b94e /embassy-stm32/src/eth | |
| parent | 46e1bae9e36917c2e763082730d99df302c1c625 (diff) | |
eth: Add lan8742a PHY
Diffstat (limited to 'embassy-stm32/src/eth')
| -rw-r--r-- | embassy-stm32/src/eth/lan8742a.rs | 103 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/mod.rs | 27 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/v2/mod.rs | 94 |
3 files changed, 204 insertions, 20 deletions
diff --git a/embassy-stm32/src/eth/lan8742a.rs b/embassy-stm32/src/eth/lan8742a.rs new file mode 100644 index 000000000..74d0ca5de --- /dev/null +++ b/embassy-stm32/src/eth/lan8742a.rs | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | //! SMSC LAN8742A Ethernet PHY | ||
| 2 | |||
| 3 | use super::{StationManagement, PHY}; | ||
| 4 | |||
| 5 | #[allow(dead_code)] | ||
| 6 | mod phy_consts { | ||
| 7 | pub const PHY_REG_BCR: u8 = 0x00; | ||
| 8 | pub const PHY_REG_BSR: u8 = 0x01; | ||
| 9 | pub const PHY_REG_ID1: u8 = 0x02; | ||
| 10 | pub const PHY_REG_ID2: u8 = 0x03; | ||
| 11 | pub const PHY_REG_ANTX: u8 = 0x04; | ||
| 12 | pub const PHY_REG_ANRX: u8 = 0x05; | ||
| 13 | pub const PHY_REG_ANEXP: u8 = 0x06; | ||
| 14 | pub const PHY_REG_ANNPTX: u8 = 0x07; | ||
| 15 | pub const PHY_REG_ANNPRX: u8 = 0x08; | ||
| 16 | pub const PHY_REG_SSR: u8 = 0x1F; // Special Status Register | ||
| 17 | pub const PHY_REG_CTL: u8 = 0x0D; // Ethernet PHY Register Control | ||
| 18 | pub const PHY_REG_ADDAR: u8 = 0x0E; // Ethernet PHY Address or Data | ||
| 19 | |||
| 20 | pub const PHY_REG_WUCSR: u16 = 0x8010; | ||
| 21 | |||
| 22 | pub const PHY_REG_BCR_COLTEST: u16 = 1 << 7; | ||
| 23 | pub const PHY_REG_BCR_FD: u16 = 1 << 8; | ||
| 24 | pub const PHY_REG_BCR_ANRST: u16 = 1 << 9; | ||
| 25 | pub const PHY_REG_BCR_ISOLATE: u16 = 1 << 10; | ||
| 26 | pub const PHY_REG_BCR_POWERDN: u16 = 1 << 11; | ||
| 27 | pub const PHY_REG_BCR_AN: u16 = 1 << 12; | ||
| 28 | pub const PHY_REG_BCR_100M: u16 = 1 << 13; | ||
| 29 | pub const PHY_REG_BCR_LOOPBACK: u16 = 1 << 14; | ||
| 30 | pub const PHY_REG_BCR_RESET: u16 = 1 << 15; | ||
| 31 | |||
| 32 | pub const PHY_REG_BSR_JABBER: u16 = 1 << 1; | ||
| 33 | pub const PHY_REG_BSR_UP: u16 = 1 << 2; | ||
| 34 | pub const PHY_REG_BSR_FAULT: u16 = 1 << 4; | ||
| 35 | pub const PHY_REG_BSR_ANDONE: u16 = 1 << 5; | ||
| 36 | |||
| 37 | pub const PHY_REG_SSR_ANDONE: u16 = 1 << 12; | ||
| 38 | pub const PHY_REG_SSR_SPEED: u16 = 0b111 << 2; | ||
| 39 | pub const PHY_REG_SSR_10BASE_HD: u16 = 0b001 << 2; | ||
| 40 | pub const PHY_REG_SSR_10BASE_FD: u16 = 0b101 << 2; | ||
| 41 | pub const PHY_REG_SSR_100BASE_HD: u16 = 0b010 << 2; | ||
| 42 | pub const PHY_REG_SSR_100BASE_FD: u16 = 0b110 << 2; | ||
| 43 | } | ||
| 44 | use self::phy_consts::*; | ||
| 45 | |||
| 46 | /// SMSC LAN8742A Ethernet PHY | ||
| 47 | pub struct LAN8742A; | ||
| 48 | |||
| 49 | unsafe impl PHY for LAN8742A { | ||
| 50 | /// Reset PHY and wait for it to come out of reset. | ||
| 51 | fn phy_reset<S: StationManagement>(sm: &mut S) { | ||
| 52 | sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_RESET); | ||
| 53 | while sm.smi_read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} | ||
| 54 | } | ||
| 55 | |||
| 56 | /// PHY initialisation. | ||
| 57 | fn phy_init<S: StationManagement>(sm: &mut S) { | ||
| 58 | // Clear WU CSR | ||
| 59 | Self::smi_write_ext(sm, PHY_REG_WUCSR, 0); | ||
| 60 | |||
| 61 | // Enable auto-negotiation | ||
| 62 | sm.smi_write( | ||
| 63 | PHY_REG_BCR, | ||
| 64 | PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, | ||
| 65 | ); | ||
| 66 | } | ||
| 67 | |||
| 68 | fn poll_link<S: StationManagement>(sm: &mut S) -> bool { | ||
| 69 | let bsr = sm.smi_read(PHY_REG_BSR); | ||
| 70 | let ssr = sm.smi_read(PHY_REG_SSR); | ||
| 71 | |||
| 72 | // No link without autonegotiate | ||
| 73 | if bsr & PHY_REG_BSR_ANDONE == 0 { | ||
| 74 | return false; | ||
| 75 | } | ||
| 76 | // No link if link is down | ||
| 77 | if bsr & PHY_REG_BSR_UP == 0 { | ||
| 78 | return false; | ||
| 79 | } | ||
| 80 | // No link if autonegotiate incomplete | ||
| 81 | if ssr & PHY_REG_SSR_ANDONE == 0 { | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | // No link if other side isn't 100Mbps full duplex | ||
| 85 | if ssr & PHY_REG_SSR_SPEED != PHY_REG_SSR_100BASE_FD { | ||
| 86 | return false; | ||
| 87 | } | ||
| 88 | |||
| 89 | // Got link | ||
| 90 | true | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Public functions for the LAN8742A | ||
| 95 | impl LAN8742A { | ||
| 96 | // Writes a value to an extended PHY register in MMD address space | ||
| 97 | fn smi_write_ext<S: StationManagement>(sm: &mut S, reg_addr: u16, reg_data: u16) { | ||
| 98 | sm.smi_write(PHY_REG_CTL, 0x0003); // set address | ||
| 99 | sm.smi_write(PHY_REG_ADDAR, reg_addr); | ||
| 100 | sm.smi_write(PHY_REG_CTL, 0x4003); // set data | ||
| 101 | sm.smi_write(PHY_REG_ADDAR, reg_data); | ||
| 102 | } | ||
| 103 | } | ||
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 8791e1552..e41ebf4d0 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs | |||
| @@ -3,5 +3,32 @@ | |||
| 3 | #[cfg_attr(eth_v1, path = "v1.rs")] | 3 | #[cfg_attr(eth_v1, path = "v1.rs")] |
| 4 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] | 4 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] |
| 5 | mod _version; | 5 | mod _version; |
| 6 | pub mod lan8742a; | ||
| 6 | 7 | ||
| 7 | pub use _version::*; | 8 | pub use _version::*; |
| 9 | |||
| 10 | /// Station Management Interface (SMI) on an ethernet PHY | ||
| 11 | /// | ||
| 12 | /// # Safety | ||
| 13 | /// | ||
| 14 | /// The methods cannot move out of self | ||
| 15 | pub unsafe trait StationManagement { | ||
| 16 | /// Read a register over SMI. | ||
| 17 | fn smi_read(&mut self, reg: u8) -> u16; | ||
| 18 | /// Write a register over SMI. | ||
| 19 | fn smi_write(&mut self, reg: u8, val: u16); | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Traits for an Ethernet PHY | ||
| 23 | /// | ||
| 24 | /// # Safety | ||
| 25 | /// | ||
| 26 | /// The methods cannot move S | ||
| 27 | pub unsafe trait PHY { | ||
| 28 | /// Reset PHY and wait for it to come out of reset. | ||
| 29 | fn phy_reset<S: StationManagement>(sm: &mut S); | ||
| 30 | /// PHY initialisation. | ||
| 31 | fn phy_init<S: StationManagement>(sm: &mut S); | ||
| 32 | /// Poll link to see if it is up and FD with 100Mbps | ||
| 33 | fn poll_link<S: StationManagement>(sm: &mut S) -> bool; | ||
| 34 | } | ||
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index d54c6e859..67d722d15 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -14,32 +14,21 @@ use crate::interrupt::Interrupt; | |||
| 14 | use crate::pac::gpio::vals::Ospeedr; | 14 | use crate::pac::gpio::vals::Ospeedr; |
| 15 | use crate::pac::ETH; | 15 | use crate::pac::ETH; |
| 16 | use crate::peripherals; | 16 | use crate::peripherals; |
| 17 | use crate::time::Hertz; | ||
| 17 | 18 | ||
| 18 | mod descriptors; | 19 | mod descriptors; |
| 20 | use super::{StationManagement, PHY}; | ||
| 19 | use descriptors::DescriptorRing; | 21 | use descriptors::DescriptorRing; |
| 20 | 22 | ||
| 21 | /// Station Management Interface (SMI) on an ethernet PHY | 23 | pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> { |
| 22 | pub trait StationManagement { | ||
| 23 | /// Read a register over SMI. | ||
| 24 | fn smi_read(&mut self, reg: u8) -> u16; | ||
| 25 | /// Write a register over SMI. | ||
| 26 | fn smi_write(&mut self, reg: u8, val: u16); | ||
| 27 | } | ||
| 28 | |||
| 29 | /// Traits for an Ethernet PHY | ||
| 30 | pub trait PHY { | ||
| 31 | /// Reset PHY and wait for it to come out of reset. | ||
| 32 | fn phy_reset(&mut self); | ||
| 33 | /// PHY initialisation. | ||
| 34 | fn phy_init(&mut self); | ||
| 35 | } | ||
| 36 | |||
| 37 | pub struct Ethernet<'d, T: Instance, const TX: usize, const RX: usize> { | ||
| 38 | state: PeripheralMutex<Inner<'d, T, TX, RX>>, | 24 | state: PeripheralMutex<Inner<'d, T, TX, RX>>, |
| 39 | pins: [AnyPin; 9], | 25 | pins: [AnyPin; 9], |
| 26 | _phy: P, | ||
| 27 | clock_range: u8, | ||
| 28 | phy_addr: u8, | ||
| 40 | } | 29 | } |
| 41 | 30 | ||
| 42 | impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX> { | 31 | impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> { |
| 43 | pub fn new( | 32 | pub fn new( |
| 44 | peri: impl Unborrow<Target = T> + 'd, | 33 | peri: impl Unborrow<Target = T> + 'd, |
| 45 | interrupt: impl Unborrow<Target = T::Interrupt> + 'd, | 34 | interrupt: impl Unborrow<Target = T::Interrupt> + 'd, |
| @@ -52,7 +41,10 @@ impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX> | |||
| 52 | tx_d0: impl Unborrow<Target = impl TXD0Pin<T>> + 'd, | 41 | tx_d0: impl Unborrow<Target = impl TXD0Pin<T>> + 'd, |
| 53 | tx_d1: impl Unborrow<Target = impl TXD1Pin<T>> + 'd, | 42 | tx_d1: impl Unborrow<Target = impl TXD1Pin<T>> + 'd, |
| 54 | tx_en: impl Unborrow<Target = impl TXEnPin<T>> + 'd, | 43 | tx_en: impl Unborrow<Target = impl TXEnPin<T>> + 'd, |
| 44 | phy: P, | ||
| 55 | mac_addr: [u8; 6], | 45 | mac_addr: [u8; 6], |
| 46 | hclk: Hertz, | ||
| 47 | phy_addr: u8, | ||
| 56 | ) -> Self { | 48 | ) -> Self { |
| 57 | unborrow!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 49 | unborrow!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
| 58 | 50 | ||
| @@ -116,6 +108,20 @@ impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX> | |||
| 116 | }); | 108 | }); |
| 117 | } | 109 | } |
| 118 | 110 | ||
| 111 | // Set the MDC clock frequency in the range 1MHz - 2.5MHz | ||
| 112 | let hclk_mhz = hclk.0 / 1_000_000; | ||
| 113 | let clock_range = match hclk_mhz { | ||
| 114 | 0..=34 => 2, // Divide by 16 | ||
| 115 | 35..=59 => 3, // Divide by 26 | ||
| 116 | 60..=99 => 0, // Divide by 42 | ||
| 117 | 100..=149 => 1, // Divide by 62 | ||
| 118 | 150..=249 => 4, // Divide by 102 | ||
| 119 | 250..=310 => 5, // Divide by 124 | ||
| 120 | _ => { | ||
| 121 | panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") | ||
| 122 | } | ||
| 123 | }; | ||
| 124 | |||
| 119 | let pins = [ | 125 | let pins = [ |
| 120 | ref_clk.degrade(), | 126 | ref_clk.degrade(), |
| 121 | mdio.degrade(), | 127 | mdio.degrade(), |
| @@ -128,7 +134,13 @@ impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX> | |||
| 128 | tx_en.degrade(), | 134 | tx_en.degrade(), |
| 129 | ]; | 135 | ]; |
| 130 | 136 | ||
| 131 | Self { state, pins } | 137 | Self { |
| 138 | state, | ||
| 139 | pins, | ||
| 140 | _phy: phy, | ||
| 141 | clock_range, | ||
| 142 | phy_addr, | ||
| 143 | } | ||
| 132 | } | 144 | } |
| 133 | 145 | ||
| 134 | pub fn init(self: Pin<&mut Self>) { | 146 | pub fn init(self: Pin<&mut Self>) { |
| @@ -163,10 +175,52 @@ impl<'d, T: Instance, const TX: usize, const RX: usize> Ethernet<'d, T, TX, RX> | |||
| 163 | }); | 175 | }); |
| 164 | } | 176 | } |
| 165 | }); | 177 | }); |
| 178 | P::phy_reset(this); | ||
| 179 | P::phy_init(this); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement | ||
| 184 | for Ethernet<'d, T, P, TX, RX> | ||
| 185 | { | ||
| 186 | fn smi_read(&mut self, reg: u8) -> u16 { | ||
| 187 | // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` | ||
| 188 | unsafe { | ||
| 189 | let mac = ETH.ethernet_mac(); | ||
| 190 | |||
| 191 | mac.macmdioar().modify(|w| { | ||
| 192 | w.set_pa(self.phy_addr); | ||
| 193 | w.set_rda(reg); | ||
| 194 | w.set_goc(0b11); // read | ||
| 195 | w.set_cr(self.clock_range); | ||
| 196 | w.set_mb(true); | ||
| 197 | }); | ||
| 198 | while mac.macmdioar().read().mb() {} | ||
| 199 | mac.macmdiodr().read().md() | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | fn smi_write(&mut self, reg: u8, val: u16) { | ||
| 204 | // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` | ||
| 205 | unsafe { | ||
| 206 | let mac = ETH.ethernet_mac(); | ||
| 207 | |||
| 208 | mac.macmdiodr().write(|w| w.set_md(val)); | ||
| 209 | mac.macmdioar().modify(|w| { | ||
| 210 | w.set_pa(self.phy_addr); | ||
| 211 | w.set_rda(reg); | ||
| 212 | w.set_goc(0b01); // write | ||
| 213 | w.set_cr(self.clock_range); | ||
| 214 | w.set_mb(true); | ||
| 215 | }); | ||
| 216 | while mac.macmdioar().read().mb() {} | ||
| 217 | } | ||
| 166 | } | 218 | } |
| 167 | } | 219 | } |
| 168 | 220 | ||
| 169 | impl<'d, T: Instance, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, TX, RX> { | 221 | impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop |
| 222 | for Ethernet<'d, T, P, TX, RX> | ||
| 223 | { | ||
| 170 | fn drop(&mut self) { | 224 | fn drop(&mut self) { |
| 171 | for pin in self.pins.iter_mut() { | 225 | for pin in self.pins.iter_mut() { |
| 172 | // NOTE(unsafe) Exclusive access to the regs | 226 | // NOTE(unsafe) Exclusive access to the regs |
