1use core::{fmt::Debug, ptr::NonNull, time::Duration};
2
3use log::error;
4use mbarrier::mb;
5use tock_registers::{interfaces::*, register_bitfields, register_structs, registers::*};
6
7use crate::{DError, Speed, osal::wait_for};
8
9register_structs! {
10 pub MacRegister {
11 (0x0 => ctrl: ReadWrite<u32, CTRL::Register>),
12 (0x4 => _rsv1),
13 (0x8 => status: ReadOnly<u32, STATUS::Register>),
14 (0xC => _rsv2),
15 (0x18 => ctrl_ext: ReadWrite<u32, CTRL_EXT::Register>),
16 (0x1c => _rsv3),
17 (0x20 => mdic: ReadWrite<u32, MDIC::Register>),
18 (0x24 => _rsv4),
19 (0xc0 => icr: ReadWrite<u32, ICR::Register>),
20 (0xc4 => _rsv13),
21 (0xd0 => ims: ReadWrite<u32, IMS::Register>),
22 (0xd4 => _rsv14),
23 (0xd8 => imc: ReadWrite<u32, IMC::Register>),
24 (0xdc => _rsv15),
25 (0x100 => pub rctl: ReadWrite<u32, RCTL::Register>),
26 (0x104 => _rsv7),
27 (0x400 => tctl: ReadWrite<u32, TCTL::Register>),
28 (0x404 => _rsv12),
29 (0x1514 => gpie: ReadWrite<u32, GPIE::Register>),
30 (0x1518 => _rsv16),
31 (0x1524 => eims: ReadWrite<u32>),
32 (0x1528 => eimc: ReadWrite<u32>),
33 (0x152c => eiac: ReadWrite<u32>),
34 (0x1530 => eiam: ReadWrite<u32>),
35 (0x1534 => _rsv5),
36 (0x1580 => eicr: ReadWrite<u32>),
37 (0x1584 => _rsv6),
38 (0x5400 => ralh_0_15: [ReadWrite<u32>; 32]),
39 (0x5480 => _rsv8),
40 (0x54e0 => ralh_16_23: [ReadWrite<u32>;32]),
41 (0x5560 => _rsv9),
42 (0x5B50 => swsm: ReadWrite<u32, SWSM::Register>),
43 (0x5B54 => fwsm: ReadWrite<u32>),
44 (0x5B58 => _rsv10),
45 (0x5B5C => sw_fw_sync: ReadWrite<u32>),
46 (0x5B60 => _rsv11),
47
48 (0xEFFF => @END),
50 }
51}
52
53register_bitfields! [
54 u32,
56
57 CTRL [
58 FD OFFSET(0) NUMBITS(1)[
59 HalfDuplex = 0,
60 FullDuplex = 1,
61 ],
62 SLU OFFSET(6) NUMBITS(1)[],
63 SPEED OFFSET(8) NUMBITS(2)[
64 Speed10 = 0,
65 Speed100 = 1,
66 Speed1000 = 0b10,
67 ],
68 FRCSPD OFFSET(11) NUMBITS(1)[],
69 FRCDPLX OFFSET(12) NUMBITS(1)[],
70 RST OFFSET(26) NUMBITS(1)[
71 Normal = 0,
72 Reset = 1,
73 ],
74 PHY_RST OFFSET(31) NUMBITS(1)[],
75 ],
76 STATUS [
77 FD OFFSET(0) NUMBITS(1)[
78 HalfDuplex = 0,
79 FullDuplex = 1,
80 ],
81 LU OFFSET(1) NUMBITS(1)[],
82 SPEED OFFSET(6) NUMBITS(2)[
83 Speed10 = 0,
84 Speed100 = 1,
85 Speed1000 = 0b10,
86 ],
87 PHYRA OFFSET(10) NUMBITS(1)[],
88 ],
89 pub CTRL_EXT [
90 LINK_MODE OFFSET(22) NUMBITS(2)[
91 DircetCooper = 0,
92 SGMII = 0b10,
93 InternalSerdes = 0b11,
94 ],
95 ],
96 MDIC [
97 DATA OFFSET(0) NUMBITS(16)[],
98 REGADDR OFFSET(16) NUMBITS(5)[],
99 PHY_ADDR OFFSET(21) NUMBITS(5)[],
100 OP OFFSET(26) NUMBITS(2)[
101 Write = 0b1,
102 Read = 0b10,
103 ],
104 READY OFFSET(28) NUMBITS(1)[],
105 I OFFSET(29) NUMBITS(1)[],
106 E OFFSET(30) NUMBITS(1)[
107 NoError = 0,
108 Error = 1,
109 ],
110 Destination OFFSET(31) NUMBITS(1)[
111 Internal = 0,
112 External = 1,
113 ]
114 ],
115
116 SWSM [
117 SMBI OFFSET(0) NUMBITS(1)[],
118 SWESMBI OFFSET(1) NUMBITS(1)[],
119 WMNG OFFSET(2) NUMBITS(1)[],
120 EEUR OFFSET(3) NUMBITS(1)[],
121 ],
122
123 SW_FW_SYNC [
124 SW_EEP_SM OFFSET(0) NUMBITS(1)[],
125 SW_PHY_SM0 OFFSET(1) NUMBITS(1)[],
126 SW_PHY_SM1 OFFSET(2) NUMBITS(1)[],
127 SW_MAC_CSR_SM OFFSET(3) NUMBITS(1)[],
128 SW_FLASH_SM OFFSET(4) NUMBITS(1)[],
129
130 FW_EEP_SM OFFSET(16) NUMBITS(1)[],
131 FW_PHY_SM0 OFFSET(17) NUMBITS(1)[],
132 FW_PHY_SM1 OFFSET(18) NUMBITS(1)[],
133 FW_MAC_CSR_SM OFFSET(19) NUMBITS(1)[],
134 FW_FLASH_SM OFFSET(20) NUMBITS(1)[],
135 ],
136
137 pub RCTL [
138 RXEN OFFSET(1) NUMBITS(1)[
139 Disabled = 0,
140 Enabled = 1,
141 ],
142 SBP OFFSET(2) NUMBITS(1)[
143 DoNotStore = 0,
144 Store = 1,
145 ],
146 UPE OFFSET(3) NUMBITS(1)[
147 Disabled = 0,
148 Enabled = 1,
149 ],
150 MPE OFFSET(4) NUMBITS(1)[
151 Disabled = 0,
152 Enabled = 1,
153 ],
154 LPE OFFSET(5) NUMBITS(1)[
155 Disabled = 0,
156 Enabled = 1,
157 ],
158 LBM OFFSET(6) NUMBITS(2)[
159 Normal = 0b00,
160 MacLoopback = 0b01,
161 Reserved = 0b11,
162 ],
163 MO OFFSET(12) NUMBITS(2)[
164 Bits47_36 = 0b00,
165 Bits46_35 = 0b01,
166 Bits45_34 = 0b10,
167 Bits43_32 = 0b11,
168 ],
169 BAM OFFSET(15) NUMBITS(1)[
170 Ignore = 0,
171 Accept = 1,
172 ],
173 BSIZE OFFSET(16) NUMBITS(2)[
174 Bytes2048 = 0b00,
175 Bytes1024 = 0b01,
176 Bytes512 = 0b10,
177 Bytes256 = 0b11,
178 ],
179 VFE OFFSET(18) NUMBITS(1)[
180 Disabled = 0,
181 Enabled = 1,
182 ],
183 CFIEN OFFSET(19) NUMBITS(1)[
184 Disabled = 0,
185 Enabled = 1,
186 ],
187 CFI OFFSET(20) NUMBITS(1)[
188 Accept = 0,
189 Discard = 1,
190 ],
191 PSP OFFSET(21) NUMBITS(1)[],
192 DPF OFFSET(22) NUMBITS(1)[
193 Forward = 0,
194 Discard = 1,
195 ],
196 PMCF OFFSET(23) NUMBITS(1)[
197 Pass = 0,
198 Filter = 1,
199 ],
200 SECRC OFFSET(26) NUMBITS(1)[
201 DoNotStrip = 0,
202 Strip = 1,
203 ],
204 ],
205
206 TCTL [
208 EN OFFSET(1) NUMBITS(1)[
209 Disabled = 0,
210 Enabled = 1,
211 ],
212 PSP OFFSET(3) NUMBITS(1)[
213 Disabled = 0,
214 Enabled = 1,
215 ],
216 CT OFFSET(4) NUMBITS(8)[],
217 COLD OFFSET(12) NUMBITS(10)[],
218 SWXOFF OFFSET(22) NUMBITS(1)[],
219 RTLC OFFSET(24) NUMBITS(1)[],
220 NRTU OFFSET(25) NUMBITS(1)[],
221 MULR OFFSET(28) NUMBITS(1)[],
222 ],
223
224 EICR [
226 RxTxQ OFFSET(0) NUMBITS(16)[],
228 Reserved1 OFFSET(16) NUMBITS(14)[],
229 TCP_Timer OFFSET(30) NUMBITS(1)[],
230 Other_Cause OFFSET(31) NUMBITS(1)[],
231 ],
232
233 EICR_MSIX [
235 MSIX OFFSET(0) NUMBITS(25)[],
237 Reserved OFFSET(25) NUMBITS(7)[],
238 ],
239
240 EIMS [
242 RxTxQ OFFSET(0) NUMBITS(16)[],
244 Reserved1 OFFSET(16) NUMBITS(14)[],
245 TCP_Timer OFFSET(30) NUMBITS(1)[],
246 Other_Cause OFFSET(31) NUMBITS(1)[],
247 ],
248
249 EIMS_MSIX [
251 MSIX OFFSET(0) NUMBITS(25)[],
253 Reserved OFFSET(25) NUMBITS(7)[],
254 ],
255
256 ICR [
258 TXDW OFFSET(0) NUMBITS(1)[], TXQE OFFSET(1) NUMBITS(1)[], LSC OFFSET(2) NUMBITS(1)[], RXSEQ OFFSET(3) NUMBITS(1)[], RXDMT0 OFFSET(4) NUMBITS(1)[], RXO OFFSET(6) NUMBITS(1)[], RXT0 OFFSET(7) NUMBITS(1)[], MDAC OFFSET(9) NUMBITS(1)[], RXCFG OFFSET(10) NUMBITS(1)[], GPI_EN0 OFFSET(11) NUMBITS(1)[], GPI_EN1 OFFSET(12) NUMBITS(1)[], GPI_EN2 OFFSET(13) NUMBITS(1)[], GPI_EN3 OFFSET(14) NUMBITS(1)[], TXD_LOW OFFSET(15) NUMBITS(1)[], SRPD OFFSET(16) NUMBITS(1)[], ACK OFFSET(17) NUMBITS(1)[], MNG OFFSET(18) NUMBITS(1)[], DOCK OFFSET(19) NUMBITS(1)[], INT_ASSERTED OFFSET(31) NUMBITS(1)[], ],
278
279 IMS [
281 TXDW OFFSET(0) NUMBITS(1)[], TXQE OFFSET(1) NUMBITS(1)[], LSC OFFSET(2) NUMBITS(1)[], RXSEQ OFFSET(3) NUMBITS(1)[], RXDMT0 OFFSET(4) NUMBITS(1)[], RXO OFFSET(6) NUMBITS(1)[], RXT0 OFFSET(7) NUMBITS(1)[], MDAC OFFSET(9) NUMBITS(1)[], RXCFG OFFSET(10) NUMBITS(1)[], GPI_EN0 OFFSET(11) NUMBITS(1)[], GPI_EN1 OFFSET(12) NUMBITS(1)[], GPI_EN2 OFFSET(13) NUMBITS(1)[], GPI_EN3 OFFSET(14) NUMBITS(1)[], TXD_LOW OFFSET(15) NUMBITS(1)[], SRPD OFFSET(16) NUMBITS(1)[], ACK OFFSET(17) NUMBITS(1)[], MNG OFFSET(18) NUMBITS(1)[], DOCK OFFSET(19) NUMBITS(1)[], ],
300
301 IMC [
303 TXDW OFFSET(0) NUMBITS(1)[], TXQE OFFSET(1) NUMBITS(1)[], LSC OFFSET(2) NUMBITS(1)[], RXSEQ OFFSET(3) NUMBITS(1)[], RXDMT0 OFFSET(4) NUMBITS(1)[], RXO OFFSET(6) NUMBITS(1)[], RXT0 OFFSET(7) NUMBITS(1)[], MDAC OFFSET(9) NUMBITS(1)[], RXCFG OFFSET(10) NUMBITS(1)[], GPI_EN0 OFFSET(11) NUMBITS(1)[], GPI_EN1 OFFSET(12) NUMBITS(1)[], GPI_EN2 OFFSET(13) NUMBITS(1)[], GPI_EN3 OFFSET(14) NUMBITS(1)[], TXD_LOW OFFSET(15) NUMBITS(1)[], SRPD OFFSET(16) NUMBITS(1)[], ACK OFFSET(17) NUMBITS(1)[], MNG OFFSET(18) NUMBITS(1)[], DOCK OFFSET(19) NUMBITS(1)[], ],
322
323 GPIE [
325 NSICR OFFSET(0) NUMBITS(1)[
326 Normal = 0,
327 ClearOnRead = 1,
328 ],
329 Multiple_MSIX OFFSET(4) NUMBITS(1)[
330 SingleVector = 0,
331 MultipleVectors = 1,
332 ],
333 LL_Interval OFFSET(7) NUMBITS(5)[], EIAME OFFSET(30) NUMBITS(1)[
335 Disabled = 0,
336 Enabled = 1,
337 ],
338 PBA_Support OFFSET(31) NUMBITS(1)[
339 Legacy = 0,
340 MSIX = 1,
341 ],
342 ],
343];
344
345#[derive(Clone, Copy)]
346pub struct Mac {
347 reg: NonNull<MacRegister>,
348}
349
350impl Mac {
351 pub fn new(iobase: NonNull<u8>) -> Self {
352 Self { reg: iobase.cast() }
353 }
354
355 pub fn iobase<T>(&self) -> NonNull<T> {
356 self.reg.cast()
357 }
358
359 pub fn write_mdic(&self, phys_addr: u32, offset: u32, data: u16) -> Result<(), DError> {
360 self.reg().mdic.write(
361 MDIC::REGADDR.val(offset)
362 + MDIC::PHY_ADDR.val(phys_addr)
363 + MDIC::DATA.val(data as _)
364 + MDIC::OP::Write,
365 );
366 mb();
367
368 loop {
369 let mdic = self.reg().mdic.extract();
370
371 if mdic.is_set(MDIC::READY) {
372 break;
373 }
374 if mdic.is_set(MDIC::E) {
375 error!("MDIC read error");
376 return Err(DError::Unknown("MDIC read error"));
377 }
378 }
379
380 Ok(())
381 }
382
383 pub fn read_mdic(&self, phys_addr: u32, offset: u32) -> Result<u16, DError> {
384 self.reg()
385 .mdic
386 .write(MDIC::REGADDR.val(offset) + MDIC::PHY_ADDR.val(phys_addr) + MDIC::OP::Read);
387 mb();
388 loop {
389 let mdic = self.reg().mdic.extract();
390 if mdic.is_set(MDIC::READY) {
391 return Ok(mdic.read(MDIC::DATA) as _);
392 }
393 if mdic.is_set(MDIC::E) {
394 error!("MDIC read error");
395 return Err(DError::Unknown("MDIC read error"));
396 }
397 }
398 }
399
400 pub fn disable_interrupts(&mut self) {
401 self.reg_mut().eimc.set(u32::MAX);
402 self.clear_interrupts();
403 }
404
405 pub fn enable_interrupts(&mut self) {
406 self.reg_mut().eims.set(u32::MAX);
407 }
408
409 pub fn enable_legacy_interrupts(&mut self) {
411 self.reg_mut().ims.write(
413 IMS::TXDW::SET
414 + IMS::TXQE::SET
415 + IMS::LSC::SET
416 + IMS::RXDMT0::SET
417 + IMS::RXO::SET
418 + IMS::RXT0::SET
419 + IMS::MDAC::SET,
420 );
421 }
422
423 pub fn disable_legacy_interrupts(&mut self) {
425 self.reg_mut().imc.write(
427 IMC::TXDW::SET
428 + IMC::TXQE::SET
429 + IMC::LSC::SET
430 + IMC::RXSEQ::SET
431 + IMC::RXDMT0::SET
432 + IMC::RXO::SET
433 + IMC::RXT0::SET
434 + IMC::MDAC::SET
435 + IMC::RXCFG::SET
436 + IMC::GPI_EN0::SET
437 + IMC::GPI_EN1::SET
438 + IMC::GPI_EN2::SET
439 + IMC::GPI_EN3::SET
440 + IMC::TXD_LOW::SET
441 + IMC::SRPD::SET
442 + IMC::ACK::SET
443 + IMC::MNG::SET
444 + IMC::DOCK::SET,
445 );
446 }
447
448 pub fn legacy_interrupts_ack(&mut self) -> LegacyIrqMsg {
450 let icr = self.reg().icr.get();
451 let ims = self.reg().ims.get();
452 let status = icr & ims;
453
454 LegacyIrqMsg {
455 txdw: status & ICR::TXDW.mask != 0,
456 txqe: status & ICR::TXQE.mask != 0,
457 lsc: status & ICR::LSC.mask != 0,
458 rxseq: status & ICR::RXSEQ.mask != 0,
459 rxdmt0: status & ICR::RXDMT0.mask != 0,
460 rxo: status & ICR::RXO.mask != 0,
461 rxt0: status & ICR::RXT0.mask != 0,
462 mdac: status & ICR::MDAC.mask != 0,
463 rxcfg: status & ICR::RXCFG.mask != 0,
464 asserted: status & ICR::INT_ASSERTED.mask != 0,
465 }
466 }
467
468 pub fn interrupts_ack(&mut self) -> IrqMsg {
469 let eicr = self.reg().eicr.get();
470 let eims = self.reg().eims.get();
471 let status = eicr & eims;
472 let tcp_timer = status & EICR::TCP_Timer.mask != 0;
473 let other = status & EICR::Other_Cause.mask != 0;
474 let queue_idx = (status & EICR::RxTxQ.mask) as u16;
475 IrqMsg {
476 queue_idx,
477 tcp_timer,
478 other,
479 }
480 }
481
482 pub fn link_mode(&self) -> Option<LinkMode> {
483 Some(
484 match self.reg().ctrl_ext.read_as_enum(CTRL_EXT::LINK_MODE) {
485 Some(CTRL_EXT::LINK_MODE::Value::DircetCooper) => LinkMode::DirectCooper,
486 Some(CTRL_EXT::LINK_MODE::Value::SGMII) => LinkMode::Sgmii,
487 Some(CTRL_EXT::LINK_MODE::Value::InternalSerdes) => LinkMode::InternalSerdes,
488 None => return None,
489 },
490 )
491 }
492
493 pub fn clear_interrupts(&mut self) {
495 self.reg_mut().eimc.set(u32::MAX);
497 self.reg().eicr.get();
498 }
499
500 pub fn reset(&mut self) -> Result<(), DError> {
501 self.reg_mut()
502 .ctrl
503 .modify(CTRL::RST::Reset + CTRL::PHY_RST::SET);
504 wait_for(
505 || self.reg().ctrl.matches_any(&[CTRL::RST::Normal]),
506 Duration::from_millis(1),
507 Some(1000),
508 )
509 }
510
511 pub fn set_link_up(&mut self) {
512 self.reg_mut().ctrl.modify(CTRL::SLU::SET + CTRL::FD::SET);
513 }
514
515 pub fn reg(&self) -> &MacRegister {
516 unsafe { self.reg.as_ref() }
517 }
518 pub fn reg_mut(&mut self) -> &mut MacRegister {
519 unsafe { self.reg.as_mut() }
520 }
521
522 pub fn read_mac(&self) -> [u8; 6] {
523 let low = self.ral(0);
524 let high = self.rah(0);
525
526 [
527 (low & 0xff) as u8,
528 ((low >> 8) & 0xff) as u8,
529 ((low >> 16) & 0xff) as u8,
530 (low >> 24) as u8,
531 (high & 0xff) as u8,
532 ((high >> 8) & 0xff) as u8,
533 ]
534 }
535
536 pub fn disable_rx(&mut self) {
537 self.reg_mut().rctl.modify(RCTL::RXEN::Disabled);
538 }
539
540 pub fn enable_rx(&mut self) {
541 self.reg_mut().rctl.modify(RCTL::RXEN::Enabled);
542 }
543
544 pub fn enable_tx(&mut self) {
545 self.reg_mut().tctl.modify(TCTL::EN::Enabled);
546 }
547
548 pub fn enable_loopback(&mut self) {
549 self.reg_mut().rctl.modify(RCTL::LBM::MacLoopback);
550 }
551
552 pub fn disable_loopback(&mut self) {
553 self.reg_mut().rctl.modify(RCTL::LBM::Normal);
554 }
555
556 pub fn configure_msix_mode(&mut self) {
558 self.reg_mut().gpie.write(
559 GPIE::Multiple_MSIX::MultipleVectors + GPIE::EIAME::Enabled + GPIE::PBA_Support::MSIX,
560 );
561 }
562
563 pub fn configure_legacy_mode(&mut self) {
565 self.reg_mut().gpie.write(
566 GPIE::Multiple_MSIX::SingleVector + GPIE::EIAME::Disabled + GPIE::PBA_Support::Legacy,
567 );
568 }
569
570 pub fn set_nsicr(&mut self, enable: bool) {
572 if enable {
573 self.reg_mut().gpie.modify(GPIE::NSICR::ClearOnRead);
574 } else {
575 self.reg_mut().gpie.modify(GPIE::NSICR::Normal);
576 }
577 }
578
579 pub fn set_ll_interval(&mut self, interval: u8) {
581 let val = (interval as u32) & 0x1F;
583 self.reg_mut().gpie.modify(GPIE::LL_Interval.val(val));
584 }
585
586 fn ral(&self, i: usize) -> u32 {
587 if i <= 15 {
588 self.reg().ralh_0_15[i * 2].get()
589 } else {
590 self.reg().ralh_16_23[i * 2].get()
591 }
592 }
593
594 fn rah(&self, i: usize) -> u32 {
595 if i <= 15 {
596 self.reg().ralh_0_15[i * 2 + 1].get()
597 } else {
598 self.reg().ralh_16_23[i * 2 + 1].get()
599 }
600 }
601
602 pub fn status(&self) -> MacStatus {
603 let status = self.reg().status.extract();
604 let speed = match status.read_as_enum(STATUS::SPEED) {
605 Some(STATUS::SPEED::Value::Speed1000) => Speed::Mb1000,
606 Some(STATUS::SPEED::Value::Speed100) => Speed::Mb100,
607 _ => Speed::Mb10,
608 };
609 let full_duplex = status.is_set(STATUS::FD);
610 let link_up = status.is_set(STATUS::LU);
611 let phy_reset_asserted = status.is_set(STATUS::PHYRA);
612
613 MacStatus {
614 full_duplex,
615 link_up,
616 speed,
617 phy_reset_asserted,
618 }
619 }
620}
621
622#[derive(Debug, Clone)]
623pub struct IrqMsg {
624 pub queue_idx: u16,
625 pub tcp_timer: bool,
626 pub other: bool,
627}
628
629#[derive(Debug, Clone)]
630pub struct LegacyIrqMsg {
631 pub txdw: bool, pub txqe: bool, pub lsc: bool, pub rxseq: bool, pub rxdmt0: bool, pub rxo: bool, pub rxt0: bool, pub mdac: bool, pub rxcfg: bool, pub asserted: bool, }
642
643#[derive(Clone, Copy, PartialEq, Eq)]
644#[repr(transparent)]
645pub struct MacAddr6([u8; 6]);
646
647impl MacAddr6 {
648 pub fn new(bytes: [u8; 6]) -> Self {
649 MacAddr6(bytes)
650 }
651
652 pub fn bytes(&self) -> [u8; 6] {
653 self.0
654 }
655}
656
657impl Debug for MacAddr6 {
658 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
659 write!(
660 f,
661 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
662 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
663 )
664 }
665}
666
667impl From<[u8; 6]> for MacAddr6 {
668 fn from(addr: [u8; 6]) -> Self {
669 MacAddr6(addr)
670 }
671}
672
673impl From<MacAddr6> for [u8; 6] {
674 fn from(addr: MacAddr6) -> Self {
675 addr.0
676 }
677}
678
679#[derive(Debug, Clone)]
680pub struct MacStatus {
681 pub full_duplex: bool,
682 pub link_up: bool,
683 pub speed: Speed,
684 pub phy_reset_asserted: bool,
685}
686
687#[derive(Debug, Clone, Copy, PartialEq, Eq)]
688pub enum LinkMode {
689 DirectCooper,
690 Sgmii,
691 InternalSerdes,
692}