use core::{mem::size_of, ptr::NonNull};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
};
pub const VENDOR_ID: u16 = 0x10ec;
pub const DEVICE_ID_RTL8125: u16 = 0x8125;
pub const RTL8125_REGS_SIZE: usize = EEE_TXIDLE_TIMER_8125 + size_of::<u16>();
const MAC0_BKP: usize = 0x19e0;
const EEE_TXIDLE_TIMER_8125: usize = 0x6048;
const CONFIG_WRITE_ENABLE_LOCKED: u8 = 0;
const CONFIG_WRITE_ENABLE_UNLOCKED: u8 = 0xc0;
const CPLUS_CMD_KEEP_MASK: u16 = CPLUS_CMD::NORMAL_MODE::SET.value
| CPLUS_CMD::RX_VLAN::SET.value
| CPLUS_CMD::RX_CHKSUM::SET.value
| CPLUS_CMD::INTERRUPT_TIMER_MASK.val(0x3).value;
const RX_DMA_BURST_UNLIMITED: u32 = 7;
const RX_FETCH_DFLT_8125_VALUE: u32 = 8;
const TX_DMA_BURST_UNLIMITED: u32 = 7;
const TX_INTER_FRAME_GAP_VALUE: u32 = 3;
pub const DEFAULT_IRQ_MASK: u32 = INTR::LINK_CHANGE::SET.value
| INTR::RX_OVERFLOW::SET.value
| INTR::TX_ERROR::SET.value
| INTR::TX_OK::SET.value
| INTR::RX_ERROR::SET.value
| INTR::RX_OK::SET.value;
pub const fn phy_status_link_up(value: u8) -> bool {
value & PHY_STATUS::LINK_UP::SET.value != 0
}
pub fn irq_has_tx_event(value: u32) -> bool {
value
& (INTR::TX_OK::SET.value
| INTR::TX_ERROR::SET.value
| INTR::TX_DESC_UNAVAILABLE::SET.value)
!= 0
}
pub fn irq_has_rx_event(value: u32) -> bool {
value
& (INTR::RX_OK::SET.value
| INTR::RX_ERROR::SET.value
| INTR::RX_FIFO_OVERFLOW::SET.value
| INTR::RX_OVERFLOW::SET.value)
!= 0
}
pub fn irq_has_link_change(value: u32) -> bool {
value & INTR::LINK_CHANGE::SET.value != 0
}
pub fn irq_has_rx_overflow(value: u32) -> bool {
value & (INTR::RX_OVERFLOW::SET.value | INTR::RX_FIFO_OVERFLOW::SET.value) != 0
}
register_structs! {
Registers {
(0x0000 => mac0: ReadWrite<u32>),
(0x0004 => mac4: ReadWrite<u16>),
(0x0006 => _reserved0),
(0x0008 => mar0: ReadWrite<u32>),
(0x000c => mar4: ReadWrite<u32>),
(0x0010 => _reserved1),
(0x0020 => tx_desc_start_addr_low: ReadWrite<u32>),
(0x0024 => tx_desc_start_addr_high: ReadWrite<u32>),
(0x0028 => _reserved2),
(0x0037 => chip_cmd: ReadWrite<u8, CHIP_CMD::Register>),
(0x0038 => intr_mask_8125: ReadWrite<u32, INTR::Register>),
(0x003c => intr_status_8125: ReadWrite<u32, INTR::Register>),
(0x0040 => tx_config: ReadWrite<u32, TX_CONFIG::Register>),
(0x0044 => rx_config: ReadWrite<u32, RX_CONFIG::Register>),
(0x0048 => _reserved3),
(0x0050 => cfg9346: ReadWrite<u8>),
(0x0051 => _reserved4),
(0x0052 => config1: ReadWrite<u8, CONFIG1::Register>),
(0x0053 => config2: ReadWrite<u8, CONFIG2::Register>),
(0x0054 => config3: ReadWrite<u8, CONFIG3::Register>),
(0x0055 => _reserved5),
(0x0056 => config5: ReadWrite<u8, CONFIG5::Register>),
(0x0057 => _reserved6),
(0x0064 => csidr: ReadWrite<u32>),
(0x0068 => csiar: ReadWrite<u32, CSIAR::Register>),
(0x006c => phy_status: ReadWrite<u8, PHY_STATUS::Register>),
(0x006d => _reserved7),
(0x0080 => ephyar: ReadWrite<u32, EPHYAR::Register>),
(0x0084 => _reserved8),
(0x0090 => tx_poll_8125: ReadWrite<u16, TX_POLL::Register>),
(0x0092 => _reserved9),
(0x00b0 => ocpdr: ReadWrite<u32, OCPDR::Register>),
(0x00b4 => _reserved10),
(0x00b8 => gphy_ocp: ReadWrite<u32, OCPDR::Register>),
(0x00bc => _reserved11),
(0x00d3 => mcu: ReadWrite<u8, MCU::Register>),
(0x00d4 => _reserved12),
(0x00da => rx_max_size: ReadWrite<u16>),
(0x00dc => _reserved13),
(0x00e0 => cplus_cmd: ReadWrite<u16, CPLUS_CMD::Register>),
(0x00e2 => intr_mitigate: ReadWrite<u16>),
(0x00e4 => rx_desc_addr_low: ReadWrite<u32>),
(0x00e8 => rx_desc_addr_high: ReadWrite<u32>),
(0x00ec => _reserved14),
(0x00f0 => misc: ReadWrite<u32, MISC::Register>),
(0x00f4 => @END),
}
}
register_bitfields! {u8,
CHIP_CMD [
RESET OFFSET(4) NUMBITS(1) [],
RX_ENABLE OFFSET(3) NUMBITS(1) [],
TX_ENABLE OFFSET(2) NUMBITS(1) []
],
CONFIG1 [
SPEED_DOWN OFFSET(4) NUMBITS(1) []
],
CONFIG2 [
CLK_REQ_ENABLE OFFSET(7) NUMBITS(1) []
],
CONFIG3 [
READY_TO_L23 OFFSET(1) NUMBITS(1) []
],
CONFIG5 [
ASPM_ENABLE OFFSET(0) NUMBITS(1) []
],
PHY_STATUS [
LINK_UP OFFSET(1) NUMBITS(1) []
],
MCU [
NOW_IS_OOB OFFSET(7) NUMBITS(1) [],
TX_EMPTY OFFSET(5) NUMBITS(1) [],
RX_EMPTY OFFSET(4) NUMBITS(1) [],
LINK_LIST_READY OFFSET(1) NUMBITS(1) []
]
}
register_bitfields! {u16,
CPLUS_CMD [
NORMAL_MODE OFFSET(13) NUMBITS(1) [],
RX_VLAN OFFSET(6) NUMBITS(1) [],
RX_CHKSUM OFFSET(5) NUMBITS(1) [],
PCI_DAC OFFSET(4) NUMBITS(1) [],
PCI_MULTIPLE_RW OFFSET(3) NUMBITS(1) [],
INTERRUPT_TIMER_MASK OFFSET(0) NUMBITS(2) []
],
TX_POLL [
NORMAL_PRIORITY OFFSET(0) NUMBITS(1) []
]
}
register_bitfields! {u32,
CSIAR [
FLAG OFFSET(31) NUMBITS(1) [],
BYTE_ENABLE OFFSET(12) NUMBITS(4) [],
FUNCTION OFFSET(16) NUMBITS(3) [],
ADDR OFFSET(0) NUMBITS(12) []
],
INTR [
TX_DESC_UNAVAILABLE OFFSET(7) NUMBITS(1) [],
RX_FIFO_OVERFLOW OFFSET(6) NUMBITS(1) [],
LINK_CHANGE OFFSET(5) NUMBITS(1) [],
RX_OVERFLOW OFFSET(4) NUMBITS(1) [],
TX_ERROR OFFSET(3) NUMBITS(1) [],
TX_OK OFFSET(2) NUMBITS(1) [],
RX_ERROR OFFSET(1) NUMBITS(1) [],
RX_OK OFFSET(0) NUMBITS(1) []
],
TX_CONFIG [
INTER_FRAME_GAP OFFSET(24) NUMBITS(2) [],
DMA_BURST OFFSET(8) NUMBITS(3) []
],
RX_CONFIG [
FETCH_DFLT OFFSET(27) NUMBITS(4) [],
PAUSE_SLOT_ON OFFSET(11) NUMBITS(1) [],
DMA_BURST OFFSET(8) NUMBITS(3) [],
ACCEPT_BROADCAST OFFSET(3) NUMBITS(1) [],
ACCEPT_MULTICAST OFFSET(2) NUMBITS(1) [],
ACCEPT_MY_PHYS OFFSET(1) NUMBITS(1) [],
ACCEPT_ALL_PHYS OFFSET(0) NUMBITS(1) []
],
EPHYAR [
READY OFFSET(31) NUMBITS(1) [],
REG OFFSET(16) NUMBITS(5) [],
DATA OFFSET(0) NUMBITS(16) []
],
OCPDR [
BUSY OFFSET(31) NUMBITS(1) [],
DATA OFFSET(0) NUMBITS(16) []
],
MISC [
RXDV_GATED_ENABLE OFFSET(19) NUMBITS(1) []
]
}
#[derive(Clone, Copy)]
pub struct Regs {
base: NonNull<Registers>,
}
unsafe impl Send for Regs {}
unsafe impl Sync for Regs {}
impl Regs {
pub fn new(base: NonNull<u8>) -> Self {
Self { base: base.cast() }
}
fn regs(&self) -> &Registers {
unsafe { self.base.as_ref() }
}
fn raw_base(&self) -> *mut u8 {
self.base.as_ptr().cast()
}
pub fn commit(&self) {
let _ = self.regs().chip_cmd.get();
}
pub fn read_mac(&self) -> [u8; 6] {
let low = self.regs().mac0.get().to_le_bytes();
let high = self.regs().mac4.get().to_le_bytes();
[low[0], low[1], low[2], low[3], high[0], high[1]]
}
pub fn read_backup_mac(&self) -> [u8; 6] {
self.read_mac_at(MAC0_BKP)
}
pub fn write_mac(&self, mac: [u8; 6]) {
self.regs().mac4.set(u16::from_le_bytes([mac[4], mac[5]]));
self.commit();
self.regs()
.mac0
.set(u32::from_le_bytes([mac[0], mac[1], mac[2], mac[3]]));
self.commit();
}
pub fn unlock_config(&self) {
self.regs().cfg9346.set(CONFIG_WRITE_ENABLE_UNLOCKED);
}
pub fn lock_config(&self) {
self.regs().cfg9346.set(CONFIG_WRITE_ENABLE_LOCKED);
}
pub fn request_reset(&self) {
self.regs().chip_cmd.modify(CHIP_CMD::RESET::SET);
}
pub fn reset_pending(&self) -> bool {
self.regs().chip_cmd.is_set(CHIP_CMD::RESET)
}
pub fn disable_tx_rx(&self) {
self.regs()
.chip_cmd
.modify(CHIP_CMD::TX_ENABLE::CLEAR + CHIP_CMD::RX_ENABLE::CLEAR);
}
pub fn enable_tx_rx(&self) {
self.regs()
.chip_cmd
.write(CHIP_CMD::TX_ENABLE::SET + CHIP_CMD::RX_ENABLE::SET);
}
pub fn clear_now_is_oob(&self) {
self.regs().mcu.modify(MCU::NOW_IS_OOB::CLEAR);
}
pub fn rxtx_empty(&self) -> bool {
self.regs()
.mcu
.matches_all(MCU::TX_EMPTY::SET + MCU::RX_EMPTY::SET)
}
pub fn link_list_ready(&self) -> bool {
self.regs().mcu.is_set(MCU::LINK_LIST_READY)
}
pub fn clear_ready_to_l23(&self) {
self.regs().config3.modify(CONFIG3::READY_TO_L23::CLEAR);
}
pub fn clear_speed_down(&self) {
self.regs().config1.modify(CONFIG1::SPEED_DOWN::CLEAR);
}
pub fn set_aspm_clkreq(&self, enable: bool) {
if enable {
self.regs().config5.modify(CONFIG5::ASPM_ENABLE::SET);
self.regs().config2.modify(CONFIG2::CLK_REQ_ENABLE::SET);
} else {
self.regs().config2.modify(CONFIG2::CLK_REQ_ENABLE::CLEAR);
self.regs().config5.modify(CONFIG5::ASPM_ENABLE::CLEAR);
}
}
pub fn read_phy_status(&self) -> u8 {
self.regs().phy_status.get()
}
pub fn csi_read(&self, addr: u32) -> Option<u32> {
self.regs().csiar.write(
CSIAR::ADDR.val(addr & 0x0fff) + CSIAR::FUNCTION.val(0) + CSIAR::BYTE_ENABLE.val(0x0f),
);
for _ in 0..1_000 {
if self.regs().csiar.is_set(CSIAR::FLAG) {
return Some(self.regs().csidr.get());
}
core::hint::spin_loop();
}
None
}
pub fn csi_write(&self, addr: u32, value: u32) -> bool {
self.regs().csidr.set(value);
self.regs().csiar.write(
CSIAR::FLAG::SET
+ CSIAR::ADDR.val(addr & 0x0fff)
+ CSIAR::FUNCTION.val(0)
+ CSIAR::BYTE_ENABLE.val(0x0f),
);
for _ in 0..1_000 {
if !self.regs().csiar.is_set(CSIAR::FLAG) {
return true;
}
core::hint::spin_loop();
}
false
}
pub fn link_up(&self) -> bool {
self.regs().phy_status.is_set(PHY_STATUS::LINK_UP)
}
pub fn read_chip_cmd(&self) -> u8 {
self.regs().chip_cmd.get()
}
pub fn read_mcu(&self) -> u8 {
self.regs().mcu.get()
}
pub fn read_interrupt_status(&self) -> u32 {
self.regs().intr_status_8125.get()
}
pub fn write_interrupt_status(&self, bits: u32) {
self.regs().intr_status_8125.set(bits);
}
pub fn read_interrupt_mask(&self) -> u32 {
self.regs().intr_mask_8125.get()
}
pub fn write_interrupt_mask(&self, bits: u32) {
self.regs().intr_mask_8125.set(bits);
}
pub fn read_tx_config(&self) -> u32 {
self.regs().tx_config.get()
}
pub fn write_default_tx_config(&self) {
self.regs().tx_config.write(
TX_CONFIG::DMA_BURST.val(TX_DMA_BURST_UNLIMITED)
+ TX_CONFIG::INTER_FRAME_GAP.val(TX_INTER_FRAME_GAP_VALUE),
);
}
pub fn read_rx_config(&self) -> u32 {
self.regs().rx_config.get()
}
pub fn write_default_rx_config(&self) {
self.write_rx_config(false);
}
pub fn write_rx_config(&self, pause_slot_on: bool) {
let mut value = RX_CONFIG::FETCH_DFLT.val(RX_FETCH_DFLT_8125_VALUE)
+ RX_CONFIG::DMA_BURST.val(RX_DMA_BURST_UNLIMITED);
if pause_slot_on {
value += RX_CONFIG::PAUSE_SLOT_ON::SET;
}
self.regs().rx_config.write(value);
}
pub fn write_default_rx_config_8125b(&self) {
self.regs().rx_config.write(
RX_CONFIG::FETCH_DFLT.val(RX_FETCH_DFLT_8125_VALUE)
+ RX_CONFIG::PAUSE_SLOT_ON::SET
+ RX_CONFIG::DMA_BURST.val(RX_DMA_BURST_UNLIMITED),
);
}
pub fn set_rx_accept_mode(&self) {
self.regs().rx_config.modify(
RX_CONFIG::ACCEPT_ALL_PHYS::CLEAR
+ RX_CONFIG::ACCEPT_MY_PHYS::SET
+ RX_CONFIG::ACCEPT_MULTICAST::SET
+ RX_CONFIG::ACCEPT_BROADCAST::SET,
);
}
pub fn configure_cplus(&self, dma_mask: u64) {
let mut value = (self.regs().cplus_cmd.get() & CPLUS_CMD_KEEP_MASK)
| CPLUS_CMD::PCI_MULTIPLE_RW::SET.value;
if dma_mask > u32::MAX as u64 {
value |= CPLUS_CMD::PCI_DAC::SET.value;
}
self.regs().cplus_cmd.set(value);
}
pub fn read_cplus_cmd(&self) -> u16 {
self.regs().cplus_cmd.get()
}
pub fn write_rx_max_size(&self, size: u16) {
self.regs().rx_max_size.set(size);
}
pub fn disable_interrupt_mitigation(&self) {
self.regs().intr_mitigate.set(0);
}
pub fn write_tx_desc_base(&self, base: u64) {
self.regs().tx_desc_start_addr_high.set((base >> 32) as u32);
self.regs().tx_desc_start_addr_low.set(base as u32);
}
pub fn write_rx_desc_base(&self, base: u64) {
self.regs().rx_desc_addr_high.set((base >> 32) as u32);
self.regs().rx_desc_addr_low.set(base as u32);
}
pub fn read_rx_desc_base(&self) -> u64 {
(u64::from(self.regs().rx_desc_addr_high.get()) << 32)
| u64::from(self.regs().rx_desc_addr_low.get())
}
pub fn poll_tx(&self) {
self.regs()
.tx_poll_8125
.write(TX_POLL::NORMAL_PRIORITY::SET);
}
pub fn set_multicast_filter_all(&self) {
self.regs().mar4.set(u32::MAX);
self.regs().mar0.set(u32::MAX);
}
pub fn enable_rxdv_gate(&self) {
self.regs().misc.modify(MISC::RXDV_GATED_ENABLE::SET);
}
pub fn disable_rxdv_gate(&self) {
self.regs().misc.modify(MISC::RXDV_GATED_ENABLE::CLEAR);
}
pub fn start_mac_ocp_read(&self, reg: u32) {
self.regs().ocpdr.set(reg << 15);
}
pub fn start_mac_ocp_write(&self, reg: u32, data: u16) {
self.regs()
.ocpdr
.set(OCPDR::BUSY::SET.value | (reg << 15) | u32::from(data));
}
pub fn read_mac_ocp_data(&self) -> u16 {
self.regs().ocpdr.read(OCPDR::DATA) as u16
}
pub fn start_ephy_read(&self, reg: u32) {
self.regs().ephyar.write(EPHYAR::REG.val(reg & 0x1f));
}
pub fn start_ephy_write(&self, reg: u32, data: u16) {
self.regs().ephyar.write(
EPHYAR::READY::SET + EPHYAR::REG.val(reg & 0x1f) + EPHYAR::DATA.val(u32::from(data)),
);
}
pub fn ephy_ready(&self) -> bool {
self.regs().ephyar.is_set(EPHYAR::READY)
}
pub fn read_ephy_data(&self) -> u16 {
self.regs().ephyar.read(EPHYAR::DATA) as u16
}
pub fn start_phy_ocp_read(&self, reg: u32) {
self.regs().gphy_ocp.set(reg << 15);
}
pub fn start_phy_ocp_write(&self, reg: u32, data: u16) {
self.regs()
.gphy_ocp
.set(OCPDR::BUSY::SET.value | (reg << 15) | u32::from(data));
}
pub fn phy_ocp_busy(&self) -> bool {
self.regs().gphy_ocp.is_set(OCPDR::BUSY)
}
pub fn read_phy_ocp_data(&self) -> u16 {
self.regs().gphy_ocp.read(OCPDR::DATA) as u16
}
pub fn write_eee_txidle_timer(&self, value: u16) {
self.write16_at(EEE_TXIDLE_TIMER_8125, value);
}
pub fn write_vendor_u8(&self, offset: usize, value: u8) {
self.write8_at(offset, value);
}
pub fn write_vendor_u16(&self, offset: usize, value: u16) {
self.write16_at(offset, value);
}
pub fn write_vendor_u32(&self, offset: usize, value: u32) {
self.write32_at(offset, value);
}
pub fn clear_vendor_u16_bits(&self, offset: usize, mask: u16) {
self.write16_at(offset, self.read16_at(offset) & !mask);
}
fn read_mac_at(&self, offset: usize) -> [u8; 6] {
[
self.read8_at(offset),
self.read8_at(offset + 1),
self.read8_at(offset + 2),
self.read8_at(offset + 3),
self.read8_at(offset + 4),
self.read8_at(offset + 5),
]
}
fn read8_at(&self, offset: usize) -> u8 {
unsafe { self.raw_base().add(offset).cast::<u8>().read_volatile() }
}
fn write8_at(&self, offset: usize, value: u8) {
unsafe {
self.raw_base()
.add(offset)
.cast::<u8>()
.write_volatile(value);
}
}
fn read16_at(&self, offset: usize) -> u16 {
unsafe { self.raw_base().add(offset).cast::<u16>().read_volatile() }
}
fn write16_at(&self, offset: usize, value: u16) {
unsafe {
self.raw_base()
.add(offset)
.cast::<u16>()
.write_volatile(value);
}
}
fn write32_at(&self, offset: usize, value: u32) {
unsafe {
self.raw_base()
.add(offset)
.cast::<u32>()
.write_volatile(value);
}
}
}