1use crate::event_manager::LiteXEventManager;
11use crate::litex_registers::{LiteXSoCRegisterConfiguration, Read, Write};
12use core::cell::Cell;
13use core::slice;
14use kernel::debug;
15use kernel::hil::ethernet::{EthernetAdapterDatapath, EthernetAdapterDatapathClient};
16use kernel::utilities::cells::{MapCell, OptionalCell, TakeCell};
17use kernel::utilities::StaticRef;
18use kernel::ErrorCode;
19
20const LITEETH_TX_EVENT: usize = 0;
23const LITEETH_RX_EVENT: usize = 0;
24
25type LiteEthRXEV<'a, R> = LiteXEventManager<
26 'a,
27 u8,
28 <R as LiteXSoCRegisterConfiguration>::ReadOnly8,
29 <R as LiteXSoCRegisterConfiguration>::ReadWrite8,
30 <R as LiteXSoCRegisterConfiguration>::ReadWrite8,
31>;
32type LiteEthTXEV<'a, R> = LiteEthRXEV<'a, R>;
33
34#[repr(C)]
35pub struct LiteEthMacRegisters<R: LiteXSoCRegisterConfiguration> {
36 rx_slot: R::ReadOnly8,
38 rx_length: R::ReadOnly32,
40 rx_errors: R::ReadOnly32,
42 rx_ev_status: R::ReadOnly8,
44 rx_ev_pending: R::ReadWrite8,
45 rx_ev_enable: R::ReadWrite8,
46
47 tx_start: R::ReadWrite8,
49 tx_ready: R::ReadOnly8,
51 tx_level: R::ReadOnly8,
53 tx_slot: R::ReadWrite8,
55 tx_length: R::ReadWrite16,
57 tx_ev_status: R::ReadOnly8,
59 tx_ev_pending: R::ReadWrite8,
60 tx_ev_enable: R::ReadWrite8,
61
62 preamble_crc: R::ReadWrite8,
64 preamble_errors: R::ReadOnly8,
66 crc_errors: R::ReadOnly32,
68}
69
70impl<R: LiteXSoCRegisterConfiguration> LiteEthMacRegisters<R> {
71 fn rx_ev(&self) -> LiteEthRXEV<'_, R> {
72 LiteEthRXEV::<R>::new(&self.rx_ev_status, &self.rx_ev_pending, &self.rx_ev_enable)
73 }
74
75 fn tx_ev(&self) -> LiteEthTXEV<'_, R> {
76 LiteEthTXEV::<R>::new(&self.tx_ev_status, &self.tx_ev_pending, &self.tx_ev_enable)
77 }
78}
79
80pub struct LiteEth<'a, const MAX_TX_SLOTS: usize, R: LiteXSoCRegisterConfiguration> {
81 mac_regs: StaticRef<LiteEthMacRegisters<R>>,
82 mac_memory_base: usize,
83 mac_memory_len: usize,
84 slot_size: usize,
85 rx_slots: usize,
86 tx_slots: usize,
87 client: OptionalCell<&'a dyn EthernetAdapterDatapathClient>,
88 tx_frame: TakeCell<'static, [u8]>,
89 tx_frame_info: MapCell<[(usize, u16); MAX_TX_SLOTS]>,
90 initialized: Cell<bool>,
91}
92
93impl<const MAX_TX_SLOTS: usize, R: LiteXSoCRegisterConfiguration> LiteEth<'_, MAX_TX_SLOTS, R> {
94 pub unsafe fn new(
95 mac_regs: StaticRef<LiteEthMacRegisters<R>>,
96 mac_memory_base: usize,
97 mac_memory_len: usize,
98 slot_size: usize,
99 rx_slots: usize,
100 tx_slots: usize,
101 ) -> Self {
102 LiteEth {
103 mac_regs,
104 mac_memory_base,
105 mac_memory_len,
106 slot_size,
107 rx_slots,
108 tx_slots,
109 client: OptionalCell::empty(),
110 tx_frame: TakeCell::empty(),
111 tx_frame_info: MapCell::new([(0, 0); MAX_TX_SLOTS]),
112 initialized: Cell::new(false),
113 }
114 }
115
116 pub fn initialize(&self) {
117 assert!(
124 (self.rx_slots + self.tx_slots) * self.slot_size <= self.mac_memory_len,
125 "LiteEth: slots would exceed assigned MAC memory area"
126 );
127
128 assert!(self.rx_slots > 0, "LiteEth: no RX slot");
129 assert!(self.tx_slots > 0, "LiteEth: no TX slot");
130
131 assert!(
134 MAX_TX_SLOTS >= self.tx_slots,
135 "LiteEth: MAX_TX_SLOTS ({}) must be larger or equal to tx_slots ({})",
136 MAX_TX_SLOTS,
137 self.tx_slots,
138 );
139
140 self.mac_regs.tx_ev().disable_event(LITEETH_TX_EVENT);
142
143 while self.mac_regs.rx_ev().event_pending(LITEETH_RX_EVENT) {
149 self.mac_regs.rx_ev().clear_event(LITEETH_RX_EVENT);
150 }
151 while self.mac_regs.tx_ev().event_pending(LITEETH_TX_EVENT) {
152 self.mac_regs.tx_ev().clear_event(LITEETH_TX_EVENT);
153 }
154
155 self.initialized.set(true);
156 }
157
158 #[allow(clippy::mut_from_ref)]
159 unsafe fn get_slot_buffer(&self, tx: bool, slot_id: usize) -> Option<&mut [u8]> {
160 if (tx && slot_id > self.tx_slots) || (!tx && slot_id > self.rx_slots) {
161 return None;
162 }
163
164 let slots_offset = if tx {
165 self.mac_memory_base + self.slot_size * self.rx_slots
166 } else {
167 self.mac_memory_base
168 };
169
170 let slot_addr = slots_offset + slot_id * self.slot_size;
171 Some(slice::from_raw_parts_mut(
172 slot_addr as *mut u8,
173 self.slot_size,
174 ))
175 }
176
177 fn rx_interrupt(&self) {
178 let pkt_len = self.mac_regs.rx_length.get();
180
181 let slot_id: usize = self.mac_regs.rx_slot.get().into();
183
184 let slot = unsafe {
191 self.get_slot_buffer(false, slot_id)
192 .expect("LiteEth: invalid RX slot id")
193 };
194
195 self.client
197 .map(|client| client.received_frame(&slot[..(pkt_len as usize)], None));
198
199 self.mac_regs.rx_ev().clear_event(LITEETH_RX_EVENT);
202 }
203
204 fn tx_interrupt(&self) {
205 self.mac_regs.tx_ev().clear_event(LITEETH_TX_EVENT);
214
215 if self.tx_frame.is_none() {
216 debug!("LiteEth: tx interrupt called without tx_frame set");
217 }
218
219 let frame = self
221 .tx_frame
222 .take()
223 .expect("LiteEth: TakeCell empty in tx callback");
224
225 let slot_id = 0; let (frame_identifier, len) = self
228 .tx_frame_info
229 .map(|pkt_info| pkt_info[slot_id])
230 .unwrap();
231
232 self.client.map(move |client| {
233 client.transmit_frame_done(Ok(()), frame, len, frame_identifier, None)
234 });
235 }
236
237 pub fn service_interrupt(&self) {
238 while self.mac_regs.tx_ev().event_asserted(LITEETH_TX_EVENT) {
242 self.tx_interrupt();
243 }
244
245 while self.mac_regs.rx_ev().event_asserted(LITEETH_RX_EVENT) {
249 self.rx_interrupt();
250 }
251 }
252}
253
254impl<'a, const MAX_TX_SLOTS: usize, R: LiteXSoCRegisterConfiguration> EthernetAdapterDatapath<'a>
255 for LiteEth<'a, MAX_TX_SLOTS, R>
256{
257 fn set_client(&self, client: &'a dyn EthernetAdapterDatapathClient) {
258 self.client.set(client);
259 }
260
261 fn enable_receive(&self) {
262 if !self.initialized.get() {
264 panic!("LiteEth: cannot enable_receive without prior initialization!");
265 }
266
267 self.mac_regs.rx_ev().enable_event(LITEETH_RX_EVENT);
268 }
269
270 fn disable_receive(&self) {
271 self.mac_regs.rx_ev().disable_event(LITEETH_RX_EVENT);
273 }
274
275 fn transmit_frame(
281 &self,
282 frame: &'static mut [u8],
283 len: u16,
284 frame_identifier: usize,
285 ) -> Result<(), (ErrorCode, &'static mut [u8])> {
286 if frame.len() < (len as usize) {
287 return Err((ErrorCode::INVAL, frame));
288 }
289
290 if self.tx_frame.is_some() {
291 return Err((ErrorCode::BUSY, frame));
292 }
293
294 let slot_id = 0;
296
297 let slot = unsafe { self.get_slot_buffer(true, slot_id) }.expect("LiteEth: no TX slot");
298 if slot.len() < (len as usize) {
299 return Err((ErrorCode::SIZE, frame));
300 }
301
302 self.tx_frame_info
304 .map(|pkt_info| {
305 pkt_info[slot_id] = (frame_identifier, len);
306 })
307 .unwrap();
308
309 slot[..(len as usize)].copy_from_slice(&frame[..(len as usize)]);
311
312 self.tx_frame.replace(frame);
314
315 self.mac_regs.tx_slot.set(0);
317 self.mac_regs.tx_length.set(len);
318
319 while self.mac_regs.tx_ready.get() == 0 {}
321
322 self.mac_regs.tx_ev().enable_event(LITEETH_TX_EVENT);
324
325 self.mac_regs.tx_start.set(1);
327
328 Ok(())
329 }
330}