#![allow(dead_code)]
pub const BASE: usize = 0x3FF6_A000;
pub const GMACCONFIG: usize = 0x00;
pub const GMACFF: usize = 0x04;
pub const GMACHASTH: usize = 0x08;
pub const GMACHASTL: usize = 0x0C;
pub const GMACMIIADDR: usize = 0x10;
pub const GMACMIIDATA: usize = 0x14;
pub const GMACFC: usize = 0x18;
pub const GMACVLAN: usize = 0x1C;
pub const GMACDEBUG: usize = 0x24;
pub const GMACINTS: usize = 0x38;
pub const GMACINTMASK: usize = 0x3C;
pub const GMACADDR0H: usize = 0x40;
pub const GMACADDR0L: usize = 0x44;
pub mod config {
pub const RX_ENABLE: u32 = 1 << 2;
pub const TX_ENABLE: u32 = 1 << 3;
pub const AUTO_PAD_CRC_STRIP: u32 = 1 << 7;
pub const LINK_UP: u32 = 1 << 8;
pub const RETRY_DISABLE: u32 = 1 << 9;
pub const CHECKSUM_OFFLOAD: u32 = 1 << 10;
pub const DUPLEX_FULL: u32 = 1 << 11;
pub const SPEED_100: u32 = 1 << 14;
pub const PORT_SELECT: u32 = 1 << 15;
pub const IFG_SHIFT: u32 = 17;
pub const IFG_MASK: u32 = 0x07 << 17;
pub const JUMBO_FRAME: u32 = 1 << 20;
pub const FRAME_BURST: u32 = 1 << 21;
pub const JABBER_DISABLE: u32 = 1 << 22;
pub const WATCHDOG_DISABLE: u32 = 1 << 23;
}
pub mod frame_filter {
pub const PROMISCUOUS: u32 = 1 << 0;
pub const HASH_UNICAST: u32 = 1 << 1;
pub const HASH_MULTICAST: u32 = 1 << 2;
pub const DA_INVERSE: u32 = 1 << 3;
pub const PASS_ALL_MULTICAST: u32 = 1 << 4;
pub const DISABLE_BROADCAST: u32 = 1 << 5;
pub const RECEIVE_ALL: u32 = 1 << 31;
}
pub mod miiaddr {
pub const PA_SHIFT: u32 = 11;
pub const PA_MASK: u32 = 0x1F << 11;
pub const GR_SHIFT: u32 = 6;
pub const GR_MASK: u32 = 0x1F << 6;
pub const CR_SHIFT: u32 = 2;
pub const CR_MASK: u32 = 0x0F << 2;
pub const GW: u32 = 1 << 1;
pub const GB: u32 = 1 << 0;
}
pub mod flow_control {
pub const FCB_BPA: u32 = 1 << 0;
pub const TX_ENABLE: u32 = 1 << 1;
pub const RX_ENABLE: u32 = 1 << 2;
pub const UNICAST_PAUSE: u32 = 1 << 3;
pub const PLT_SHIFT: u32 = 4;
pub const PLT_MASK: u32 = 0x03 << 4;
pub const ZERO_QUANTA_DISABLE: u32 = 1 << 7;
pub const PT_SHIFT: u32 = 16;
pub const PT_MASK: u32 = 0xFFFF << 16;
}
#[inline(always)]
pub unsafe fn read(offset: usize) -> u32 {
core::ptr::read_volatile((BASE + offset) as *const u32)
}
#[inline(always)]
pub unsafe fn write(offset: usize, val: u32) {
core::ptr::write_volatile((BASE + offset) as *mut u32, val);
}
#[inline(always)]
pub unsafe fn set_bits(offset: usize, bits: u32) {
let val = read(offset);
write(offset, val | bits);
}
#[inline(always)]
pub unsafe fn clear_bits(offset: usize, bits: u32) {
let val = read(offset);
write(offset, val & !bits);
}
#[inline(always)]
pub fn config() -> u32 {
unsafe { read(GMACCONFIG) }
}
#[inline(always)]
pub fn set_config(val: u32) {
unsafe { write(GMACCONFIG, val) }
}
#[inline(always)]
pub fn set_frame_filter(val: u32) {
unsafe { write(GMACFF, val) }
}
#[inline(always)]
pub fn set_hash_table(value: u64) {
unsafe {
write(GMACHASTL, value as u32);
write(GMACHASTH, (value >> 32) as u32);
}
}
#[inline(always)]
pub fn set_speed_100mbps(is_100: bool) {
unsafe {
if is_100 {
set_bits(GMACCONFIG, config::SPEED_100);
} else {
clear_bits(GMACCONFIG, config::SPEED_100);
}
}
}
#[inline(always)]
pub fn set_duplex_full(full: bool) {
unsafe {
if full {
set_bits(GMACCONFIG, config::DUPLEX_FULL);
} else {
clear_bits(GMACCONFIG, config::DUPLEX_FULL);
}
}
}
pub fn set_mac_address(addr: &[u8; 6]) {
let high = (addr[4] as u32) | ((addr[5] as u32) << 8) | (1u32 << 31);
let low = (addr[0] as u32)
| ((addr[1] as u32) << 8)
| ((addr[2] as u32) << 16)
| ((addr[3] as u32) << 24);
unsafe {
write(GMACADDR0H, high);
write(GMACADDR0L, low);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn base_address() {
assert_eq!(BASE, 0x3FF6_A000);
}
#[test]
fn register_offsets_within_block() {
let offsets = [
GMACCONFIG,
GMACFF,
GMACHASTH,
GMACHASTL,
GMACMIIADDR,
GMACMIIDATA,
GMACFC,
GMACVLAN,
GMACDEBUG,
GMACINTS,
GMACINTMASK,
GMACADDR0H,
GMACADDR0L,
];
for off in offsets {
assert!(off < 0x1000, "offset {:#x} exceeds MAC block size", off);
}
}
#[test]
fn config_bits_no_overlap() {
let bits = [
config::RX_ENABLE,
config::TX_ENABLE,
config::RETRY_DISABLE,
config::CHECKSUM_OFFLOAD,
config::DUPLEX_FULL,
config::SPEED_100,
config::PORT_SELECT,
config::JUMBO_FRAME,
config::FRAME_BURST,
config::JABBER_DISABLE,
config::WATCHDOG_DISABLE,
config::LINK_UP,
config::AUTO_PAD_CRC_STRIP,
];
for i in 0..bits.len() {
for j in (i + 1)..bits.len() {
assert_eq!(
bits[i] & bits[j],
0,
"config bits {:#x} and {:#x} overlap",
bits[i],
bits[j]
);
}
}
}
#[test]
fn miiaddr_fields_no_overlap() {
assert_eq!(miiaddr::GB & miiaddr::GW, 0);
assert_eq!(miiaddr::CR_MASK & miiaddr::GR_MASK, 0);
assert_eq!(miiaddr::GR_MASK & miiaddr::PA_MASK, 0);
assert_eq!(miiaddr::CR_MASK & miiaddr::PA_MASK, 0);
}
#[test]
fn flow_control_bits_no_overlap() {
let bits = [
flow_control::FCB_BPA,
flow_control::TX_ENABLE,
flow_control::RX_ENABLE,
flow_control::UNICAST_PAUSE,
flow_control::ZERO_QUANTA_DISABLE,
];
for i in 0..bits.len() {
for j in (i + 1)..bits.len() {
assert_eq!(
bits[i] & bits[j],
0,
"flow_control bits {:#x} and {:#x} overlap",
bits[i],
bits[j]
);
}
}
}
#[test]
fn frame_filter_bits_no_overlap() {
let bits = [
frame_filter::PROMISCUOUS,
frame_filter::HASH_UNICAST,
frame_filter::HASH_MULTICAST,
frame_filter::DA_INVERSE,
frame_filter::PASS_ALL_MULTICAST,
frame_filter::DISABLE_BROADCAST,
frame_filter::RECEIVE_ALL,
];
for i in 0..bits.len() {
for j in (i + 1)..bits.len() {
assert_eq!(
bits[i] & bits[j],
0,
"frame_filter bits {:#x} and {:#x} overlap",
bits[i],
bits[j]
);
}
}
}
}