use core::ptr;
use crate::rcc::{rec, CoreClocks, ResetEnable};
use crate::stm32;
use smoltcp::{
self,
phy::{self, DeviceCapabilities},
time::Instant,
wire::EthernetAddress,
};
use crate::{
ethernet::{PinsRMII, StationManagement},
gpio::Speed,
};
const ETH_BUF_SIZE: usize = 1536;
#[allow(dead_code)]
mod emac_consts {
pub const EMAC_DES3_OWN: u32 = 0x8000_0000;
pub const EMAC_DES3_CTXT: u32 = 0x4000_0000;
pub const EMAC_DES3_FD: u32 = 0x2000_0000;
pub const EMAC_DES3_LD: u32 = 0x1000_0000;
pub const EMAC_DES3_ES: u32 = 0x0000_8000;
pub const EMAC_TDES2_IOC: u32 = 0x8000_0000;
pub const EMAC_RDES3_IOC: u32 = 0x4000_0000;
pub const EMAC_RDES3_PL: u32 = 0x0000_7FFF;
pub const EMAC_RDES3_BUF1V: u32 = 0x0100_0000;
pub const EMAC_TDES2_B1L: u32 = 0x0000_3FFF;
pub const EMAC_DES0_BUF1AP: u32 = 0xFFFF_FFFF;
}
use self::emac_consts::*;
#[derive(Copy, Clone)]
#[repr(C, packed)]
struct TDes {
tdes0: u32,
tdes1: u32,
tdes2: u32,
tdes3: u32,
}
impl TDes {
pub fn init(&mut self) {
self.tdes0 = 0;
self.tdes1 = 0;
self.tdes2 = 0;
self.tdes3 = 0; }
pub fn available(&self) -> bool {
self.tdes3 & EMAC_DES3_OWN == 0
}
}
#[repr(C, packed)]
struct TDesRing<const TD: usize> {
td: [TDes; TD],
tbuf: [[u32; ETH_BUF_SIZE / 4]; TD],
tdidx: usize,
}
impl<const TD: usize> TDesRing<TD> {
const fn new() -> Self {
Self {
td: [TDes {
tdes0: 0,
tdes1: 0,
tdes2: 0,
tdes3: 0,
}; TD],
tbuf: [[0; ETH_BUF_SIZE / 4]; TD],
tdidx: 0,
}
}
pub fn init(&mut self) {
for x in 0..TD {
self.td[x].init();
}
self.tdidx = 0;
unsafe {
let dma = &*stm32::ETHERNET_DMA::ptr();
dma.dmactx_dlar
.write(|w| w.bits(&self.td[0] as *const _ as u32));
dma.dmactx_rlr.write(|w| w.tdrl().bits(TD as u16 - 1));
dma.dmactx_dtpr
.write(|w| w.bits(&self.td[0] as *const _ as u32));
}
}
pub fn available(&self) -> bool {
self.td[self.tdidx].available()
}
pub fn release(&mut self) {
let x = self.tdidx;
assert!(self.td[x].tdes3 & EMAC_DES3_OWN == 0);
let address = ptr::addr_of!(self.tbuf[x]) as u32;
self.td[x].tdes0 = address; self.td[x].tdes1 = 0; assert!(self.td[x].tdes2 & !EMAC_TDES2_B1L == 0); assert!(self.td[x].tdes2 & EMAC_TDES2_B1L > 0); self.td[x].tdes3 = 0;
self.td[x].tdes3 |= EMAC_DES3_FD; self.td[x].tdes3 |= EMAC_DES3_LD; self.td[x].tdes3 |= EMAC_DES3_OWN;
cortex_m::asm::dsb();
let x = (x + 1) % TD;
unsafe {
let dma = &*stm32::ETHERNET_DMA::ptr();
dma.dmactx_dtpr
.write(|w| w.bits(&(self.td[x]) as *const _ as u32));
}
self.tdidx = x;
}
pub unsafe fn buf_as_slice_mut(&mut self, length: usize) -> &mut [u8] {
let x = self.tdidx;
self.td[x].tdes0 = ptr::addr_of!(self.tbuf[x]) as u32;
let len = core::cmp::min(length, ETH_BUF_SIZE);
self.td[x].tdes2 = (length as u32) & EMAC_TDES2_B1L;
let addr = ptr::addr_of_mut!(self.tbuf[x]) as *mut _;
core::slice::from_raw_parts_mut(addr, len)
}
}
#[derive(Copy, Clone)]
#[repr(C, packed)]
struct RDes {
rdes0: u32,
rdes1: u32,
rdes2: u32,
rdes3: u32,
}
impl RDes {
pub fn init(&mut self) {
self.rdes0 = 0;
self.rdes1 = 0;
self.rdes2 = 0;
self.rdes3 = 0; }
pub fn valid(&self) -> bool {
self.rdes3
& (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT)
== (EMAC_DES3_FD | EMAC_DES3_LD)
}
pub fn available(&self) -> bool {
self.rdes3 & EMAC_DES3_OWN == 0 }
}
#[repr(C, packed)]
struct RDesRing<const RD: usize> {
rd: [RDes; RD],
rbuf: [[u32; ETH_BUF_SIZE / 4]; RD],
rdidx: usize,
}
impl<const RD: usize> RDesRing<RD> {
const fn new() -> Self {
Self {
rd: [RDes {
rdes0: 0,
rdes1: 0,
rdes2: 0,
rdes3: 0,
}; RD],
rbuf: [[0; ETH_BUF_SIZE / 4]; RD],
rdidx: 0,
}
}
pub fn init(&mut self) {
for x in 0..RD {
self.rd[x].init();
}
self.rdidx = 0;
unsafe {
let dma = &*stm32::ETHERNET_DMA::ptr();
dma.dmacrx_dlar
.write(|w| w.bits(&self.rd[0] as *const _ as u32));
dma.dmacrx_rlr.write(|w| w.rdrl().bits(RD as u16 - 1));
}
while self.available() {
self.release()
}
}
pub fn available(&self) -> bool {
self.rd[self.rdidx].available()
}
pub fn valid(&self) -> bool {
self.rd[self.rdidx].valid()
}
pub fn release(&mut self) {
let x = self.rdidx;
assert!(self.rd[x].rdes3 & EMAC_DES3_OWN == 0);
let address = ptr::addr_of!(self.rbuf[x]) as u32;
self.rd[x].rdes0 = address; self.rd[x].rdes1 = 0; self.rd[x].rdes2 = 0; self.rd[x].rdes3 = 0;
self.rd[x].rdes3 |= EMAC_DES3_OWN; self.rd[x].rdes3 |= EMAC_RDES3_BUF1V; self.rd[x].rdes3 |= EMAC_RDES3_IOC;
cortex_m::asm::dsb();
unsafe {
let dma = &*stm32::ETHERNET_DMA::ptr();
dma.dmacrx_dtpr
.write(|w| w.bits(&(self.rd[x]) as *const _ as u32));
}
self.rdidx = (x + 1) % RD;
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn buf_as_slice_mut(&self) -> &mut [u8] {
let x = self.rdidx;
let addr = ptr::addr_of!(self.rbuf[x]) as *mut u8;
let len = (self.rd[x].rdes3 & EMAC_RDES3_PL) as usize;
let len = core::cmp::min(len, ETH_BUF_SIZE);
core::slice::from_raw_parts_mut(addr, len)
}
}
pub struct DesRing<const TD: usize, const RD: usize> {
tx: TDesRing<TD>,
rx: RDesRing<RD>,
}
impl<const TD: usize, const RD: usize> DesRing<TD, RD> {
pub const fn new() -> Self {
DesRing {
tx: TDesRing::new(),
rx: RDesRing::new(),
}
}
}
impl<const TD: usize, const RD: usize> Default for DesRing<TD, RD> {
fn default() -> Self {
Self::new()
}
}
pub struct EthernetDMA<'a, const TD: usize, const RD: usize> {
ring: &'a mut DesRing<TD, RD>,
eth_dma: stm32::ETHERNET_DMA,
}
pub struct EthernetMAC {
eth_mac: stm32::ETHERNET_MAC,
eth_phy_addr: u8,
clock_range: u8,
}
pub fn new<'a, const TD: usize, const RD: usize>(
eth_mac: stm32::ETHERNET_MAC,
eth_mtl: stm32::ETHERNET_MTL,
eth_dma: stm32::ETHERNET_DMA,
mut pins: impl PinsRMII,
ring: &'a mut DesRing<TD, RD>,
mac_addr: EthernetAddress,
prec: rec::Eth1Mac,
clocks: &CoreClocks,
) -> (EthernetDMA<'a, TD, RD>, EthernetMAC) {
pins.set_speed(Speed::VeryHigh);
unsafe {
new_unchecked(eth_mac, eth_mtl, eth_dma, ring, mac_addr, prec, clocks)
}
}
pub unsafe fn new_unchecked<'a, const TD: usize, const RD: usize>(
eth_mac: stm32::ETHERNET_MAC,
eth_mtl: stm32::ETHERNET_MTL,
eth_dma: stm32::ETHERNET_DMA,
ring: &'a mut DesRing<TD, RD>,
mac_addr: EthernetAddress,
prec: rec::Eth1Mac,
clocks: &CoreClocks,
) -> (EthernetDMA<'a, TD, RD>, EthernetMAC) {
{
let rcc = &*stm32::RCC::ptr();
let syscfg = &*stm32::SYSCFG::ptr();
rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit());
prec.enable();
rcc.ahb1enr
.modify(|_, w| w.eth1txen().set_bit().eth1rxen().set_bit());
syscfg.pmcr.modify(|_, w| w.epis().bits(0b100)); }
cortex_m::interrupt::free(|_cs| {
eth_dma.dmamr.modify(|_, w| w.swr().set_bit());
while eth_dma.dmamr.read().swr().bit_is_set() {}
eth_mac
.mac1ustcr
.modify(|_, w| w.tic_1us_cntr().bits(200 - 1));
eth_mac.maccr.modify(|_, w| {
w.arpen()
.clear_bit()
.ipc()
.set_bit()
.ipg()
.bits(0b000) .ecrsfd()
.clear_bit()
.dcrs()
.clear_bit()
.bl()
.bits(0b00) .prelen()
.bits(0b00) .cst()
.set_bit()
.fes()
.set_bit()
.dm()
.set_bit()
.acs()
.set_bit()
.dr()
.set_bit()
});
eth_mac.macecr.modify(|_, w| {
w.eipgen()
.clear_bit()
.usp()
.clear_bit()
.spen()
.clear_bit()
.dcrcc()
.clear_bit()
});
eth_mac.maca0hr.write(|w| {
w.addrhi().bits(
u16::from(mac_addr.0[4]) | (u16::from(mac_addr.0[5]) << 8),
)
});
eth_mac.maca0lr.write(|w| {
w.addrlo().bits(
u32::from(mac_addr.0[0])
| (u32::from(mac_addr.0[1]) << 8)
| (u32::from(mac_addr.0[2]) << 16)
| (u32::from(mac_addr.0[3]) << 24),
)
});
eth_mac.macpfr.modify(|_, w| {
w.dntu()
.clear_bit()
.ipfe()
.clear_bit()
.vtfe()
.clear_bit()
.hpf()
.clear_bit()
.saf()
.clear_bit()
.saif()
.clear_bit()
.pcf()
.bits(0b00)
.dbf()
.clear_bit()
.pm()
.clear_bit()
.daif()
.clear_bit()
.hmc()
.clear_bit()
.huc()
.clear_bit()
.ra()
.clear_bit()
.pr()
.clear_bit()
});
eth_mac.macwtr.write(|w| w.pwe().clear_bit());
eth_mac.macqtx_fcr.modify(|_, w| {
w.pt().bits(0x100)
});
eth_mac.macrx_fcr.modify(|_, w| w);
eth_mac.mmc_rx_interrupt_mask.modify(|_, w| {
w.rxlpiuscim()
.set_bit()
.rxucgpim()
.set_bit()
.rxalgnerpim()
.set_bit()
.rxcrcerpim()
.set_bit()
});
eth_mac.mmc_tx_interrupt_mask.modify(|_, w| {
w.txlpiuscim()
.set_bit()
.txgpktim()
.set_bit()
.txmcolgpim()
.set_bit()
.txscolgpim()
.set_bit()
});
eth_mac
.mmc_tx_interrupt_mask
.modify(|r, w| w.bits(r.bits() | (1 << 27)));
eth_mac
.mmc_rx_interrupt_mask
.modify(|r, w| w.bits(r.bits() | (1 << 27)));
eth_mtl.mtlrx_qomr.modify(|_, w| {
w
.rsf()
.set_bit()
.dis_tcp_ef()
.clear_bit()
.fep()
.clear_bit()
.fup()
.clear_bit()
});
eth_mtl.mtltx_qomr.modify(|_, w| {
w
.tsf()
.set_bit()
});
eth_dma.dmamr.modify(|_, w| {
w.intm()
.bits(0b00)
.pr()
.bits(0b000)
.txpr()
.clear_bit()
.da()
.clear_bit()
});
eth_dma.dmasbmr.modify(|_, w| {
w.aal()
.set_bit()
.fb()
.set_bit()
});
eth_dma
.dmaccr
.modify(|_, w| w.dsl().bits(0).pblx8().clear_bit().mss().bits(536));
eth_dma.dmactx_cr.modify(|_, w| {
w
.txpbl()
.bits(32)
.tse()
.clear_bit()
.osf()
.clear_bit()
});
eth_dma.dmacrx_cr.modify(|_, w| {
w
.rbsz()
.bits(ETH_BUF_SIZE as u16)
.rxpbl()
.bits(32)
.rpf()
.clear_bit()
});
ring.tx.init();
ring.rx.init();
cortex_m::asm::dsb();
eth_mac.maccr.modify(|_, w| {
w.re()
.bit(true) .te()
.bit(true) });
eth_mtl.mtltx_qomr.modify(|_, w| w.ftq().set_bit());
eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit());
eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit());
eth_dma
.dmacsr
.modify(|_, w| w.tps().set_bit().rps().set_bit());
});
let hclk_mhz = clocks.hclk().raw() / 1_000_000;
let csr_clock_range = match hclk_mhz {
0..=34 => 2, 35..=59 => 3, 60..=99 => 0, 100..=149 => 1, 150..=249 => 4, 250..=310 => 5, _ => panic!(
"HCLK results in MDC clock > 2.5MHz even for the \
highest CSR clock divider"
),
};
let mac = EthernetMAC {
eth_mac,
eth_phy_addr: 0,
clock_range: csr_clock_range,
};
let dma = EthernetDMA { ring, eth_dma };
(dma, mac)
}
impl EthernetMAC {
pub fn set_phy_addr(self, eth_phy_addr: u8) -> Self {
Self {
eth_mac: self.eth_mac,
eth_phy_addr,
clock_range: self.clock_range,
}
}
}
impl StationManagement for EthernetMAC {
fn smi_read(&mut self, reg: u8) -> u16 {
while self.eth_mac.macmdioar.read().mb().bit_is_set() {}
self.eth_mac.macmdioar.modify(|_, w| unsafe {
w.pa()
.bits(self.eth_phy_addr)
.rda()
.bits(reg)
.goc()
.bits(0b11) .cr()
.bits(self.clock_range)
.mb()
.set_bit()
});
while self.eth_mac.macmdioar.read().mb().bit_is_set() {}
self.eth_mac.macmdiodr.read().md().bits()
}
fn smi_write(&mut self, reg: u8, val: u16) {
while self.eth_mac.macmdioar.read().mb().bit_is_set() {}
self.eth_mac
.macmdiodr
.write(|w| unsafe { w.md().bits(val) });
self.eth_mac.macmdioar.modify(|_, w| unsafe {
w.pa()
.bits(self.eth_phy_addr)
.rda()
.bits(reg)
.goc()
.bits(0b01) .cr()
.bits(self.clock_range)
.mb()
.set_bit()
});
while self.eth_mac.macmdioar.read().mb().bit_is_set() {}
}
}
pub struct TxToken<'a, const TD: usize>(&'a mut TDesRing<TD>);
impl<'a, const TD: usize> phy::TxToken for TxToken<'a, TD> {
fn consume<R, F>(
self,
_timestamp: Instant,
len: usize,
f: F,
) -> smoltcp::Result<R>
where
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
{
assert!(len <= ETH_BUF_SIZE);
let result = f(unsafe { self.0.buf_as_slice_mut(len) });
self.0.release();
result
}
}
pub struct RxToken<'a, const RD: usize>(&'a mut RDesRing<RD>);
impl<'a, const RD: usize> phy::RxToken for RxToken<'a, RD> {
fn consume<R, F>(self, _timestamp: Instant, f: F) -> smoltcp::Result<R>
where
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
{
let result = f(unsafe { self.0.buf_as_slice_mut() });
self.0.release();
result
}
}
impl<'a, const TD: usize, const RD: usize> phy::Device<'a>
for EthernetDMA<'_, TD, RD>
{
type RxToken = RxToken<'a, RD>;
type TxToken = TxToken<'a, TD>;
#[allow(clippy::field_reassign_with_default)]
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1514;
caps.max_burst_size = Some(core::cmp::min(TD, RD));
caps
}
fn receive(&mut self) -> Option<(RxToken<RD>, TxToken<TD>)> {
while self.ring.rx.available() && !self.ring.rx.valid() {
self.ring.rx.release()
}
if self.ring.rx.available() && self.ring.tx.available() {
Some((RxToken(&mut self.ring.rx), TxToken(&mut self.ring.tx)))
} else {
None
}
}
fn transmit(&mut self) -> Option<TxToken<TD>> {
if self.ring.tx.available() {
Some(TxToken(&mut self.ring.tx))
} else {
None
}
}
}
impl<const TD: usize, const RD: usize> EthernetDMA<'_, TD, RD> {
pub fn number_packets_dropped(&self) -> u32 {
self.eth_dma.dmacmfcr.read().mfc().bits() as u32
}
}
pub unsafe fn interrupt_handler() {
let eth_dma = &*stm32::ETHERNET_DMA::ptr();
eth_dma
.dmacsr
.write(|w| w.nis().set_bit().ri().set_bit().ti().set_bit());
let _ = eth_dma.dmacsr.read();
let _ = eth_dma.dmacsr.read(); }
pub unsafe fn enable_interrupt() {
let eth_dma = &*stm32::ETHERNET_DMA::ptr();
eth_dma
.dmacier
.modify(|_, w| w.nie().set_bit().rie().set_bit().tie().set_bit());
}