Skip to content

Commit 0a3b105

Browse files
author
Johannes Draaijer
committed
Split up impls for MAC and DMA, and require that SMI has exclusive access to the
entirety of the MAC peripheral for the duration of SMI accesses
1 parent 7d43e3b commit 0a3b105

File tree

4 files changed

+382
-351
lines changed

4 files changed

+382
-351
lines changed

src/dma.rs

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
use cortex_m::peripheral::NVIC;
2+
3+
use crate::{
4+
rx::{RxPacket, RxRing},
5+
stm32::{Interrupt, ETHERNET_DMA},
6+
tx::TxRing,
7+
EthernetMAC, RxError, RxRingEntry, TxError, TxRingEntry,
8+
};
9+
10+
/// Ethernet DMA.
11+
pub struct EthernetDMA<'rx, 'tx> {
12+
eth_dma: ETHERNET_DMA,
13+
rx_ring: RxRing<'rx>,
14+
tx_ring: TxRing<'tx>,
15+
}
16+
17+
impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
18+
/// Create and initialise the ethernet DMA
19+
///
20+
/// # Note
21+
/// - Make sure that the buffers reside in a memory region that is
22+
/// accessible by the peripheral. Core-Coupled Memory (CCM) is
23+
/// usually not accessible.
24+
//
25+
// NOTE: eth_mac is unused, but required for initialization as
26+
// owning an [`EthernetMAC`] requires that all of it's checks
27+
// (GPIO, clock speed) have passed.
28+
pub fn new(
29+
#[allow(unused)] eth_mac: &EthernetMAC,
30+
eth_dma: ETHERNET_DMA,
31+
rx_buffer: &'rx mut [RxRingEntry],
32+
tx_buffer: &'tx mut [TxRingEntry],
33+
) -> Self {
34+
// reset DMA bus mode register
35+
eth_dma.dmabmr.modify(|_, w| w.sr().set_bit());
36+
37+
// Wait until done
38+
while eth_dma.dmabmr.read().sr().bit_is_set() {}
39+
40+
// operation mode register
41+
eth_dma.dmaomr.modify(|_, w| {
42+
// Dropping of TCP/IP checksum error frames disable
43+
w.dtcefd()
44+
.set_bit()
45+
// Receive store and forward
46+
.rsf()
47+
.set_bit()
48+
// Disable flushing of received frames
49+
.dfrf()
50+
.set_bit()
51+
// Transmit store and forward
52+
.tsf()
53+
.set_bit()
54+
// Forward error frames
55+
.fef()
56+
.set_bit()
57+
// Operate on second frame
58+
.osf()
59+
.set_bit()
60+
});
61+
62+
// bus mode register
63+
eth_dma.dmabmr.modify(|_, w| {
64+
// For any non-f107 chips, we must use enhanced descriptor format to support checksum
65+
// offloading and/or timestamps.
66+
#[cfg(not(feature = "stm32f107"))]
67+
let w = w.edfe().set_bit();
68+
69+
unsafe {
70+
// Address-aligned beats
71+
w.aab()
72+
.set_bit()
73+
// Fixed burst
74+
.fb()
75+
.set_bit()
76+
// Rx DMA PBL
77+
.rdp()
78+
.bits(32)
79+
// Programmable burst length
80+
.pbl()
81+
.bits(32)
82+
// Rx Tx priority ratio 2:1
83+
.pm()
84+
.bits(0b01)
85+
// Use separate PBL
86+
.usp()
87+
.set_bit()
88+
}
89+
});
90+
91+
let mut dma = EthernetDMA {
92+
eth_dma,
93+
rx_ring: RxRing::new(rx_buffer),
94+
tx_ring: TxRing::new(tx_buffer),
95+
};
96+
97+
dma.rx_ring.start(&dma.eth_dma);
98+
dma.tx_ring.start(&dma.eth_dma);
99+
100+
dma
101+
}
102+
103+
/// Enable RX and TX interrupts
104+
///
105+
/// In your handler you must call
106+
/// [`eth_interrupt_handler()`](fn.eth_interrupt_handler.html) to
107+
/// clear interrupt pending bits. Otherwise the interrupt will
108+
/// reoccur immediately.
109+
pub fn enable_interrupt(&self) {
110+
self.eth_dma.dmaier.modify(|_, w| {
111+
w
112+
// Normal interrupt summary enable
113+
.nise()
114+
.set_bit()
115+
// Receive Interrupt Enable
116+
.rie()
117+
.set_bit()
118+
// Transmit Interrupt Enable
119+
.tie()
120+
.set_bit()
121+
});
122+
123+
// Enable ethernet interrupts
124+
unsafe {
125+
NVIC::unmask(Interrupt::ETH);
126+
}
127+
}
128+
129+
/// Calls [`eth_interrupt_handler()`](fn.eth_interrupt_handler.html)
130+
pub fn interrupt_handler(&self) -> InterruptReasonSummary {
131+
let status = eth_interrupt_handler(&self.eth_dma);
132+
eth_interrupt_handler(&self.eth_dma);
133+
status
134+
}
135+
136+
/// Is Rx DMA currently running?
137+
///
138+
/// It stops if the ring is full. Call `recv_next()` to free an
139+
/// entry and to demand poll from the hardware.
140+
pub fn rx_is_running(&self) -> bool {
141+
self.rx_ring.running_state(&self.eth_dma).is_running()
142+
}
143+
144+
/// Receive the next packet (if any is ready), or return `None`
145+
/// immediately.
146+
pub fn recv_next(&mut self) -> Result<RxPacket, RxError> {
147+
self.rx_ring.recv_next(&self.eth_dma)
148+
}
149+
150+
/// Is Tx DMA currently running?
151+
pub fn tx_is_running(&self) -> bool {
152+
self.tx_ring.is_running(&self.eth_dma)
153+
}
154+
155+
/// Send a packet
156+
pub fn send<F: FnOnce(&mut [u8]) -> R, R>(
157+
&mut self,
158+
length: usize,
159+
f: F,
160+
) -> Result<R, TxError> {
161+
let result = self.tx_ring.send(length, f);
162+
self.tx_ring.demand_poll(&self.eth_dma);
163+
result
164+
}
165+
}
166+
167+
/// A summary of the reasons for the interrupt
168+
/// that occured
169+
pub struct InterruptReasonSummary {
170+
pub is_rx: bool,
171+
pub is_tx: bool,
172+
pub is_error: bool,
173+
}
174+
175+
/// Call in interrupt handler to clear interrupt reason, when
176+
/// [`enable_interrupt()`](struct.EthernetDMA.html#method.enable_interrupt).
177+
///
178+
/// There are two ways to call this:
179+
///
180+
/// * Via the [`EthernetDMA`](struct.EthernetDMA.html) driver instance that your interrupt handler has access to.
181+
/// * By unsafely getting `Peripherals`.
182+
pub fn eth_interrupt_handler(eth_dma: &ETHERNET_DMA) -> InterruptReasonSummary {
183+
let status = eth_dma.dmasr.read();
184+
185+
let status = InterruptReasonSummary {
186+
is_rx: status.rs().bit_is_set(),
187+
is_tx: status.ts().bit_is_set(),
188+
is_error: status.ais().bit_is_set(),
189+
};
190+
191+
eth_dma
192+
.dmasr
193+
.write(|w| w.nis().set_bit().ts().set_bit().rs().set_bit());
194+
195+
status
196+
}

0 commit comments

Comments
 (0)