sifive: support for cadence gemgxl ethernet
This commit is contained in:
@@ -96,7 +96,7 @@ struct HartContext {
|
||||
/// RISC-V Platform-Level Interrupt Controller (PLIC) device
|
||||
pub struct Plic {
|
||||
base: PhysicalAddress,
|
||||
max_irqs: usize,
|
||||
max_irq: usize,
|
||||
// hart id -> context map
|
||||
context_map: Vec<HartContext>,
|
||||
inner: OneTimeInit<Inner>,
|
||||
@@ -116,8 +116,8 @@ impl Plic {
|
||||
log::error!("plic: irq cannot be zero");
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
if irq as usize >= self.max_irqs {
|
||||
log::error!("plic: irq ({}) >= max_irqs ({})", irq, self.max_irqs);
|
||||
if irq as usize > self.max_irq {
|
||||
log::error!("plic: irq ({}) > max_irq ({})", irq, self.max_irq);
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
Ok(irq)
|
||||
@@ -209,7 +209,7 @@ impl Device for Plic {
|
||||
|
||||
let common = DeviceMemoryIo::<CommonRegs>::map(self.base, Default::default())?;
|
||||
|
||||
for i in 0..self.max_irqs - 1 {
|
||||
for i in 0..self.max_irq {
|
||||
common.PRIORITY[i].set(3);
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ impl Device for Plic {
|
||||
Default::default(),
|
||||
)?;
|
||||
|
||||
for i in 0..self.max_irqs.div_ceil(32) {
|
||||
for i in 0..self.max_irq.div_ceil(32) {
|
||||
enable.ENABLE[i].set(0);
|
||||
}
|
||||
control.THRESHOLD.set(0);
|
||||
@@ -289,7 +289,7 @@ device_tree_driver! {
|
||||
let ndev = node.prop_usize("riscv,ndev")?;
|
||||
let iext = node.property("interrupts-extended")?;
|
||||
|
||||
let max_irqs = MAX_IRQS.min(ndev);
|
||||
let max_irq = MAX_IRQS.min(ndev);
|
||||
|
||||
// Parse the context -> hart mapping, only select S-mode external interrupts
|
||||
let mut context_map = Vec::new();
|
||||
@@ -322,7 +322,7 @@ device_tree_driver! {
|
||||
|
||||
let intc = Arc::new(Plic {
|
||||
base,
|
||||
max_irqs,
|
||||
max_irq,
|
||||
context_map,
|
||||
inner: OneTimeInit::new(),
|
||||
});
|
||||
|
||||
@@ -10,7 +10,9 @@ yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
ygg_driver_net_core.path = "../../net/core"
|
||||
|
||||
static_assertions.workspace = true
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
use core::{mem::MaybeUninit, time::Duration};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::ClockHandle,
|
||||
device::{Device, DeviceInitContext},
|
||||
dma::DmaAllocator,
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{
|
||||
InitSequence, Node, ProbeContext, device_tree_driver, util::read_mac_address,
|
||||
};
|
||||
use futures_util::task::AtomicWaker;
|
||||
use libk::{
|
||||
dma::DmaBuffer,
|
||||
error::Error,
|
||||
task::runtime::{self, with_timeout},
|
||||
};
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::{OneTimeInit, event::BitmapEvent, sync::IrqSafeSpinlock};
|
||||
use tock_registers::{
|
||||
LocalRegisterCopy,
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
};
|
||||
use ygg_driver_net_core::{
|
||||
RxPacket,
|
||||
ephy::PhyAccess,
|
||||
interface::{NetworkDevice, NetworkInterfaceType},
|
||||
};
|
||||
use yggdrasil_abi::net::{MacAddress, link::LinkState};
|
||||
|
||||
use crate::ethernet::{
|
||||
queue::Queue,
|
||||
regs::{ControlRegs, Regs},
|
||||
};
|
||||
|
||||
const RX_BUF_SIZE_MUL: usize = 64;
|
||||
|
||||
mod queue;
|
||||
mod regs;
|
||||
|
||||
// TODO multiqueue
|
||||
struct Ethernet {
|
||||
// Device tree info
|
||||
name: &'static str,
|
||||
irq: IrqHandle,
|
||||
clk_pclk: ClockHandle,
|
||||
clk_hclk: ClockHandle,
|
||||
local_mac_address: Option<MacAddress>,
|
||||
|
||||
// Memory/HW
|
||||
// caps: Capabilities,
|
||||
dma: OneTimeInit<Arc<dyn DmaAllocator>>,
|
||||
control_regs: IrqSafeSpinlock<DeviceMemoryIo<'static, ControlRegs>>,
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
|
||||
// Operation
|
||||
queue: OneTimeInit<Queue>,
|
||||
softirq: BitmapEvent<AtomicWaker>,
|
||||
iface_id: OneTimeInit<u32>,
|
||||
}
|
||||
|
||||
// bitflags! {
|
||||
// pub struct CapFlags: u32 {
|
||||
// const MACB_IS_GEM: bit 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
// struct Capabilities {
|
||||
// flags: CapFlags,
|
||||
// queue_mask: u32,
|
||||
// queue_count: usize,
|
||||
// }
|
||||
|
||||
impl Ethernet {
|
||||
fn probe(node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let name = node.name().unwrap_or("gemgxl");
|
||||
let irq = node.interrupt(0)?;
|
||||
let clk_pclk = node.named_clock("pclk")?;
|
||||
let clk_hclk = node.named_clock("hclk")?;
|
||||
let base = node.map_base(context, 0)?;
|
||||
let base_control = node.map_base(context, 1)?;
|
||||
let local_mac_address = read_mac_address(node);
|
||||
|
||||
let regs = unsafe { DeviceMemoryIo::<Regs>::map(base, Default::default()) }.ok()?;
|
||||
let control_regs = unsafe { DeviceMemoryIo::map(base_control, Default::default()) }.ok()?;
|
||||
|
||||
// let caps = regs.probe_capabilities();
|
||||
|
||||
let gem = Arc::new(Ethernet {
|
||||
name,
|
||||
clk_pclk,
|
||||
clk_hclk,
|
||||
irq,
|
||||
local_mac_address,
|
||||
|
||||
// caps,
|
||||
dma: OneTimeInit::new(),
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
control_regs: IrqSafeSpinlock::new(control_regs),
|
||||
|
||||
queue: OneTimeInit::new(),
|
||||
softirq: BitmapEvent::new(AtomicWaker::new()),
|
||||
iface_id: OneTimeInit::new(),
|
||||
});
|
||||
|
||||
context.sequence = Some(InitSequence::Late);
|
||||
|
||||
Some(gem)
|
||||
}
|
||||
|
||||
async fn softirq(self: Arc<Self>) {
|
||||
let iface = *self.iface_id.get();
|
||||
let queue = self.queue.get();
|
||||
|
||||
loop {
|
||||
let event_fut = self.softirq.wait();
|
||||
let bits = match with_timeout(event_fut, Duration::from_millis(100)).await {
|
||||
Ok(bits) => bits as u32,
|
||||
Err(_) => {
|
||||
queue.consume_tx().ok();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let status = LocalRegisterCopy::<_, regs::Interrupt::Register>::new(bits);
|
||||
|
||||
if status.matches_all(regs::Interrupt::RCOMP::SET) {
|
||||
queue
|
||||
.consume_rx(&**self.dma.get(), |packet| {
|
||||
let packet = RxPacket::new(packet, 0, iface);
|
||||
ygg_driver_net_core::receive_packet(packet).ok();
|
||||
})
|
||||
.ok();
|
||||
|
||||
let regs = self.regs.lock();
|
||||
regs.interrupt_enable.write(regs::Interrupt::RCOMP::SET);
|
||||
}
|
||||
if status.matches_all(regs::Interrupt::TCOMP::SET) {
|
||||
queue.consume_tx().ok();
|
||||
|
||||
let regs = self.regs.lock();
|
||||
regs.interrupt_enable.write(regs::Interrupt::RCOMP::SET);
|
||||
}
|
||||
if status.matches_all(regs::Interrupt::LINK::SET) {
|
||||
log::info!("Link status changed");
|
||||
let regs = self.regs.lock();
|
||||
regs.interrupt_enable.write(regs::Interrupt::LINK::SET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn start_xmit(&self, frame: DmaBuffer<[u8]>) -> Result<(), Error> {
|
||||
let queue = self.queue.get();
|
||||
|
||||
queue.push_xmit(frame)?;
|
||||
|
||||
let regs = self.regs.lock();
|
||||
regs.network_control
|
||||
.modify(regs::NetworkControl::TSTART::SET);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Ethernet {
|
||||
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
|
||||
let _ = (&self.clk_pclk, &self.clk_hclk, &self.control_regs);
|
||||
log::info!("{}: hw address {:?}", self.name, self.local_mac_address);
|
||||
// TODO enable clocks
|
||||
let dma = self.dma.init(cx.dma_allocator);
|
||||
let regs = self.regs.lock();
|
||||
|
||||
let queue = self.queue.init(Queue::with_capacity(&**dma, 256, 64)?);
|
||||
|
||||
let rx_queue_base = queue.rx_queue_base().try_into_u32().unwrap();
|
||||
let tx_queue_base = queue.tx_queue_base().try_into_u32().unwrap();
|
||||
|
||||
regs.rx_buffer_queue_base.set(rx_queue_base);
|
||||
regs.tx_buffer_queue_base.set(tx_queue_base);
|
||||
regs.dma_config.write(
|
||||
regs::DmaConfig::RX_BUF_SIZE.val((queue::RX_BUFFER_SIZE / RX_BUF_SIZE_MUL) as _),
|
||||
);
|
||||
|
||||
regs.network_control.modify(regs::NetworkControl::MPE::SET);
|
||||
|
||||
{
|
||||
let phy = PhyAccess::new(&**regs, 0);
|
||||
let phy_id = phy.id()?;
|
||||
log::info!("{}: PHY {:04x}:{:04x}", self.name, phy_id.0, phy_id.1);
|
||||
phy.setup_link(false, None)?;
|
||||
}
|
||||
|
||||
regs.network_config.write(regs::NetworkConfig::PROMISC::SET);
|
||||
regs.network_control
|
||||
.modify(regs::NetworkControl::TE::SET + regs::NetworkControl::RE::SET);
|
||||
|
||||
// if (GEM_BFEXT(DAW64, gem_readl(...)))
|
||||
// hw_dma_cap |= HW_DMA_CAP_64B
|
||||
|
||||
regs.interrupt_disable.set(0xFFFFFFFF);
|
||||
regs.interrupt_enable.write(
|
||||
regs::Interrupt::LINK::SET + regs::Interrupt::RCOMP::SET + regs::Interrupt::TCOMP::SET,
|
||||
);
|
||||
|
||||
let iface =
|
||||
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
|
||||
self.iface_id.init(iface.id());
|
||||
|
||||
runtime::spawn(self.clone().softirq())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
self.irq.register(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkDevice for Ethernet {
|
||||
fn allocate_transmit_buffer(&self, len: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
||||
DmaBuffer::new_uninit_slice(&**self.dma.get(), len)
|
||||
}
|
||||
|
||||
fn transmit_buffer(&self, buffer: DmaBuffer<[u8]>) -> Result<(), Error> {
|
||||
self.start_xmit(buffer)
|
||||
}
|
||||
|
||||
fn packet_prefix_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn read_hardware_address(&self) -> MacAddress {
|
||||
// TODO from registers
|
||||
self.local_mac_address.unwrap()
|
||||
}
|
||||
|
||||
fn link_state(&self) -> LinkState {
|
||||
self.regs.lock().link_state()
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Ethernet {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
const MASK: u32 = regs::Interrupt::RCOMP::SET.value
|
||||
| regs::Interrupt::TCOMP::SET.value
|
||||
| regs::Interrupt::LINK::SET.value;
|
||||
|
||||
let regs = self.regs.lock();
|
||||
let status = regs.interrupt_status.get();
|
||||
// Mask the IRQ and send it to softirq handler
|
||||
regs.interrupt_disable.set(MASK & status);
|
||||
self.softirq.signal(status as u64);
|
||||
|
||||
status != 0
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["sifive,fu540-c000-gem"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
Ethernet::probe(node, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
use core::mem::{self, MaybeUninit};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use device_api::dma::DmaAllocator;
|
||||
use libk::{
|
||||
dma::{BusAddress, DmaBuffer},
|
||||
error::Error,
|
||||
};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
|
||||
pub const RX_BUFFER_SIZE: usize = 4096;
|
||||
|
||||
static_assertions::const_assert_eq!(RX_BUFFER_SIZE % super::RX_BUF_SIZE_MUL, 0);
|
||||
static_assertions::const_assert_ne!(RX_BUFFER_SIZE / super::RX_BUF_SIZE_MUL, 0);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct Descriptor {
|
||||
address: u32,
|
||||
control: u32,
|
||||
}
|
||||
|
||||
struct RxQueue {
|
||||
entries: DmaBuffer<[Descriptor]>,
|
||||
buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
|
||||
|
||||
rd: usize,
|
||||
}
|
||||
|
||||
struct TxQueue {
|
||||
entries: DmaBuffer<[Descriptor]>,
|
||||
buffers: Vec<Option<DmaBuffer<[u8]>>>,
|
||||
|
||||
wr: usize,
|
||||
rd: usize,
|
||||
}
|
||||
|
||||
pub struct Queue {
|
||||
rx_queue: IrqSafeSpinlock<RxQueue>,
|
||||
rx_queue_base: BusAddress,
|
||||
tx_queue: IrqSafeSpinlock<TxQueue>,
|
||||
tx_queue_base: BusAddress,
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
const USED: u32 = 1 << 31;
|
||||
const LENGTH: u32 = 0x1FFF;
|
||||
|
||||
const TX_WRAP: u32 = 1 << 30;
|
||||
const TX_LAST: u32 = 1 << 15;
|
||||
|
||||
const RX_OWNERSHIP: u32 = 1 << 0;
|
||||
const RX_WRAP: u32 = 1 << 1;
|
||||
|
||||
const EMPTY_RX: Self = Self {
|
||||
address: Self::RX_OWNERSHIP,
|
||||
control: 0,
|
||||
};
|
||||
const EMPTY_TX: Self = Self {
|
||||
address: 0,
|
||||
control: Self::USED,
|
||||
};
|
||||
|
||||
fn setup_rx(&mut self, buffer: BusAddress, wrap: bool) {
|
||||
let mut address = buffer.try_into_u32().unwrap();
|
||||
if wrap {
|
||||
address |= Self::RX_WRAP;
|
||||
}
|
||||
self.address = address;
|
||||
self.control = 0;
|
||||
}
|
||||
|
||||
fn setup_tx(&mut self, buffer: BusAddress, len: usize, wrap: bool) {
|
||||
assert!(len < Self::LENGTH as usize - 1);
|
||||
let mut control = len as u32;
|
||||
if wrap {
|
||||
control |= Self::TX_WRAP;
|
||||
}
|
||||
// LAST
|
||||
control |= Self::TX_LAST;
|
||||
self.address = buffer.try_into_u32().unwrap();
|
||||
self.control = control;
|
||||
}
|
||||
}
|
||||
|
||||
impl RxQueue {
|
||||
fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
|
||||
let mut entries = DmaBuffer::new_slice(dma, Descriptor::EMPTY_RX, capacity)?;
|
||||
let buffers = (0..capacity)
|
||||
.map(|_| DmaBuffer::new_uninit_slice(dma, RX_BUFFER_SIZE))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
for i in 0..capacity {
|
||||
entries[i].setup_rx(buffers[i].bus_address(), i == capacity - 1);
|
||||
}
|
||||
Ok(Self {
|
||||
buffers,
|
||||
entries,
|
||||
|
||||
rd: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn consume<F: Fn(DmaBuffer<[u8]>)>(
|
||||
&mut self,
|
||||
dma: &dyn DmaAllocator,
|
||||
packet_handler: F,
|
||||
) -> Result<usize, Error> {
|
||||
let mut count = 0;
|
||||
let capacity = self.entries.len();
|
||||
|
||||
loop {
|
||||
let index = self.rd % self.entries.len();
|
||||
|
||||
self.entries.cache_flush_element(index, false);
|
||||
|
||||
let entry = &mut self.entries[index];
|
||||
|
||||
if entry.address & Descriptor::RX_OWNERSHIP != 0 {
|
||||
// if entry.rx_completed().is_some() {
|
||||
// Grab the current buffer (the one just written to by the DMA), replace it
|
||||
// with the newly allocated one, and mark the descriptor as DMA-owned again
|
||||
let new_buffer = DmaBuffer::new_uninit_slice(dma, 4096)?;
|
||||
let new_buffer_address = new_buffer.bus_address();
|
||||
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
|
||||
let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
|
||||
// TODO packet size hint
|
||||
packet_handler(buffer);
|
||||
entry.setup_rx(new_buffer_address, index == capacity - 1);
|
||||
// entry.setup_rx(new_buffer_address, true)?;
|
||||
self.rd = self.rd.wrapping_add(1);
|
||||
count += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
impl TxQueue {
|
||||
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
|
||||
let mut entries = DmaBuffer::new_slice(dma, Descriptor::EMPTY_TX, capacity)?;
|
||||
entries[capacity - 1].control |= Descriptor::TX_WRAP;
|
||||
let buffers = (0..capacity).map(|_| None).collect();
|
||||
Ok(Self {
|
||||
entries,
|
||||
buffers,
|
||||
|
||||
wr: 0,
|
||||
rd: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn can_xmit(&self) -> bool {
|
||||
let capacity = self.entries.len();
|
||||
self.wr.wrapping_add(1) % capacity != self.rd % capacity
|
||||
}
|
||||
|
||||
pub fn push_xmit(&mut self, frame: DmaBuffer<[u8]>) -> Result<usize, Error> {
|
||||
if !self.can_xmit() {
|
||||
return Err(Error::WouldBlock);
|
||||
}
|
||||
|
||||
let address = frame.bus_address();
|
||||
let frame_len = frame.len();
|
||||
|
||||
if !(8..0x2000).contains(&frame_len) {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let capacity = self.entries.len();
|
||||
let index = self.wr % capacity;
|
||||
assert!(self.buffers[index].is_none());
|
||||
|
||||
frame.cache_flush_all(true);
|
||||
self.entries[index].setup_tx(address, frame_len, index == capacity - 1);
|
||||
// self.entries[index].setup_tx(address, frame_len, true)?;
|
||||
self.entries.cache_flush_element(index, true);
|
||||
|
||||
self.buffers[index] = Some(frame);
|
||||
self.wr = self.wr.wrapping_add(1);
|
||||
|
||||
Ok(self.wr % capacity)
|
||||
}
|
||||
|
||||
pub fn consume(&mut self) -> Result<usize, Error> {
|
||||
let mut count = 0;
|
||||
|
||||
loop {
|
||||
let index = self.rd % self.entries.len();
|
||||
|
||||
self.entries.cache_flush_element(index, false);
|
||||
|
||||
if self.rd == self.wr {
|
||||
break;
|
||||
}
|
||||
|
||||
let entry = &self.entries[index];
|
||||
|
||||
if entry.control & Descriptor::USED != 0 {
|
||||
let _ = self.buffers[index].take().unwrap();
|
||||
self.rd = self.rd.wrapping_add(1);
|
||||
count += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn with_capacity(
|
||||
dma: &dyn DmaAllocator,
|
||||
tx_capacity: usize,
|
||||
rx_capacity: usize,
|
||||
) -> Result<Self, Error> {
|
||||
let rx_queue = RxQueue::with_capacity(dma, rx_capacity)?;
|
||||
let rx_queue_base = rx_queue.entries.bus_address();
|
||||
let tx_queue = TxQueue::with_capacity(dma, tx_capacity)?;
|
||||
let tx_queue_base = tx_queue.entries.bus_address();
|
||||
|
||||
Ok(Self {
|
||||
rx_queue: IrqSafeSpinlock::new(rx_queue),
|
||||
rx_queue_base,
|
||||
tx_queue: IrqSafeSpinlock::new(tx_queue),
|
||||
tx_queue_base,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rx_queue_base(&self) -> BusAddress {
|
||||
self.rx_queue_base
|
||||
}
|
||||
pub fn tx_queue_base(&self) -> BusAddress {
|
||||
self.tx_queue_base
|
||||
}
|
||||
|
||||
pub fn push_xmit(&self, packet: DmaBuffer<[u8]>) -> Result<usize, Error> {
|
||||
self.tx_queue.lock().push_xmit(packet)
|
||||
}
|
||||
|
||||
pub fn consume_rx<F: Fn(DmaBuffer<[u8]>)>(
|
||||
&self,
|
||||
dma: &dyn DmaAllocator,
|
||||
packet_handler: F,
|
||||
) -> Result<usize, Error> {
|
||||
self.rx_queue.lock().consume(dma, packet_handler)
|
||||
}
|
||||
|
||||
pub fn consume_tx(&self) -> Result<usize, Error> {
|
||||
self.tx_queue.lock().consume()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
use core::time::Duration;
|
||||
|
||||
use libk::{error::Error, task::runtime::pwait};
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use ygg_driver_net_core::ephy::MdioBus;
|
||||
use yggdrasil_abi::net::link::LinkState;
|
||||
|
||||
// use crate::ethernet::{CapFlags, Capabilities};
|
||||
|
||||
register_structs! {
|
||||
pub ControlRegs {
|
||||
(0x00 => pub tx_clk_sel: ReadWrite<u32>),
|
||||
(0x04 => _0),
|
||||
(0x20 => pub control_status_speed_mode: ReadWrite<u32>),
|
||||
(0x24 => _1),
|
||||
(0x40 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub NetworkControl [
|
||||
// Rx enable
|
||||
RE OFFSET(2) NUMBITS(1) [],
|
||||
// Tx enable
|
||||
TE OFFSET(3) NUMBITS(1) [],
|
||||
// MDIO enable
|
||||
MPE OFFSET(4) NUMBITS(1) [],
|
||||
// Start Tx
|
||||
TSTART OFFSET(9) NUMBITS(1) [],
|
||||
// Halt Tx
|
||||
THALT OFFSET(10) NUMBITS(1) [],
|
||||
],
|
||||
pub NetworkConfig [
|
||||
// Speed
|
||||
SPD OFFSET(0) NUMBITS(1) [],
|
||||
// Full duplex
|
||||
FD OFFSET(1) NUMBITS(1) [],
|
||||
PROMISC OFFSET(4) NUMBITS(1) [],
|
||||
// Gigabit mode enable
|
||||
GBE OFFSET(10) NUMBITS(1) [],
|
||||
],
|
||||
pub NetworkStatus [
|
||||
LINK OFFSET(0) NUMBITS(1) [],
|
||||
MDIO_IDLE OFFSET(2) NUMBITS(1) [],
|
||||
],
|
||||
pub DmaConfig [
|
||||
RX_BUF_SIZE OFFSET(16) NUMBITS(8) [],
|
||||
],
|
||||
pub Interrupt [
|
||||
// Rx complete
|
||||
RCOMP OFFSET(1) NUMBITS(1) [],
|
||||
// Tx complete
|
||||
TCOMP OFFSET(7) NUMBITS(1) [],
|
||||
// Link change
|
||||
LINK OFFSET(8) NUMBITS(1) [],
|
||||
],
|
||||
pub PhyMaintenance [
|
||||
DATA OFFSET(0) NUMBITS(16) [],
|
||||
CODE OFFSET(16) NUMBITS(2) [],
|
||||
REGA OFFSET(18) NUMBITS(5) [],
|
||||
PHYA OFFSET(23) NUMBITS(5) [],
|
||||
RW OFFSET(28) NUMBITS(2) [
|
||||
Read = 0b10,
|
||||
Write = 0b01,
|
||||
],
|
||||
SOF OFFSET(30) NUMBITS(1) [],
|
||||
],
|
||||
pub ModuleId [
|
||||
IDNUM OFFSET(16) NUMBITS(12) [],
|
||||
REV OFFSET(0) NUMBITS(16) [],
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
pub Regs {
|
||||
(0x000 => pub network_control: ReadWrite<u32, NetworkControl::Register>),
|
||||
(0x004 => pub network_config: ReadWrite<u32, NetworkConfig::Register>),
|
||||
(0x008 => pub network_status: ReadOnly<u32, NetworkStatus::Register>),
|
||||
(0x00C => pub user_io: ReadWrite<u32>),
|
||||
(0x010 => pub dma_config: ReadWrite<u32, DmaConfig::Register>),
|
||||
(0x014 => pub transmit_status: ReadWrite<u32>),
|
||||
(0x018 => pub rx_buffer_queue_base: ReadWrite<u32>),
|
||||
(0x01C => pub tx_buffer_queue_base: ReadWrite<u32>),
|
||||
(0x020 => pub rx_status: ReadWrite<u32>),
|
||||
(0x024 => pub interrupt_status: ReadWrite<u32, Interrupt::Register>),
|
||||
(0x028 => pub interrupt_enable: WriteOnly<u32, Interrupt::Register>),
|
||||
(0x02C => pub interrupt_disable: WriteOnly<u32, Interrupt::Register>),
|
||||
(0x030 => pub interrupt_mask: ReadOnly<u32, Interrupt::Register>),
|
||||
(0x034 => pub phy_maintenance: ReadWrite<u32, PhyMaintenance::Register>),
|
||||
(0x038 => pub rx_pause_quantum: ReadOnly<u32>),
|
||||
(0x03C => pub tx_pause_quantum: ReadWrite<u32>),
|
||||
(0x040 => pub tx_partial_store_fwd: ReadWrite<u32>),
|
||||
(0x044 => pub rx_partial_store_fwd: ReadWrite<u32>),
|
||||
(0x048 => _0),
|
||||
(0x080 => pub hash_register_bottom: ReadWrite<u32>),
|
||||
(0x084 => pub hash_register_top: ReadWrite<u32>),
|
||||
(0x088 => pub specific_address_x: [ReadWrite<u32>; 8]),
|
||||
(0x0A8 => pub type_id_match: [ReadWrite<u32>; 4]),
|
||||
(0x0B8 => pub wake_on_lan: ReadWrite<u32>),
|
||||
(0x0BC => pub ipg_stretch: ReadWrite<u32>),
|
||||
(0x0C0 => pub stacked_vlan: ReadWrite<u32>),
|
||||
(0x0C4 => pub tx_pfc_pause: ReadWrite<u32>),
|
||||
(0x0C8 => pub specific_address_mask: [ReadWrite<u32>; 2]),
|
||||
(0x0D0 => pub rx_buffer_ahb_mask: ReadWrite<u32>),
|
||||
(0x0D4 => pub ptp_rx_unicast_dst_ip: ReadWrite<u32>),
|
||||
(0x0D8 => pub ptp_tx_unicast_dst_ip: ReadWrite<u32>),
|
||||
(0x0DC => pub tsu_timer_cval_nanos: ReadWrite<u32>),
|
||||
(0x0E0 => pub tsu_timer_cval_sec: ReadWrite<u32>),
|
||||
(0x0E4 => _1),
|
||||
(0x0FC => pub module_id: ReadOnly<u32, ModuleId::Register>),
|
||||
(0x100 => _2),
|
||||
(0x200 => pub pcs_control: ReadWrite<u32>),
|
||||
(0x204 => pub pcs_status: ReadOnly<u32>),
|
||||
(0x208 => pub pcs_phy_id_upper: ReadOnly<u32>),
|
||||
(0x20C => pub pcs_phy_id_lower: ReadOnly<u32>),
|
||||
(0x210 => pub pcs_aneg_adv: ReadWrite<u32>),
|
||||
(0x214 => pub pcs_aneg_link_ability: ReadOnly<u32>),
|
||||
(0x218 => pub pcs_aneg_expansion: ReadOnly<u32>),
|
||||
(0x21C => pub pcs_aneg_next_page: ReadWrite<u32>),
|
||||
(0x220 => pub pcs_aneg_link_next_page: ReadOnly<u32>),
|
||||
(0x224 => _3),
|
||||
(0x280 => pub design_config_1: ReadOnly<u32>),
|
||||
(0x284 => pub design_config_2: ReadOnly<u32>),
|
||||
(0x288 => pub design_config_3: ReadOnly<u32>),
|
||||
(0x28C => pub design_config_4: ReadOnly<u32>),
|
||||
(0x290 => pub design_config_5: ReadOnly<u32>),
|
||||
(0x294 => pub design_config_6: ReadOnly<u32>),
|
||||
(0x298 => pub design_config_7: ReadOnly<u32>),
|
||||
(0x29C => _4),
|
||||
(0x400 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
// pub fn probe_capabilities(&self) -> Capabilities {
|
||||
// let mut flags = CapFlags::empty();
|
||||
// let mut queue_mask = 0x1;
|
||||
// let mut queue_count = 1;
|
||||
// let module_id = self.module_id.read(ModuleId::IDNUM);
|
||||
|
||||
// if module_id >= 0x2 {
|
||||
// flags |= CapFlags::MACB_IS_GEM;
|
||||
|
||||
// queue_mask |= self.design_config_6.get() & 0xFF;
|
||||
// queue_count = queue_mask.count_ones() as usize;
|
||||
// }
|
||||
|
||||
// Capabilities {
|
||||
// flags,
|
||||
// queue_mask,
|
||||
// queue_count,
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn link_state(&self) -> LinkState {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn wait_mdio_idle(&self, timeout: Duration) -> Result<(), Error> {
|
||||
pwait(timeout, Duration::from_millis(20), || {
|
||||
self.network_status
|
||||
.matches_all(NetworkStatus::MDIO_IDLE::SET)
|
||||
})
|
||||
}
|
||||
}
|
||||
impl MdioBus for Regs {
|
||||
fn mii_read(&self, phyaddr: u8, regaddr: u8) -> Result<u16, Error> {
|
||||
self.wait_mdio_idle(Duration::from_secs(1))?;
|
||||
self.phy_maintenance.write(
|
||||
PhyMaintenance::CODE.val(2)
|
||||
+ PhyMaintenance::PHYA.val(phyaddr as _)
|
||||
+ PhyMaintenance::REGA.val(regaddr as _)
|
||||
+ PhyMaintenance::SOF.val(1)
|
||||
+ PhyMaintenance::RW::Read,
|
||||
);
|
||||
self.wait_mdio_idle(Duration::from_secs(1))?;
|
||||
let value = self.phy_maintenance.read(PhyMaintenance::DATA);
|
||||
Ok(value as u16)
|
||||
}
|
||||
fn mii_write(&self, phyaddr: u8, regaddr: u8, value: u16) -> Result<(), Error> {
|
||||
self.wait_mdio_idle(Duration::from_secs(1))?;
|
||||
self.phy_maintenance.write(
|
||||
PhyMaintenance::CODE.val(2)
|
||||
+ PhyMaintenance::PHYA.val(phyaddr as _)
|
||||
+ PhyMaintenance::REGA.val(regaddr as _)
|
||||
+ PhyMaintenance::SOF.val(1)
|
||||
+ PhyMaintenance::DATA.val(value as _)
|
||||
+ PhyMaintenance::RW::Write,
|
||||
);
|
||||
self.wait_mdio_idle(Duration::from_secs(1))
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,5 @@
|
||||
extern crate alloc;
|
||||
|
||||
mod clock;
|
||||
mod ethernet;
|
||||
mod uart;
|
||||
|
||||
Reference in New Issue
Block a user