use super::{MAC_BASE, read_reg, reg_bit_ops, reg_ro, reg_rw, write_reg};
pub const GMACCONFIG_OFFSET: usize = 0x00;
pub const GMACFF_OFFSET: usize = 0x04;
pub const GMACHASTH_OFFSET: usize = 0x08;
pub const GMACHASTL_OFFSET: usize = 0x0C;
pub const GMACMIIADDR_OFFSET: usize = 0x10;
pub const GMACMIIDATA_OFFSET: usize = 0x14;
pub const GMACFC_OFFSET: usize = 0x18;
pub const GMACVLAN_OFFSET: usize = 0x1C;
pub const GMACDEBUG_OFFSET: usize = 0x24;
pub const GMACPMT_OFFSET: usize = 0x2C;
pub const GMACLPI_OFFSET: usize = 0x30;
pub const GMACLPITIMER_OFFSET: usize = 0x34;
pub const GMACINTS_OFFSET: usize = 0x38;
pub const GMACINTMASK_OFFSET: usize = 0x3C;
pub const GMACADDR0H_OFFSET: usize = 0x40;
pub const GMACADDR0L_OFFSET: usize = 0x44;
pub const GMACADDR1H_OFFSET: usize = 0x48;
pub const GMACADDR1L_OFFSET: usize = 0x4C;
pub const GMACADDR2H_OFFSET: usize = 0x50;
pub const GMACADDR2L_OFFSET: usize = 0x54;
pub const GMACADDR3H_OFFSET: usize = 0x58;
pub const GMACADDR3L_OFFSET: usize = 0x5C;
pub const GMACADDR4H_OFFSET: usize = 0x60;
pub const GMACADDR4L_OFFSET: usize = 0x64;
pub const MAC_ADDR_FILTER_COUNT: usize = 4;
pub const GMACADDRH_AE: u32 = 1 << 31;
pub const GMACADDRH_SA: u32 = 1 << 30;
pub const GMACADDRH_MBC_SHIFT: u32 = 24;
pub const GMACADDRH_MBC_MASK: u32 = 0x3F << 24;
pub const GMACAN_OFFSET: usize = 0xC0;
pub const GMACANS_OFFSET: usize = 0xC4;
pub const GMACANA_OFFSET: usize = 0xC8;
pub const GMACANLPA_OFFSET: usize = 0xCC;
pub const GMACANE_OFFSET: usize = 0xD0;
pub const GMACTBI_OFFSET: usize = 0xD4;
pub const GMACSGMII_OFFSET: usize = 0xD8;
pub const GMACCONFIG_PRELEN_SHIFT: u32 = 0;
pub const GMACCONFIG_PRELEN_MASK: u32 = 0x3;
pub const GMACCONFIG_RE: u32 = 1 << 2;
pub const GMACCONFIG_TE: u32 = 1 << 3;
pub const GMACCONFIG_DC: u32 = 1 << 4;
pub const GMACCONFIG_BL_SHIFT: u32 = 5;
pub const GMACCONFIG_BL_MASK: u32 = 0x3 << 5;
pub const GMACCONFIG_ACS: u32 = 1 << 7;
pub const GMACCONFIG_LUD: u32 = 1 << 8;
pub const GMACCONFIG_DR: u32 = 1 << 9;
pub const GMACCONFIG_IPC: u32 = 1 << 10;
pub const GMACCONFIG_DM: u32 = 1 << 11;
pub const GMACCONFIG_LM: u32 = 1 << 12;
pub const GMACCONFIG_DO: u32 = 1 << 13;
pub const GMACCONFIG_FES: u32 = 1 << 14;
pub const GMACCONFIG_PS: u32 = 1 << 15;
pub const GMACCONFIG_DCRS: u32 = 1 << 16;
pub const GMACCONFIG_IFG_SHIFT: u32 = 17;
pub const GMACCONFIG_IFG_MASK: u32 = 0x7 << 17;
pub const GMACCONFIG_JE: u32 = 1 << 20;
pub const GMACCONFIG_BE: u32 = 1 << 21;
pub const GMACCONFIG_JD: u32 = 1 << 22;
pub const GMACCONFIG_WD: u32 = 1 << 23;
pub const GMACCONFIG_TC: u32 = 1 << 24;
pub const GMACCONFIG_CST: u32 = 1 << 25;
pub const GMACCONFIG_SFTERR: u32 = 1 << 26;
pub const GMACCONFIG_TWOKPE: u32 = 1 << 27;
pub const GMACCONFIG_SARC_SHIFT: u32 = 28;
pub const GMACCONFIG_SARC_MASK: u32 = 0x7 << 28;
pub mod ifg {
pub const IFG_96: u32 = 0;
pub const IFG_88: u32 = 1;
pub const IFG_80: u32 = 2;
pub const IFG_72: u32 = 3;
pub const IFG_64: u32 = 4;
pub const IFG_56: u32 = 5;
pub const IFG_48: u32 = 6;
pub const IFG_40: u32 = 7;
}
pub const GMACFF_PR: u32 = 1 << 0;
pub const GMACFF_HUC: u32 = 1 << 1;
pub const GMACFF_HMC: u32 = 1 << 2;
pub const GMACFF_DAIF: u32 = 1 << 3;
pub const GMACFF_PM: u32 = 1 << 4;
pub const GMACFF_DBF: u32 = 1 << 5;
pub const GMACFF_PCF_SHIFT: u32 = 6;
pub const GMACFF_PCF_MASK: u32 = 0x3 << 6;
pub const GMACFF_SAIF: u32 = 1 << 8;
pub const GMACFF_SAF: u32 = 1 << 9;
pub const GMACFF_HPF: u32 = 1 << 10;
pub const GMACFF_VTFE: u32 = 1 << 16;
pub const GMACFF_IPFE: u32 = 1 << 20;
pub const GMACFF_DNTU: u32 = 1 << 21;
pub const GMACFF_RA: u32 = 1 << 31;
pub mod pcf {
pub const NONE: u32 = 0;
pub const ALL_EXCEPT_PAUSE: u32 = 1;
pub const ALL: u32 = 2;
pub const FILTERED: u32 = 3;
}
pub const GMACVLAN_VL_MASK: u32 = 0xFFFF;
pub const GMACVLAN_ETV: u32 = 1 << 16;
pub const GMACVLAN_VTIM: u32 = 1 << 17;
pub const GMACVLAN_ESVL: u32 = 1 << 18;
pub const GMACVLAN_VTHM: u32 = 1 << 19;
pub const GMACMIIADDR_GB: u32 = 1 << 0;
pub const GMACMIIADDR_GW: u32 = 1 << 1;
pub const GMACMIIADDR_CR_SHIFT: u32 = 2;
pub const GMACMIIADDR_CR_MASK: u32 = 0xF << 2;
pub const GMACMIIADDR_GR_SHIFT: u32 = 6;
pub const GMACMIIADDR_GR_MASK: u32 = 0x1F << 6;
pub const GMACMIIADDR_PA_SHIFT: u32 = 11;
pub const GMACMIIADDR_PA_MASK: u32 = 0x1F << 11;
pub mod csr_clock {
pub const DIV_42: u32 = 0;
pub const DIV_62: u32 = 1;
pub const DIV_16: u32 = 2;
pub const DIV_26: u32 = 3;
pub const DIV_102: u32 = 4;
pub const DIV_124: u32 = 5;
}
pub const GMACFC_FCB_BPA: u32 = 1 << 0;
pub const GMACFC_TFE: u32 = 1 << 1;
pub const GMACFC_RFE: u32 = 1 << 2;
pub const GMACFC_UP: u32 = 1 << 3;
pub const GMACFC_PLT_SHIFT: u32 = 4;
pub const GMACFC_PLT_MASK: u32 = 0x3 << 4;
pub const GMACFC_DZPQ: u32 = 1 << 7;
pub const GMACFC_PT_SHIFT: u32 = 16;
pub const GMACFC_PT_MASK: u32 = 0xFFFF << 16;
pub const GMACDEBUG_RXFNE: u32 = 1 << 0;
pub const GMACDEBUG_RXFC_SHIFT: u32 = 1;
pub const GMACDEBUG_RXFC_MASK: u32 = 0x3 << 1;
pub const GMACDEBUG_RXFRCA: u32 = 1 << 4;
pub const GMACDEBUG_RXFWCA: u32 = 1 << 5;
pub const GMACDEBUG_RXFFS_SHIFT: u32 = 8;
pub const GMACDEBUG_RXFFS_MASK: u32 = 0x3 << 8;
pub const GMACDEBUG_TXFNE: u32 = 1 << 16;
pub const GMACDEBUG_TXFWA: u32 = 1 << 17;
pub const GMACDEBUG_TXFRA: u32 = 1 << 20;
pub const GMACDEBUG_TXFC_SHIFT: u32 = 21;
pub const GMACDEBUG_TXFC_MASK: u32 = 0x3 << 21;
pub const GMACDEBUG_TXFNF: u32 = 1 << 24;
pub const GMACDEBUG_TXFF: u32 = 1 << 25;
pub struct MacRegs;
impl MacRegs {
#[inline(always)]
pub const fn base() -> usize {
MAC_BASE
}
reg_rw!(
config,
set_config,
MAC_BASE,
GMACCONFIG_OFFSET,
"GMAC Configuration register"
);
reg_rw!(
frame_filter,
set_frame_filter,
MAC_BASE,
GMACFF_OFFSET,
"Frame Filter register"
);
reg_rw!(
hash_table_high,
set_hash_table_high,
MAC_BASE,
GMACHASTH_OFFSET,
"Hash Table High register"
);
reg_rw!(
hash_table_low,
set_hash_table_low,
MAC_BASE,
GMACHASTL_OFFSET,
"Hash Table Low register"
);
reg_rw!(
mii_address,
set_mii_address,
MAC_BASE,
GMACMIIADDR_OFFSET,
"MII Address register"
);
reg_rw!(
mii_data,
set_mii_data,
MAC_BASE,
GMACMIIDATA_OFFSET,
"MII Data register"
);
reg_rw!(
flow_control,
set_flow_control,
MAC_BASE,
GMACFC_OFFSET,
"Flow Control register"
);
reg_rw!(
vlan_tag,
set_vlan_tag,
MAC_BASE,
GMACVLAN_OFFSET,
"VLAN Tag register"
);
reg_rw!(
interrupt_mask,
set_interrupt_mask,
MAC_BASE,
GMACINTMASK_OFFSET,
"Interrupt Mask register"
);
reg_rw!(
mac_addr0_high,
set_mac_addr0_high,
MAC_BASE,
GMACADDR0H_OFFSET,
"MAC Address 0 High register"
);
reg_rw!(
mac_addr0_low,
set_mac_addr0_low,
MAC_BASE,
GMACADDR0L_OFFSET,
"MAC Address 0 Low register"
);
reg_ro!(debug, MAC_BASE, GMACDEBUG_OFFSET, "Debug register");
reg_ro!(
interrupt_status,
MAC_BASE,
GMACINTS_OFFSET,
"Interrupt Status register"
);
reg_bit_ops!(
enable_tx,
disable_tx,
MAC_BASE,
GMACCONFIG_OFFSET,
GMACCONFIG_TE,
"transmitter",
"Enable",
"Disable"
);
reg_bit_ops!(
enable_rx,
disable_rx,
MAC_BASE,
GMACCONFIG_OFFSET,
GMACCONFIG_RE,
"receiver",
"Enable",
"Disable"
);
#[inline(always)]
pub fn set_duplex_full(full: bool) {
unsafe {
let cfg = read_reg(MAC_BASE + GMACCONFIG_OFFSET);
let cfg = if full {
cfg | GMACCONFIG_DM
} else {
cfg & !GMACCONFIG_DM
};
write_reg(MAC_BASE + GMACCONFIG_OFFSET, cfg);
}
}
#[inline(always)]
pub fn set_speed_100mbps(is_100: bool) {
unsafe {
let cfg = read_reg(MAC_BASE + GMACCONFIG_OFFSET);
let cfg = if is_100 {
cfg | GMACCONFIG_FES
} else {
cfg & !GMACCONFIG_FES
};
write_reg(MAC_BASE + GMACCONFIG_OFFSET, cfg);
}
}
#[inline(always)]
pub fn set_checksum_offload(enable: bool) {
unsafe {
let cfg = read_reg(MAC_BASE + GMACCONFIG_OFFSET);
let cfg = if enable {
cfg | GMACCONFIG_IPC
} else {
cfg & !GMACCONFIG_IPC
};
write_reg(MAC_BASE + GMACCONFIG_OFFSET, cfg);
}
}
#[inline(always)]
pub fn set_promiscuous(enable: bool) {
unsafe {
let ff = read_reg(MAC_BASE + GMACFF_OFFSET);
let ff = if enable {
ff | GMACFF_PR
} else {
ff & !GMACFF_PR
};
write_reg(MAC_BASE + GMACFF_OFFSET, ff);
}
}
#[inline(always)]
pub fn hash_table() -> u64 {
let low = Self::hash_table_low() as u64;
let high = Self::hash_table_high() as u64;
low | (high << 32)
}
#[inline(always)]
pub fn set_hash_table(value: u64) {
Self::set_hash_table_low(value as u32);
Self::set_hash_table_high((value >> 32) as u32);
}
#[inline(always)]
pub fn clear_hash_table() {
Self::set_hash_table_low(0);
Self::set_hash_table_high(0);
}
pub fn compute_hash_index(addr: &[u8; 6]) -> u8 {
const CRC32_POLY: u32 = 0xEDB8_8320;
let mut crc: u32 = 0xFFFF_FFFF;
for byte in addr {
let mut data = *byte;
for _ in 0..8 {
if ((crc ^ data as u32) & 1) != 0 {
crc = (crc >> 1) ^ CRC32_POLY;
} else {
crc >>= 1;
}
data >>= 1;
}
}
(crc & 0x3F) as u8
}
pub fn set_hash_bit(index: u8) {
let index = index & 0x3F;
if index < 32 {
let current = Self::hash_table_low();
Self::set_hash_table_low(current | (1 << index));
} else {
let current = Self::hash_table_high();
Self::set_hash_table_high(current | (1 << (index - 32)));
}
}
pub fn clear_hash_bit(index: u8) {
let index = index & 0x3F;
if index < 32 {
let current = Self::hash_table_low();
Self::set_hash_table_low(current & !(1 << index));
} else {
let current = Self::hash_table_high();
Self::set_hash_table_high(current & !(1 << (index - 32)));
}
}
pub fn is_hash_bit_set(index: u8) -> bool {
let index = index & 0x3F;
if index < 32 {
(Self::hash_table_low() & (1 << index)) != 0
} else {
(Self::hash_table_high() & (1 << (index - 32))) != 0
}
}
pub fn enable_hash_unicast(enable: bool) {
unsafe {
let ff = read_reg(MAC_BASE + GMACFF_OFFSET);
let ff = if enable {
ff | GMACFF_HUC
} else {
ff & !GMACFF_HUC
};
write_reg(MAC_BASE + GMACFF_OFFSET, ff);
}
}
pub fn enable_hash_multicast(enable: bool) {
unsafe {
let ff = read_reg(MAC_BASE + GMACFF_OFFSET);
let ff = if enable {
ff | GMACFF_HMC
} else {
ff & !GMACFF_HMC
};
write_reg(MAC_BASE + GMACFF_OFFSET, ff);
}
}
pub fn set_hash_perfect_filter(enable: bool) {
unsafe {
let ff = read_reg(MAC_BASE + GMACFF_OFFSET);
let ff = if enable {
ff | GMACFF_HPF
} else {
ff & !GMACFF_HPF
};
write_reg(MAC_BASE + GMACFF_OFFSET, ff);
}
}
pub fn enable_vlan_filter(enable: bool) {
unsafe {
let ff = read_reg(MAC_BASE + GMACFF_OFFSET);
let ff = if enable {
ff | GMACFF_VTFE
} else {
ff & !GMACFF_VTFE
};
write_reg(MAC_BASE + GMACFF_OFFSET, ff);
}
}
pub fn configure_vlan_filter(vid: u16, vid_only: bool, inverse: bool, svlan: bool) {
let mut vlan = (vid as u32) & GMACVLAN_VL_MASK;
if vid_only {
vlan |= GMACVLAN_ETV;
}
if inverse {
vlan |= GMACVLAN_VTIM;
}
if svlan {
vlan |= GMACVLAN_ESVL;
}
Self::set_vlan_tag(vlan);
}
pub fn set_vlan_id_filter(vid: u16) {
Self::configure_vlan_filter(vid & 0x0FFF, true, false, false);
}
pub fn get_vlan_id_filter() -> u16 {
(Self::vlan_tag() & 0x0FFF) as u16
}
pub fn clear_vlan_filter() {
Self::set_vlan_tag(0);
Self::enable_vlan_filter(false);
}
pub fn is_vlan_filter_enabled() -> bool {
unsafe { (read_reg(MAC_BASE + GMACFF_OFFSET) & GMACFF_VTFE) != 0 }
}
#[inline(always)]
pub fn is_mii_busy() -> bool {
(Self::mii_address() & GMACMIIADDR_GB) != 0
}
#[inline(always)]
pub fn enable_tx_flow_control(enable: bool) {
unsafe {
let fc = read_reg(MAC_BASE + GMACFC_OFFSET);
let fc = if enable {
fc | GMACFC_TFE
} else {
fc & !GMACFC_TFE
};
write_reg(MAC_BASE + GMACFC_OFFSET, fc);
}
}
#[inline(always)]
pub fn enable_rx_flow_control(enable: bool) {
unsafe {
let fc = read_reg(MAC_BASE + GMACFC_OFFSET);
let fc = if enable {
fc | GMACFC_RFE
} else {
fc & !GMACFC_RFE
};
write_reg(MAC_BASE + GMACFC_OFFSET, fc);
}
}
pub fn configure_flow_control(
pause_time: u16,
plt: u8,
unicast_detect: bool,
tx_enable: bool,
rx_enable: bool,
) {
let mut fc = 0u32;
fc |= (pause_time as u32) << GMACFC_PT_SHIFT;
fc |= ((plt as u32) & 0x3) << GMACFC_PLT_SHIFT;
if unicast_detect {
fc |= GMACFC_UP;
}
if tx_enable {
fc |= GMACFC_TFE;
}
if rx_enable {
fc |= GMACFC_RFE;
}
unsafe { write_reg(MAC_BASE + GMACFC_OFFSET, fc) }
}
pub fn send_pause_frame(activate: bool) {
unsafe {
let fc = read_reg(MAC_BASE + GMACFC_OFFSET);
let fc = if activate {
fc | GMACFC_FCB_BPA
} else {
fc & !GMACFC_FCB_BPA
};
write_reg(MAC_BASE + GMACFC_OFFSET, fc);
}
}
#[inline(always)]
pub fn is_flow_control_busy() -> bool {
(Self::flow_control() & GMACFC_FCB_BPA) != 0
}
#[inline(always)]
pub fn set_pass_all_multicast(enable: bool) {
unsafe {
let ff = read_reg(MAC_BASE + GMACFF_OFFSET);
let ff = if enable {
ff | GMACFF_PM
} else {
ff & !GMACFF_PM
};
write_reg(MAC_BASE + GMACFF_OFFSET, ff);
}
}
#[inline(always)]
pub fn set_broadcast_enabled(enable: bool) {
unsafe {
let ff = read_reg(MAC_BASE + GMACFF_OFFSET);
let ff = if enable {
ff & !GMACFF_DBF
} else {
ff | GMACFF_DBF
};
write_reg(MAC_BASE + GMACFF_OFFSET, ff);
}
}
pub fn set_mac_address(addr: &[u8; 6]) {
let low = (addr[0] as u32)
| ((addr[1] as u32) << 8)
| ((addr[2] as u32) << 16)
| ((addr[3] as u32) << 24);
let high = (addr[4] as u32) | ((addr[5] as u32) << 8) | (1 << 31);
Self::set_mac_addr0_low(low);
Self::set_mac_addr0_high(high);
}
pub fn get_mac_address() -> [u8; 6] {
let low = Self::mac_addr0_low();
let high = Self::mac_addr0_high();
[
(low & 0xFF) as u8,
((low >> 8) & 0xFF) as u8,
((low >> 16) & 0xFF) as u8,
((low >> 24) & 0xFF) as u8,
(high & 0xFF) as u8,
((high >> 8) & 0xFF) as u8,
]
}
#[inline(always)]
const fn addr_filter_offsets(slot: usize) -> Option<(usize, usize)> {
match slot {
1 => Some((GMACADDR1H_OFFSET, GMACADDR1L_OFFSET)),
2 => Some((GMACADDR2H_OFFSET, GMACADDR2L_OFFSET)),
3 => Some((GMACADDR3H_OFFSET, GMACADDR3L_OFFSET)),
4 => Some((GMACADDR4H_OFFSET, GMACADDR4L_OFFSET)),
_ => None,
}
}
pub fn set_mac_filter(slot: usize, addr: &[u8; 6], source_addr: bool, mask: u8) -> bool {
let Some((high_off, low_off)) = Self::addr_filter_offsets(slot) else {
return false;
};
let low = (addr[0] as u32)
| ((addr[1] as u32) << 8)
| ((addr[2] as u32) << 16)
| ((addr[3] as u32) << 24);
let mut high = (addr[4] as u32) | ((addr[5] as u32) << 8);
high |= ((mask as u32) & 0x3F) << GMACADDRH_MBC_SHIFT;
if source_addr {
high |= GMACADDRH_SA;
}
high |= GMACADDRH_AE;
unsafe {
write_reg(MAC_BASE + low_off, low);
write_reg(MAC_BASE + high_off, high);
}
true
}
pub fn clear_mac_filter(slot: usize) -> bool {
let Some((high_off, low_off)) = Self::addr_filter_offsets(slot) else {
return false;
};
unsafe {
write_reg(MAC_BASE + low_off, 0);
write_reg(MAC_BASE + high_off, 0); }
true
}
pub fn is_mac_filter_enabled(slot: usize) -> Option<bool> {
let (high_off, _) = Self::addr_filter_offsets(slot)?;
let high = unsafe { read_reg(MAC_BASE + high_off) };
Some((high & GMACADDRH_AE) != 0)
}
pub fn get_mac_filter(slot: usize) -> Option<([u8; 6], bool)> {
let (high_off, low_off) = Self::addr_filter_offsets(slot)?;
let (low, high) = unsafe { (read_reg(MAC_BASE + low_off), read_reg(MAC_BASE + high_off)) };
let addr = [
(low & 0xFF) as u8,
((low >> 8) & 0xFF) as u8,
((low >> 16) & 0xFF) as u8,
((low >> 24) & 0xFF) as u8,
(high & 0xFF) as u8,
((high >> 8) & 0xFF) as u8,
];
let enabled = (high & GMACADDRH_AE) != 0;
Some((addr, enabled))
}
pub fn clear_all_mac_filters() {
for slot in 1..=MAC_ADDR_FILTER_COUNT {
Self::clear_mac_filter(slot);
}
}
pub fn find_free_mac_filter_slot() -> Option<usize> {
(1..=MAC_ADDR_FILTER_COUNT).find(|&slot| Self::is_mac_filter_enabled(slot) == Some(false))
}
pub fn find_mac_filter(addr: &[u8; 6]) -> Option<usize> {
for slot in 1..=MAC_ADDR_FILTER_COUNT {
if let Some((filter_addr, enabled)) = Self::get_mac_filter(slot)
&& enabled
&& filter_addr == *addr
{
return Some(slot);
}
}
None
}
}