stmmac: move to softirq approach

This commit is contained in:
Mark Poliakov 2025-02-14 00:18:08 +02:00
parent 57d46ed070
commit 6253ab282e
3 changed files with 62 additions and 28 deletions

1
Cargo.lock generated
View File

@ -2770,6 +2770,7 @@ dependencies = [
"bytemuck", "bytemuck",
"device-api", "device-api",
"device-tree", "device-tree",
"futures-util",
"libk", "libk",
"libk-mm", "libk-mm",
"libk-util", "libk-util",

View File

@ -15,3 +15,4 @@ ygg_driver_net_core.path = "../core"
tock-registers.workspace = true tock-registers.workspace = true
log.workspace = true log.workspace = true
bytemuck.workspace = true bytemuck.workspace = true
futures-util.workspace = true

View File

@ -10,9 +10,10 @@ use device_api::{
interrupt::{FullIrq, InterruptHandler, IrqVector}, interrupt::{FullIrq, InterruptHandler, IrqVector},
}; };
use device_tree::driver::{device_tree_driver, util::read_mac_address, Node, ProbeContext}; use device_tree::driver::{device_tree_driver, util::read_mac_address, Node, ProbeContext};
use libk::{device::external_interrupt_controller, dma::DmaBuffer, error::Error}; use futures_util::task::AtomicWaker;
use libk::{device::external_interrupt_controller, dma::DmaBuffer, error::Error, task::runtime};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo}; use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit}; use libk_util::{event::BitmapEvent, sync::IrqSafeSpinlock, OneTimeInit};
use regs::{ use regs::{
dma::{DMACiCR, DMACiIER, DMACiSR, DMACiTXCR, DMAC0RXCR, DMAMR, DMASBMR}, dma::{DMACiCR, DMACiIER, DMACiSR, DMACiTXCR, DMAC0RXCR, DMAMR, DMASBMR},
mac::{ mac::{
@ -28,7 +29,7 @@ use ygg_driver_net_core::{
RxPacket, RxPacket,
}; };
use yggdrasil_abi::net::{ use yggdrasil_abi::net::{
link::{EthernetLinkState, LinkState}, link::{Duplex, EthernetLinkState, EthernetSpeed, LinkState},
MacAddress, MacAddress,
}; };
@ -51,12 +52,29 @@ struct Stmmac {
resets: Vec<ResetHandle>, resets: Vec<ResetHandle>,
clocks: Vec<ClockHandle>, clocks: Vec<ClockHandle>,
irq: FullIrq, irq: FullIrq,
softirq_events: BitmapEvent<AtomicWaker>,
inner: OneTimeInit<Inner>, inner: OneTimeInit<Inner>,
iface_id: OneTimeInit<u32>, iface_id: OneTimeInit<u32>,
} }
impl Inner {
fn link_state(&self) -> LinkState {
// TODO read the link state properly, i.e., read from PHY's registers, I guess? I didn't
// find a nice way to read the link state from the MAC itself, it only seems to report
// the MAC<->PHY link state (which is always up, obviously)
LinkState::Ethernet(EthernetLinkState::Up(
EthernetSpeed::Unknown,
Duplex::Unknown,
))
}
}
impl Stmmac { impl Stmmac {
const SOFTIRQ_GMII_STATUS: u64 = 1 << 0;
const SOFTIRQ_TX_STATUS: u64 = 1 << 1;
const SOFTIRQ_RX_STATUS: u64 = 1 << 2;
pub fn start_xmit(&self, frame: DmaBuffer<[u8]>) -> Result<(), Error> { pub fn start_xmit(&self, frame: DmaBuffer<[u8]>) -> Result<(), Error> {
let inner = self.inner.get(); let inner = self.inner.get();
let regs = inner.regs.lock(); let regs = inner.regs.lock();
@ -73,6 +91,35 @@ impl Stmmac {
Ok(()) Ok(())
} }
async fn softirq(&self) {
let inner = self.inner.get();
let dma = self.dma.get();
let iface = *self.iface_id.get();
loop {
let events = self.softirq_events.wait().await;
if events & Self::SOFTIRQ_GMII_STATUS != 0 {
let link_status = inner.link_state();
log::info!("stmmac: link is {link_status}");
}
if events & Self::SOFTIRQ_TX_STATUS != 0 {
inner.tx_ring.lock().consume().ok();
}
if events & Self::SOFTIRQ_RX_STATUS != 0 {
let mut rx_ring = inner.rx_ring.lock();
rx_ring
.consume(dma.as_ref(), |packet| {
let packet = RxPacket::new(packet, 0, iface);
ygg_driver_net_core::receive_packet(packet).ok();
})
.ok();
}
}
}
} }
impl InterruptHandler for Stmmac { impl InterruptHandler for Stmmac {
@ -83,36 +130,17 @@ impl InterruptHandler for Stmmac {
let dma0_sr = regs.DMA.DMAC0SR.extract(); let dma0_sr = regs.DMA.DMAC0SR.extract();
if mac_isr.matches_all(MACISR::RGSMIIIS::SET) { if mac_isr.matches_all(MACISR::RGSMIIIS::SET) {
let macphycsr = regs.MAC.MACPHYCSR.extract(); let _ = regs.MAC.MACPHYCSR.get();
let state = if macphycsr.matches_all(MACPHYCSR::LNKSTS::Up) { self.softirq_events.signal(Self::SOFTIRQ_GMII_STATUS);
"up"
} else {
"down"
};
let mode = if macphycsr.matches_all(MACPHYCSR::LNKMOD::FullDuplex) {
"full-duplex"
} else {
"half-duplex"
};
log::info!("RGMII link status update");
log::info!("Link state: link={state}, mode={mode}");
} }
if dma0_sr.matches_all(DMACiSR::TI::SET) { if dma0_sr.matches_all(DMACiSR::TI::SET) {
regs.DMA.DMAC0SR.modify(DMACiSR::TI::SET); regs.DMA.DMAC0SR.modify(DMACiSR::TI::SET);
inner.tx_ring.lock().consume().ok(); self.softirq_events.signal(Self::SOFTIRQ_TX_STATUS);
} }
if dma0_sr.matches_all(DMACiSR::RI::SET) { if dma0_sr.matches_all(DMACiSR::RI::SET) {
let dma = self.dma.get();
regs.DMA.DMAC0SR.modify(DMACiSR::RI::SET); regs.DMA.DMAC0SR.modify(DMACiSR::RI::SET);
let iface = *self.iface_id.get(); self.softirq_events.signal(Self::SOFTIRQ_RX_STATUS);
let mut rx_ring = inner.rx_ring.lock();
rx_ring
.consume(dma.as_ref(), |packet| {
let packet = RxPacket::new(packet, 0, iface);
ygg_driver_net_core::receive_packet(packet).ok();
})
.ok();
} }
true true
@ -285,6 +313,9 @@ impl Device for Stmmac {
intc.enable_irq(self.irq.irq)?; intc.enable_irq(self.irq.irq)?;
let p = self.clone();
runtime::spawn(async move { p.softirq().await })?;
Ok(()) Ok(())
} }
@ -311,8 +342,7 @@ impl NetworkDevice for Stmmac {
} }
fn link_state(&self) -> LinkState { fn link_state(&self) -> LinkState {
// TODO self.inner.get().link_state()
LinkState::Ethernet(EthernetLinkState::Down)
} }
} }
@ -334,6 +364,8 @@ device_tree_driver! {
mac, mac,
irq, irq,
softirq_events: BitmapEvent::new(AtomicWaker::new()),
dma: OneTimeInit::new(), dma: OneTimeInit::new(),
inner: OneTimeInit::new(), inner: OneTimeInit::new(),
iface_id: OneTimeInit::new(), iface_id: OneTimeInit::new(),