zynq7000_hal/eth/
mod.rs

1//! # Ethernet module
2//!
3//! ## Examples
4//!
5//! - [Zedboard Ethernet](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/zedboard/src/bin/ethernet.rs)
6use arbitrary_int::{u2, u3};
7pub use zynq7000::eth::MdcClockDivisor;
8use zynq7000::eth::{
9    BurstLength, DmaRxBufSize, GEM_0_BASE_ADDR, GEM_1_BASE_ADDR, InterruptControl, InterruptStatus,
10    MmioEthernet, RxStatus, TxStatus,
11};
12
13pub use ll::{ClockConfig, ClockDivSet, Duplex, EthernetLowLevel, Speed};
14
15pub mod embassy_net;
16pub mod ll;
17pub mod mdio;
18pub mod rx_descr;
19pub mod smoltcp;
20pub mod tx_descr;
21
22pub const MTU: usize = 1536;
23pub const MAX_MDC_SPEED: Hertz = Hertz::from_raw(2_500_000);
24
25#[repr(C, align(32))]
26#[derive(Debug, Clone, Copy)]
27pub struct AlignedBuffer(pub [u8; MTU]);
28
29#[cfg(not(feature = "7z010-7z007s-clg225"))]
30use crate::gpio::mio::{
31    Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27,
32};
33use crate::{
34    clocks::ArmClocks,
35    eth::ll::ClockDivisors,
36    gpio::{
37        IoPeriphPin,
38        mio::{
39            Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39,
40            Mio52, Mio53, MioPin, MuxConfig, Pin,
41        },
42    },
43    time::Hertz,
44};
45
46pub const MUX_CONF_PHY: MuxConfig = MuxConfig::new_with_l0();
47pub const MUX_CONF_MDIO: MuxConfig = MuxConfig::new_with_l3(u3::new(0b100));
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum EthernetId {
51    Eth0 = 0,
52    Eth1 = 1,
53}
54
55impl EthernetId {
56    /// Steal the ethernet register block for the given ethernet ID.
57    ///
58    /// # Safety
59    ///
60    /// Circumvents ownership and safety guarantees of the HAL.
61    pub const unsafe fn steal_regs(&self) -> zynq7000::eth::MmioEthernet<'static> {
62        unsafe {
63            match self {
64                EthernetId::Eth0 => zynq7000::eth::Ethernet::new_mmio_fixed_0(),
65                EthernetId::Eth1 => zynq7000::eth::Ethernet::new_mmio_fixed_1(),
66            }
67        }
68    }
69
70    pub fn clk_config_regs(
71        &self,
72        slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
73    ) -> (
74        *mut zynq7000::slcr::clocks::GigEthClockControl,
75        *mut zynq7000::slcr::clocks::GigEthRclkControl,
76    ) {
77        match self {
78            EthernetId::Eth0 => (
79                slcr.clk_ctrl().pointer_to_gem_0_clk_ctrl(),
80                slcr.clk_ctrl().pointer_to_gem_0_rclk_ctrl(),
81            ),
82            EthernetId::Eth1 => (
83                slcr.clk_ctrl().pointer_to_gem_1_clk_ctrl(),
84                slcr.clk_ctrl().pointer_to_gem_1_rclk_ctrl(),
85            ),
86        }
87    }
88}
89
90pub trait PsEthernet {
91    fn reg_block(&self) -> MmioEthernet<'static>;
92    fn id(&self) -> Option<EthernetId>;
93}
94
95impl PsEthernet for MmioEthernet<'static> {
96    #[inline]
97    fn reg_block(&self) -> MmioEthernet<'static> {
98        unsafe { self.clone() }
99    }
100
101    #[inline]
102    fn id(&self) -> Option<EthernetId> {
103        let base_addr = unsafe { self.ptr() } as usize;
104        if base_addr == GEM_0_BASE_ADDR {
105            return Some(EthernetId::Eth0);
106        } else if base_addr == GEM_1_BASE_ADDR {
107            return Some(EthernetId::Eth1);
108        }
109        None
110    }
111}
112
113pub trait TxClockPin: MioPin {
114    const ETH_ID: EthernetId;
115}
116pub trait TxControlPin: MioPin {
117    const ETH_ID: EthernetId;
118}
119pub trait TxData0Pin: MioPin {
120    const ETH_ID: EthernetId;
121}
122pub trait TxData1Pin: MioPin {
123    const ETH_ID: EthernetId;
124}
125pub trait TxData2Pin: MioPin {
126    const ETH_ID: EthernetId;
127}
128pub trait TxData3Pin: MioPin {
129    const ETH_ID: EthernetId;
130}
131pub trait RxClockPin: MioPin {
132    const ETH_ID: EthernetId;
133}
134pub trait RxControlPin: MioPin {
135    const ETH_ID: EthernetId;
136}
137pub trait RxData0Pin: MioPin {
138    const ETH_ID: EthernetId;
139}
140pub trait RxData1Pin: MioPin {
141    const ETH_ID: EthernetId;
142}
143pub trait RxData2Pin: MioPin {
144    const ETH_ID: EthernetId;
145}
146pub trait RxData3Pin: MioPin {
147    const ETH_ID: EthernetId;
148}
149
150pub trait MdClockPin: MioPin {}
151pub trait MdIoPin: MioPin {}
152
153impl MdClockPin for Pin<Mio52> {}
154impl MdIoPin for Pin<Mio53> {}
155
156#[cfg(not(feature = "7z010-7z007s-clg225"))]
157impl TxClockPin for Pin<Mio16> {
158    const ETH_ID: EthernetId = EthernetId::Eth0;
159}
160#[cfg(not(feature = "7z010-7z007s-clg225"))]
161impl TxControlPin for Pin<Mio21> {
162    const ETH_ID: EthernetId = EthernetId::Eth0;
163}
164#[cfg(not(feature = "7z010-7z007s-clg225"))]
165impl TxData0Pin for Pin<Mio17> {
166    const ETH_ID: EthernetId = EthernetId::Eth0;
167}
168#[cfg(not(feature = "7z010-7z007s-clg225"))]
169impl TxData1Pin for Pin<Mio18> {
170    const ETH_ID: EthernetId = EthernetId::Eth0;
171}
172#[cfg(not(feature = "7z010-7z007s-clg225"))]
173impl TxData2Pin for Pin<Mio19> {
174    const ETH_ID: EthernetId = EthernetId::Eth0;
175}
176#[cfg(not(feature = "7z010-7z007s-clg225"))]
177impl TxData3Pin for Pin<Mio20> {
178    const ETH_ID: EthernetId = EthernetId::Eth0;
179}
180#[cfg(not(feature = "7z010-7z007s-clg225"))]
181impl RxClockPin for Pin<Mio22> {
182    const ETH_ID: EthernetId = EthernetId::Eth0;
183}
184#[cfg(not(feature = "7z010-7z007s-clg225"))]
185impl RxControlPin for Pin<Mio27> {
186    const ETH_ID: EthernetId = EthernetId::Eth0;
187}
188#[cfg(not(feature = "7z010-7z007s-clg225"))]
189impl RxData0Pin for Pin<Mio23> {
190    const ETH_ID: EthernetId = EthernetId::Eth0;
191}
192#[cfg(not(feature = "7z010-7z007s-clg225"))]
193impl RxData1Pin for Pin<Mio24> {
194    const ETH_ID: EthernetId = EthernetId::Eth0;
195}
196#[cfg(not(feature = "7z010-7z007s-clg225"))]
197impl RxData2Pin for Pin<Mio25> {
198    const ETH_ID: EthernetId = EthernetId::Eth0;
199}
200#[cfg(not(feature = "7z010-7z007s-clg225"))]
201impl RxData3Pin for Pin<Mio26> {
202    const ETH_ID: EthernetId = EthernetId::Eth0;
203}
204
205impl TxClockPin for Pin<Mio28> {
206    const ETH_ID: EthernetId = EthernetId::Eth1;
207}
208impl TxControlPin for Pin<Mio33> {
209    const ETH_ID: EthernetId = EthernetId::Eth1;
210}
211impl TxData0Pin for Pin<Mio29> {
212    const ETH_ID: EthernetId = EthernetId::Eth1;
213}
214impl TxData1Pin for Pin<Mio30> {
215    const ETH_ID: EthernetId = EthernetId::Eth1;
216}
217impl TxData2Pin for Pin<Mio31> {
218    const ETH_ID: EthernetId = EthernetId::Eth1;
219}
220impl TxData3Pin for Pin<Mio32> {
221    const ETH_ID: EthernetId = EthernetId::Eth1;
222}
223impl RxClockPin for Pin<Mio34> {
224    const ETH_ID: EthernetId = EthernetId::Eth1;
225}
226impl RxControlPin for Pin<Mio39> {
227    const ETH_ID: EthernetId = EthernetId::Eth1;
228}
229impl RxData0Pin for Pin<Mio35> {
230    const ETH_ID: EthernetId = EthernetId::Eth1;
231}
232impl RxData1Pin for Pin<Mio36> {
233    const ETH_ID: EthernetId = EthernetId::Eth1;
234}
235impl RxData2Pin for Pin<Mio37> {
236    const ETH_ID: EthernetId = EthernetId::Eth1;
237}
238impl RxData3Pin for Pin<Mio38> {
239    const ETH_ID: EthernetId = EthernetId::Eth1;
240}
241
242/// Calculate the CPU 1x clock divisor required to achieve a clock speed which is below
243/// 2.5 MHz, as specified by the 802.3 standard.
244pub fn calculate_mdc_clk_div(arm_clks: &ArmClocks) -> Option<MdcClockDivisor> {
245    let div = arm_clks.cpu_1x_clk().raw().div_ceil(MAX_MDC_SPEED.raw());
246    match div {
247        0..8 => Some(MdcClockDivisor::Div8),
248        8..16 => Some(MdcClockDivisor::Div16),
249        16..32 => Some(MdcClockDivisor::Div32),
250        32..48 => Some(MdcClockDivisor::Div48),
251        48..64 => Some(MdcClockDivisor::Div64),
252        64..96 => Some(MdcClockDivisor::Div96),
253        96..128 => Some(MdcClockDivisor::Div128),
254        128..224 => Some(MdcClockDivisor::Div224),
255        // MDC clock divisor is too high for the maximum speed.
256        // This is not a valid configuration.
257        _ => None,
258    }
259}
260
261#[derive(Debug, Clone, Copy)]
262pub struct EthernetConfig {
263    pub clk_config_1000_mbps: ClockConfig,
264    pub mdc_clk_div: MdcClockDivisor,
265    pub mac_address: [u8; 6],
266}
267
268impl EthernetConfig {
269    /// Creates a new Ethernet configuration.
270    pub fn new(
271        clk_config_1000_mbps: ClockConfig,
272        mdc_clk_div: MdcClockDivisor,
273        mac_address: [u8; 6],
274    ) -> Self {
275        Self {
276            clk_config_1000_mbps,
277            mdc_clk_div,
278            mac_address,
279        }
280    }
281}
282
283/// Higher-level ethernet abstraction.
284pub struct Ethernet {
285    ll: ll::EthernetLowLevel,
286    mdio: mdio::Mdio,
287    current_speed: Speed,
288    current_duplex: Duplex,
289    current_clk_divs: ClockDivisors,
290}
291
292const IRQ_CONTROL: InterruptControl = InterruptControl::builder()
293    .with_tsu_sec_incr(false)
294    .with_partner_pg_rx(false)
295    .with_auto_negotiation_complete(false)
296    .with_external_interrupt(false)
297    .with_pause_transmitted(false)
298    .with_pause_time_zero(false)
299    .with_pause_with_non_zero_quantum(false)
300    .with_hresp_not_ok(true)
301    .with_rx_overrun(true)
302    .with_link_changed(false)
303    .with_frame_sent(true)
304    .with_tx_frame_corruption_ahb_error(true)
305    .with_tx_retry_limit_reached_or_late_collision(true)
306    .with_tx_descr_read_when_used(true)
307    .with_rx_descr_read_when_used(true)
308    .with_frame_received(true)
309    .with_mgmt_frame_sent(false)
310    .build();
311
312const IRQ_CLEAR_ALL: InterruptStatus = InterruptStatus::builder()
313    .with_tsu_sec_incr(true)
314    .with_partner_pg_rx(true)
315    .with_auto_negotiation_complete(true)
316    .with_external_interrupt(true)
317    .with_pause_transmitted(true)
318    .with_pause_time_zero(true)
319    .with_pause_with_non_zero_quantum(true)
320    .with_hresp_not_ok(true)
321    .with_rx_overrun(true)
322    .with_link_changed(true)
323    .with_frame_sent(true)
324    .with_tx_retry_limit_reached_or_late_collision(true)
325    .with_tx_descr_read_when_used(true)
326    .with_rx_descr_read_when_used(true)
327    .with_frame_received(true)
328    .with_mgmt_frame_sent(true)
329    .build();
330
331impl Ethernet {
332    /// Creates a new Ethernet instance with the given configuration while also
333    /// configuring all the necessary MIO pins.
334    #[allow(clippy::too_many_arguments)]
335    pub fn new_with_mio<
336        TxClock: TxClockPin,
337        TxControl: TxControlPin,
338        TxData0: TxData0Pin,
339        TxData1: TxData1Pin,
340        TxData2: TxData2Pin,
341        TxData3: TxData3Pin,
342        RxClock: RxClockPin,
343        RxControl: RxControlPin,
344        RxData0: RxData0Pin,
345        RxData1: RxData1Pin,
346        RxData2: RxData2Pin,
347        RxData3: RxData3Pin,
348        MdClock: MdClockPin,
349        MdIo: MdIoPin,
350    >(
351        mut ll: ll::EthernetLowLevel,
352        config: EthernetConfig,
353        tx_clk: TxClock,
354        tx_ctrl: TxControl,
355        tx_data: (TxData0, TxData1, TxData2, TxData3),
356        rx_clk: RxClock,
357        rx_ctrl: RxControl,
358        rx_data: (RxData0, RxData1, RxData2, RxData3),
359        md_pins: Option<(MdClock, MdIo)>,
360    ) -> Self {
361        Self::common_init(&mut ll, config.mac_address);
362        let tx_mio_config = zynq7000::slcr::mio::Config::builder()
363            .with_disable_hstl_rcvr(true)
364            .with_pullup(true)
365            .with_io_type(zynq7000::slcr::mio::IoType::Hstl)
366            .with_speed(zynq7000::slcr::mio::Speed::FastCmosEdge)
367            .with_l3_sel(MUX_CONF_PHY.l3_sel())
368            .with_l2_sel(MUX_CONF_PHY.l2_sel())
369            .with_l1_sel(MUX_CONF_PHY.l1_sel())
370            .with_l0_sel(MUX_CONF_PHY.l0_sel())
371            .with_tri_enable(false)
372            .build();
373        let rx_mio_config = zynq7000::slcr::mio::Config::builder()
374            .with_disable_hstl_rcvr(false)
375            .with_pullup(true)
376            .with_io_type(zynq7000::slcr::mio::IoType::Hstl)
377            .with_speed(zynq7000::slcr::mio::Speed::FastCmosEdge)
378            .with_l3_sel(MUX_CONF_PHY.l3_sel())
379            .with_l2_sel(MUX_CONF_PHY.l2_sel())
380            .with_l1_sel(MUX_CONF_PHY.l1_sel())
381            .with_l0_sel(MUX_CONF_PHY.l0_sel())
382            // TODO: What is correct now?
383            // Disable output driver.
384            .with_tri_enable(true)
385            //.with_tri_enable(false)
386            .build();
387        unsafe {
388            crate::slcr::Slcr::with(|slcr_mut| {
389                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
390                    tx_clk,
391                    slcr_mut,
392                    tx_mio_config,
393                );
394                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
395                    tx_ctrl,
396                    slcr_mut,
397                    tx_mio_config,
398                );
399                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
400                    tx_data.0,
401                    slcr_mut,
402                    tx_mio_config,
403                );
404                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
405                    tx_data.1,
406                    slcr_mut,
407                    tx_mio_config,
408                );
409                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
410                    tx_data.2,
411                    slcr_mut,
412                    tx_mio_config,
413                );
414                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
415                    tx_data.3,
416                    slcr_mut,
417                    tx_mio_config,
418                );
419                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
420                    rx_clk,
421                    slcr_mut,
422                    rx_mio_config,
423                );
424                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
425                    rx_ctrl,
426                    slcr_mut,
427                    rx_mio_config,
428                );
429                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
430                    rx_data.0,
431                    slcr_mut,
432                    rx_mio_config,
433                );
434                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
435                    rx_data.1,
436                    slcr_mut,
437                    rx_mio_config,
438                );
439                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
440                    rx_data.2,
441                    slcr_mut,
442                    rx_mio_config,
443                );
444                IoPeriphPin::new_with_full_config_and_unlocked_slcr(
445                    rx_data.3,
446                    slcr_mut,
447                    rx_mio_config,
448                );
449                if let Some((md_clk, md_io)) = md_pins {
450                    let md_mio_config = zynq7000::slcr::mio::Config::builder()
451                        .with_disable_hstl_rcvr(false)
452                        .with_pullup(true)
453                        .with_io_type(zynq7000::slcr::mio::IoType::LvCmos18)
454                        .with_speed(zynq7000::slcr::mio::Speed::SlowCmosEdge)
455                        .with_l3_sel(MUX_CONF_MDIO.l3_sel())
456                        .with_l2_sel(MUX_CONF_MDIO.l2_sel())
457                        .with_l1_sel(MUX_CONF_MDIO.l1_sel())
458                        .with_l0_sel(MUX_CONF_MDIO.l0_sel())
459                        .with_tri_enable(false)
460                        .build();
461                    IoPeriphPin::new_with_full_config_and_unlocked_slcr(
462                        md_clk,
463                        slcr_mut,
464                        md_mio_config,
465                    );
466                    IoPeriphPin::new_with_full_config_and_unlocked_slcr(
467                        md_io,
468                        slcr_mut,
469                        md_mio_config,
470                    );
471                }
472                // Enable VREF internal generator, which is required for HSTL pin mode.
473                slcr_mut.gpiob().modify_ctrl(|mut ctrl| {
474                    ctrl.set_vref_en(true);
475                    ctrl
476                });
477            });
478        }
479        ll.configure_clock(config.clk_config_1000_mbps, true);
480        let mut mdio = mdio::Mdio::new(&ll, true);
481        mdio.configure_clock_div(config.mdc_clk_div);
482        ll.regs.modify_net_ctrl(|mut val| {
483            val.set_management_port_enable(true);
484            val
485        });
486        let mut eth = Ethernet {
487            ll,
488            mdio,
489            current_speed: Speed::Mbps1000,
490            current_duplex: Duplex::Full,
491            current_clk_divs: config.clk_config_1000_mbps.divs,
492        };
493        eth.set_rx_buf_descriptor_base_address(0);
494        eth.set_tx_buf_descriptor_base_address(0);
495        eth
496    }
497
498    pub fn new(mut ll: EthernetLowLevel, config: EthernetConfig) -> Self {
499        Self::common_init(&mut ll, config.mac_address);
500        ll.configure_clock(config.clk_config_1000_mbps, true);
501        let mut mdio = mdio::Mdio::new(&ll, true);
502        mdio.configure_clock_div(config.mdc_clk_div);
503        Ethernet {
504            ll,
505            mdio,
506            current_speed: Speed::Mbps1000,
507            current_duplex: Duplex::Full,
508            current_clk_divs: config.clk_config_1000_mbps.divs,
509        }
510    }
511
512    /// Starts the peripheral by enabling relevant interrupts as well as the TX and RX blocks.
513    ///
514    /// The user should set valid TX and RX buffer queue addresses before calling this function.
515    pub fn start(&mut self) {
516        self.clear_interrupts();
517        self.enable_interrupts();
518        self.ll.regs.modify_net_ctrl(|mut val| {
519            val.set_tx_enable(true);
520            val.set_rx_enable(true);
521            val
522        });
523    }
524
525    /// Current speed settings.
526    #[inline]
527    pub fn speed(&self) -> Speed {
528        self.current_speed
529    }
530
531    /// Current duplex settings.
532    #[inline]
533    pub fn duplex(&self) -> Duplex {
534        self.current_duplex
535    }
536
537    pub fn reset(&mut self) {
538        self.ll.reset(3);
539        // Do not set RX and TX queue base address.
540        self.ll.initialize(false);
541    }
542
543    fn common_init(ll: &mut EthernetLowLevel, mac_address: [u8; 6]) {
544        ll.enable_peripheral_clock();
545        ll.reset(3);
546        ll.initialize(true);
547        // Set speed and duplex to sensible values, but these probably need to be set again after
548        // auto-negotiation has completed.
549        ll.set_speed_and_duplex(Speed::Mbps1000, Duplex::Full);
550        let macaddr_msbs = (u32::from(mac_address[5]) << 8) | u32::from(mac_address[4]);
551        let macaddr_lsbs = (u32::from(mac_address[3]) << 24)
552            | (u32::from(mac_address[2]) << 16)
553            | (u32::from(mac_address[1]) << 8)
554            | u32::from(mac_address[0]);
555        // Writing to the lower address portion disables the address match, writing to the higher
556        // portion enables it again. Address matching is disabled on reset, so we do not need
557        // to disable the other addresses here.
558        ll.regs.write_addr1_low(macaddr_lsbs);
559        ll.regs.write_addr1_high(macaddr_msbs);
560        ll.regs.modify_net_cfg(|mut val| {
561            val.set_rx_enable_1536(true);
562            val.set_rx_checksum_enable(true);
563            val.set_no_broadcast(false);
564            // val.set_pause_enable(true);
565            val
566        });
567        ll.regs.modify_dma_cfg(|mut val| {
568            val.set_rx_packet_buf_size_sel(u2::new(0b11));
569            val.set_tx_packet_buf_size_sel(true);
570            val.set_chksum_offload_enable(true);
571            val.set_burst_length(BurstLength::Incr16.reg_value());
572            // Configure 1536 bytes receive buffer size. This is sufficient for regular Ethernet
573            // frames.
574            val.set_dma_rx_ahb_buf_size_sel(DmaRxBufSize::new((MTU >> 6) as u8).unwrap());
575            val.set_endian_swap_mgmt_descriptor(zynq7000::eth::AhbEndianess::Little);
576            val.set_endian_swap_packet_data(zynq7000::eth::AhbEndianess::Little);
577            val
578        });
579    }
580
581    #[inline]
582    pub fn clear_interrupts(&mut self) {
583        self.ll.regs.write_interrupt_status(IRQ_CLEAR_ALL);
584    }
585
586    #[inline]
587    pub fn enable_interrupts(&mut self) {
588        self.ll.regs.write_interrupt_enable(IRQ_CONTROL);
589    }
590
591    #[inline]
592    pub fn disable_interrupts(&mut self) {
593        self.ll.regs.write_interrupt_disable(IRQ_CONTROL);
594    }
595
596    #[inline]
597    pub fn ll(&mut self) -> &mut EthernetLowLevel {
598        &mut self.ll
599    }
600
601    #[inline]
602    pub fn regs(&mut self) -> &MmioEthernet<'static> {
603        &self.ll.regs
604    }
605
606    #[inline]
607    pub fn mdio(&mut self) -> &mdio::Mdio {
608        &self.mdio
609    }
610
611    pub fn mdio_mut(&mut self) -> &mut mdio::Mdio {
612        &mut self.mdio
613    }
614
615    #[inline]
616    pub fn regs_mut(&mut self) -> &mut MmioEthernet<'static> {
617        &mut self.ll.regs
618    }
619
620    /// This function checks whether new auto-negotiated settings require driver settings
621    /// updates and perform them if necessary.
622    pub fn configure_clock_and_speed_duplex(
623        &mut self,
624        speed: Speed,
625        duplex: Duplex,
626        clk_collection: &ClockDivSet,
627    ) {
628        if speed == self.current_speed
629            && duplex == self.current_duplex
630            && *clk_collection.clk_divs_for_speed(speed) == self.current_clk_divs
631        {
632            // No change, do nothing.
633            return;
634        }
635        self.ll.set_tx_rx_enable(false, false);
636        self.ll
637            .configure_clock_and_speed_duplex(speed, duplex, clk_collection);
638        self.ll.set_tx_rx_enable(true, true);
639    }
640
641    delegate::delegate! {
642        to self.ll {
643            #[inline]
644            pub const fn id(&self) -> EthernetId;
645
646            #[inline]
647            pub fn set_rx_buf_descriptor_base_address(&mut self, addr: u32);
648
649            #[inline]
650            pub fn set_tx_buf_descriptor_base_address(&mut self, addr: u32);
651
652            #[inline]
653            pub fn set_tx_rx_enable(&mut self, tx_enable: bool, rx_enable: bool);
654        }
655    }
656}
657
658mod shared {
659    #[bitbybit::bitenum(u1, exhaustive = true)]
660    #[derive(Debug, PartialEq, Eq)]
661    pub enum Ownership {
662        Hardware = 0,
663        Software = 1,
664    }
665}
666
667/// Possibly critical errors.
668#[derive(Debug, Clone, Copy, Default)]
669pub struct EthErrors {
670    /// According to the TMR, p.551, this condition implies a packet is dropped because the
671    /// packet buffer is full. It occurs occasionally when the controller is unable to process
672    /// the packets if they arrive very fast. No special action for error recovery needs to
673    /// be taken, but we still report it.
674    pub rx_overrun: bool,
675    /// The TMR recommends re-initializing the controller and the buffer descriptors for
676    /// receive and transmit paths.
677    pub hresp_error: bool,
678    /// The TMR recommends disabling the ethernet transmitter, re-initializing the buffer
679    /// descriptors on the transmit side and re-enabling the transmitter.
680    pub tx_frame_corruption_ahb_error: bool,
681    /// Only set in Gigabit mode, for 10/100 mode, late collision and collisions are treated
682    /// the same.
683    pub tx_late_collision: bool,
684    /// In 10/100 mode, this is set when the retry limit was reached.
685    ///
686    /// According to the TMR, p.551, this implies there are a series of collisions for which
687    /// an Ethernet frame could not be sent out even with a number of retries in half-duplex mode.
688    /// No drastic measures need to be taken, but this could also be an indicator for a duplex
689    /// missmatch.
690    pub tx_retry_limit_reached: bool,
691}
692
693#[derive(Debug, Clone, Copy, Default)]
694pub struct InterruptResult {
695    pub frame_received: bool,
696    pub frame_sent: bool,
697    /// Possibly indicator for high traffic, not enough descriptors available or not handled
698    /// quick enough.
699    pub rx_descr_read_when_used: bool,
700    /// Indicator that driver is done sending all frames because it found a descriptor slot owned
701    /// by the software.
702    pub tx_descr_read_when_used: bool,
703    /// These are full errors.
704    pub errors: EthErrors,
705}
706
707impl InterruptResult {
708    #[inline]
709    pub fn has_errors(&self) -> bool {
710        self.errors.rx_overrun
711            || self.errors.hresp_error
712            || self.errors.tx_frame_corruption_ahb_error
713            || self.errors.tx_late_collision
714            || self.errors.tx_retry_limit_reached
715    }
716}
717
718/// Interrupt handler which also has dedicated handling for embassy-net.
719pub(crate) fn on_interrupt(
720    eth_id: EthernetId,
721    wake_embassy_tx: bool,
722    wake_embassy_rx: bool,
723) -> InterruptResult {
724    let mut eth_regs = unsafe { eth_id.steal_regs() };
725    let status = eth_regs.read_interrupt_status();
726    let mut clear = InterruptStatus::new_with_raw_value(0);
727    let mut tx_status_clear = TxStatus::new_with_raw_value(0);
728    let mut rx_status_clear = RxStatus::new_with_raw_value(0);
729    let mut disable = InterruptControl::new_with_raw_value(0);
730    let mut result = InterruptResult::default();
731
732    if status.frame_sent() {
733        if wake_embassy_tx {
734            embassy_net::TX_WAKER.wake();
735        }
736        result.frame_sent = true;
737        tx_status_clear.set_complete(true);
738        clear.set_frame_sent(true);
739    }
740    if status.frame_received() {
741        if wake_embassy_rx {
742            embassy_net::RX_WAKER.wake();
743        }
744        result.frame_received = true;
745        clear.set_frame_received(true);
746    }
747    if status.hresp_not_ok() {
748        result.errors.hresp_error = true;
749        clear.set_hresp_not_ok(true);
750        tx_status_clear.set_hresp_not_ok(true);
751        rx_status_clear.set_hresp_not_ok(true);
752    }
753    if status.tx_retry_limit_reached_or_late_collision() {
754        let tx_status = eth_regs.read_tx_status();
755        if tx_status.late_collision() {
756            result.errors.tx_late_collision = true;
757            tx_status_clear.set_late_collision(true);
758        } else {
759            result.errors.tx_retry_limit_reached = true;
760            tx_status_clear.set_retry_limit_reached(true);
761        }
762        // Clear this in any case.
763        tx_status_clear.set_collision(true);
764        clear.set_tx_retry_limit_reached_or_late_collision(true);
765    }
766    if status.tx_descr_read_when_used() {
767        result.tx_descr_read_when_used = true;
768        // The interrupt status bit is cleared on a read.
769        clear.set_tx_descr_read_when_used(true);
770        tx_status_clear.set_read_when_used(true);
771    }
772    if status.tx_frame_corruption_ahb_error() {
773        result.errors.tx_frame_corruption_ahb_error = true;
774        // The interrupt status bit is cleared on a read.
775        tx_status_clear.set_frame_corruption_ahb_error(true);
776    }
777    if status.rx_descr_read_when_used() {
778        result.rx_descr_read_when_used = true;
779        // I am guessing that those are related.
780        rx_status_clear.set_buf_not_available(true);
781        clear.set_rx_descr_read_when_used(true);
782    }
783    if status.rx_overrun() {
784        if wake_embassy_rx {
785            embassy_net::RX_WAKER.wake();
786        }
787        result.errors.rx_overrun = true;
788        rx_status_clear.set_overrun(true);
789        disable.set_rx_overrun(true);
790        clear.set_rx_overrun(true);
791    }
792    eth_regs.write_interrupt_status(clear);
793    eth_regs.write_interrupt_disable(disable);
794    eth_regs.write_tx_status(tx_status_clear);
795    eth_regs.write_rx_status(rx_status_clear);
796    result
797}