use crate::peripherals::{EMAC_DMA, EMAC_MAC};
fn mdc_csr_clock_range() -> u8 {
#[cfg(esp32p4)]
{
match crate::clock::ll::sys_clk_frequency() {
hz if hz >= 250_000_000 => 5, hz if hz >= 150_000_000 => 4, hz if hz >= 100_000_000 => 1, hz if hz >= 60_000_000 => 0, hz if hz >= 35_000_000 => 3, _ => 2, }
}
#[cfg(not(esp32p4))]
{
3 }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Speed {
_10M,
_100M,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Duplex {
Half,
Full,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LinkState {
pub up: bool,
pub speed: Speed,
pub duplex: Duplex,
}
#[derive(Clone, Copy)]
pub(super) struct EmacRegs;
impl EmacRegs {
pub fn dma_soft_reset(&self) {
EMAC_DMA::regs()
.dmabusmode()
.modify(|_, w| w.sw_rst().set_bit());
while EMAC_DMA::regs().dmabusmode().read().sw_rst().bit_is_set() {}
EMAC_DMA::regs().dmabusmode().modify(|_, w| unsafe {
w.alt_desc_size().set_bit();
w.fixed_burst().set_bit();
w.dmaaddralibea().set_bit();
w.use_sep_pbl().set_bit();
w.prog_burst_len().bits(8);
w.rx_dma_pbl().bits(8);
w
});
}
pub fn dma_start(&self) {
EMAC_DMA::regs().dmaoperation_mode().modify(|_, w| {
w.tx_str_fwd().set_bit();
cfg_select! {
esp32p4 => {
w.fwd_under_gf().set_bit();
}
_ => {
w.rx_store_forward().set_bit();
}
}
w.start_stop_rx().set_bit();
w.start_stop_transmission_command().set_bit()
});
}
#[expect(dead_code)]
pub fn dma_stop(&self) {
EMAC_DMA::regs().dmaoperation_mode().modify(|_, w| {
w.start_stop_rx().clear_bit();
w.start_stop_transmission_command().clear_bit()
});
}
pub fn set_descriptor_lists(&self, tx_base: u32, rx_base: u32) {
unsafe {
EMAC_DMA::regs().dmatxbaseaddr().write(|w| w.bits(tx_base));
EMAC_DMA::regs().dmarxbaseaddr().write(|w| w.bits(rx_base));
}
}
pub fn demand_tx_poll(&self) {
unsafe {
core::ptr::write_volatile(EMAC_DMA::regs().dmatxpolldemand().as_ptr(), 0);
}
}
pub fn demand_rx_poll(&self) {
unsafe {
core::ptr::write_volatile(EMAC_DMA::regs().dmarxpolldemand().as_ptr(), 0);
}
}
pub fn dma_enable_interrupts(&self, enable_tx: bool) {
EMAC_DMA::regs().dmain_en().modify(|_, w| {
w.dmain_rie().set_bit();
w.dmain_aise().set_bit();
w.dmain_nise().set_bit();
w.dmain_tie().bit(enable_tx)
});
}
pub fn dma_disable_interrupts(&self) {
unsafe {
EMAC_DMA::regs().dmain_en().write(|w| w.bits(0));
}
}
#[expect(dead_code)]
pub fn dma_clear_interrupts(&self) -> u32 {
let status = EMAC_DMA::regs().dmastatus().read().bits();
EMAC_DMA::regs()
.dmastatus()
.write(|w| unsafe { w.bits(status) });
status
}
pub fn mac_init(&self, speed: Speed, duplex: Duplex) {
EMAC_MAC::regs().emacconfig().modify(|_, w| {
w.mii().set_bit();
w.fespeed().bit(speed == Speed::_100M);
w.duplex().bit(duplex == Duplex::Full);
w.padcrcstrip().clear_bit();
w.rxipcoffload().set_bit();
w.retry().set_bit();
w.watchdog().set_bit();
w.rxown().set_bit();
w.loopback().clear_bit();
w.deferralcheck().clear_bit();
w.rx().set_bit();
w.tx().set_bit()
});
EMAC_MAC::regs().emacff().modify(|_, w| w.pam().set_bit());
}
pub fn set_speed(&self, speed: Speed) {
EMAC_MAC::regs()
.emacconfig()
.modify(|_, w| w.fespeed().bit(speed == Speed::_100M));
}
pub fn set_duplex(&self, duplex: Duplex) {
EMAC_MAC::regs()
.emacconfig()
.modify(|_, w| w.duplex().bit(duplex == Duplex::Full));
}
pub fn set_mac_address(&self, addr: &[u8; 6]) {
let hi = (addr[5] as u32) << 8 | (addr[4] as u32);
let lo = (addr[3] as u32) << 24
| (addr[2] as u32) << 16
| (addr[1] as u32) << 8
| (addr[0] as u32);
EMAC_MAC::regs().emacaddr0high().write(|w| unsafe {
w.address0_hi().bits(hi as u16);
w.address_enable0().set_bit()
});
EMAC_MAC::regs()
.emacaddr0low()
.write(|w| unsafe { w.bits(lo) });
}
pub fn mdio_read(&self, phy_addr: u8, reg: u8) -> u16 {
EMAC_MAC::regs().emacgmiiaddr().write(|w| unsafe {
w.miidev().bits(phy_addr);
w.miireg().bits(reg);
w.miicsrclk().bits(mdc_csr_clock_range());
w.miiwrite().clear_bit();
w.miibusy().set_bit()
});
self.mdio_wait();
EMAC_MAC::regs().emacmiidata().read().mii_data().bits()
}
pub fn mdio_write(&self, phy_addr: u8, reg: u8, data: u16) {
EMAC_MAC::regs()
.emacmiidata()
.write(|w| unsafe { w.mii_data().bits(data) });
EMAC_MAC::regs().emacgmiiaddr().write(|w| unsafe {
w.miidev().bits(phy_addr);
w.miireg().bits(reg);
w.miicsrclk().bits(mdc_csr_clock_range());
w.miiwrite().set_bit();
w.miibusy().set_bit()
});
self.mdio_wait();
}
fn mdio_wait(&self) {
while EMAC_MAC::regs()
.emacgmiiaddr()
.read()
.miibusy()
.bit_is_set()
{}
}
}