1use core::hint::spin_loop;
8
9use crate::{regs, Duplex, Ethernet};
10
11#[cfg(feature = "phy-init-debug")]
20mod debug_uart {
21 use core::fmt;
22 use core::ptr::{read_volatile, write_volatile};
23
24 const UART0_BASE: usize = 0x500C_A000;
25 const UART_FIFO: *mut u32 = UART0_BASE as *mut u32;
26 const UART_STATUS: *const u32 = (UART0_BASE + 0x1C) as *const u32;
27 const UART_TXFIFO_CNT_SHIFT: u32 = 16;
28 const UART_TXFIFO_CNT_MASK: u32 = 0xFF << UART_TXFIFO_CNT_SHIFT;
29 const UART_TXFIFO_CAPACITY: u32 = 128;
30
31 pub struct Writer;
32
33 impl fmt::Write for Writer {
34 fn write_str(&mut self, s: &str) -> fmt::Result {
35 for byte in s.bytes() {
36 if byte == b'\n' {
37 write_byte(b'\r');
38 }
39 write_byte(byte);
40 }
41 Ok(())
42 }
43 }
44
45 fn write_byte(byte: u8) {
46 while txfifo_count() >= UART_TXFIFO_CAPACITY {
47 core::hint::spin_loop();
48 }
49 unsafe {
50 write_volatile(UART_FIFO, byte as u32);
51 }
52 }
53
54 fn txfifo_count() -> u32 {
55 let status = unsafe { read_volatile(UART_STATUS) };
56 (status & UART_TXFIFO_CNT_MASK) >> UART_TXFIFO_CNT_SHIFT
57 }
58}
59
60#[cfg(feature = "phy-init-debug")]
64pub fn diag_log(msg: &str) {
65 use core::fmt::Write;
66 let mut uart = debug_uart::Writer;
67 let _ = writeln!(uart, "{}", msg);
68}
69
70#[cfg(not(feature = "phy-init-debug"))]
72#[inline(always)]
73pub fn diag_log(_msg: &str) {}
74
75const MDIO_TIMEOUT_POLLS: usize = 100_000;
82const PHY_RESET_TIMEOUT_POLLS: usize = 500_000;
83
84pub const BMCR: u8 = 0x00;
86pub const BMSR: u8 = 0x01;
88pub const PHYIDR1: u8 = 0x02;
90pub const PHYIDR2: u8 = 0x03;
92pub const ANAR: u8 = 0x04;
94pub const ANLPAR: u8 = 0x05;
96
97pub mod bmcr {
99 pub const RESET: u16 = 1 << 15;
101 pub const SPEED_100: u16 = 1 << 13;
103 pub const ANEN: u16 = 1 << 12;
105 pub const RESTART_AN: u16 = 1 << 9;
107 pub const FULL_DUPLEX: u16 = 1 << 8;
109}
110
111pub mod bmsr {
113 pub const LINK_STATUS: u16 = 1 << 2;
115 pub const AN_COMPLETE: u16 = 1 << 5;
117}
118
119pub mod anlpar {
121 pub const BASE_10_HALF: u16 = 1 << 5;
123 pub const BASE_10_FULL: u16 = 1 << 6;
125 pub const BASE_100_HALF: u16 = 1 << 7;
127 pub const BASE_100_FULL: u16 = 1 << 8;
129}
130
131#[derive(Clone, Copy, Debug, Eq, PartialEq)]
133pub enum LinkState {
134 Down,
136 Up { speed: Speed, duplex: Duplex },
138}
139
140#[repr(u8)]
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
143pub enum Speed {
144 Mbps10,
146 Mbps100,
148}
149
150pub trait Phy {
152 fn init(&mut self, _eth: &mut Ethernet<'_>) -> Result<(), PhyError>;
154 fn poll_link(&mut self, _eth: &mut Ethernet<'_>) -> LinkState;
156}
157
158#[derive(Clone, Copy, Debug, Eq, PartialEq)]
160pub enum PhyError {
161 Timeout,
163 Bus,
165 MdioTimeout,
167}
168
169#[derive(Clone, Copy, Debug, Eq, PartialEq)]
171pub struct Ip101 {
172 pub addr: u8,
174}
175
176impl Ip101 {
177 pub const fn new(addr: u8) -> Self {
179 Self { addr }
180 }
181
182 pub fn init(&mut self, _eth: &mut Ethernet<'_>) -> Result<(), PhyError> {
184 #[cfg(feature = "phy-init-debug")]
185 {
186 use core::fmt::Write;
187 let mut uart = debug_uart::Writer;
188 let _ = writeln!(uart, "[phy_init] addr={} starting", self.addr);
189
190 for (label, reg) in [("PHYIDR1", PHYIDR1), ("PHYIDR2", PHYIDR2),
194 ("BMCR", BMCR), ("BMSR", BMSR)] {
195 match mdio_read(self.addr, reg) {
196 Ok(v) => {
197 let _ = writeln!(uart, "[phy_init] pre {}=0x{:04X}", label, v);
198 }
199 Err(e) => {
200 let _ = writeln!(uart, "[phy_init] pre {} ERR={:?}", label, e);
201 }
202 }
203 }
204 }
205
206 let write_result = mdio_write(self.addr, BMCR, bmcr::RESET);
207
208 #[cfg(feature = "phy-init-debug")]
209 {
210 use core::fmt::Write;
211 let mut uart = debug_uart::Writer;
212 match &write_result {
213 Ok(()) => {
214 let _ = writeln!(uart, "[phy_init] BMCR<-RESET write OK, polling clear...");
215 }
216 Err(e) => {
217 let _ = writeln!(uart, "[phy_init] BMCR<-RESET write ERR={:?}", e);
218 }
219 }
220 }
221 write_result?;
222
223 #[cfg(feature = "phy-init-debug")]
224 let mut last_bmcr: u16 = 0xFFFF;
225
226 for _i in 0..PHY_RESET_TIMEOUT_POLLS {
227 let bmcr_value = match mdio_read(self.addr, BMCR) {
228 Ok(v) => v,
229 Err(e) => {
230 #[cfg(feature = "phy-init-debug")]
231 {
232 use core::fmt::Write;
233 let mut uart = debug_uart::Writer;
234 let _ = writeln!(
235 uart,
236 "[phy_init] poll iter={} mdio_read ERR={:?}",
237 _i, e
238 );
239 }
240 return Err(e);
241 }
242 };
243
244 #[cfg(feature = "phy-init-debug")]
245 {
246 if bmcr_value != last_bmcr || _i == 0 || _i % 50_000 == 0 {
247 use core::fmt::Write;
248 let mut uart = debug_uart::Writer;
249 let _ = writeln!(
250 uart,
251 "[phy_init] iter={} BMCR=0x{:04X}",
252 _i, bmcr_value
253 );
254 last_bmcr = bmcr_value;
255 }
256 }
257
258 if bmcr_value & bmcr::RESET == 0 {
259 #[cfg(feature = "phy-init-debug")]
260 {
261 use core::fmt::Write;
262 let mut uart = debug_uart::Writer;
263 let _ = writeln!(
264 uart,
265 "[phy_init] reset cleared at iter={}, writing ANEN|RESTART_AN",
266 _i
267 );
268 }
269
270 mdio_write(self.addr, BMCR, bmcr::ANEN | bmcr::RESTART_AN)?;
271
272 #[cfg(feature = "phy-init-debug")]
273 {
274 use core::fmt::Write;
275 let mut uart = debug_uart::Writer;
276 let _ = writeln!(uart, "[phy_init] DONE OK");
277 }
278
279 return Ok(());
280 }
281
282 spin_loop();
283 }
284
285 #[cfg(feature = "phy-init-debug")]
286 {
287 use core::fmt::Write;
288 let mut uart = debug_uart::Writer;
289 let _ = writeln!(
290 uart,
291 "[phy_init] TIMEOUT after {} polls (RESET bit never cleared)",
292 PHY_RESET_TIMEOUT_POLLS
293 );
294 }
295
296 Err(PhyError::Timeout)
297 }
298
299 pub fn link_up(&mut self, _eth: &mut Ethernet<'_>) -> Result<bool, PhyError> {
301 let _ = mdio_read(self.addr, BMSR)?;
302 let status = mdio_read(self.addr, BMSR)?;
303 Ok(status & bmsr::LINK_STATUS != 0)
304 }
305
306 pub fn negotiate(&mut self, _eth: &mut Ethernet<'_>) -> Result<(Speed, Duplex), PhyError> {
308 let abilities = mdio_read(self.addr, ANLPAR)?;
309 Ok(resolve_link_from_anlpar(abilities))
310 }
311
312 fn autoneg_complete(&mut self) -> Result<bool, PhyError> {
313 let status = mdio_read(self.addr, BMSR)?;
314 Ok(status & bmsr::AN_COMPLETE != 0)
315 }
316}
317
318pub fn mdio_write(phy_addr: u8, reg: u8, value: u16) -> Result<(), PhyError> {
320 wait_for_mdio_idle(|| regs::read(regs::mac::MII_ADDR))?;
321
322 regs::write(regs::mac::MII_DATA, u32::from(value));
323 regs::write(
324 regs::mac::MII_ADDR,
325 mii_address_value(phy_addr, reg, current_csr_clock_range(), true),
326 );
327 #[cfg(test)]
328 complete_test_mdio_write(reg, value);
329
330 wait_for_mdio_idle(|| regs::read(regs::mac::MII_ADDR))
331}
332
333pub fn mdio_read(phy_addr: u8, reg: u8) -> Result<u16, PhyError> {
335 wait_for_mdio_idle(|| regs::read(regs::mac::MII_ADDR))?;
336
337 regs::write(
338 regs::mac::MII_ADDR,
339 mii_address_value(phy_addr, reg, current_csr_clock_range(), false),
340 );
341 #[cfg(test)]
342 complete_test_mdio_read(reg);
343
344 wait_for_mdio_idle(|| regs::read(regs::mac::MII_ADDR))?;
345 Ok(regs::read(regs::mac::MII_DATA) as u16)
346}
347
348fn current_csr_clock_range() -> u32 {
349 (regs::read(regs::mac::MII_ADDR) & regs::bits::miiaddr::CSR_CLOCK_RANGE_MASK)
350 >> regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT
351}
352
353fn mii_address_value(phy_addr: u8, reg: u8, csr_clock_range: u32, write: bool) -> u32 {
354 let mut value = regs::bits::miiaddr::MII_BUSY
355 | ((u32::from(phy_addr) & 0x1f) << regs::bits::miiaddr::PHY_ADDR_SHIFT)
356 | ((u32::from(reg) & 0x1f) << regs::bits::miiaddr::REG_ADDR_SHIFT)
357 | ((csr_clock_range << regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT)
358 & regs::bits::miiaddr::CSR_CLOCK_RANGE_MASK);
359
360 if write {
361 value |= regs::bits::miiaddr::MII_WRITE;
362 }
363
364 value
365}
366
367fn wait_for_mdio_idle<F>(mut read_miiaddr: F) -> Result<(), PhyError>
368where
369 F: FnMut() -> u32,
370{
371 for _ in 0..MDIO_TIMEOUT_POLLS {
372 if read_miiaddr() & regs::bits::miiaddr::MII_BUSY == 0 {
373 return Ok(());
374 }
375 }
376
377 Err(PhyError::MdioTimeout)
378}
379
380#[cfg(test)]
381fn complete_test_mdio_write(reg: u8, value: u16) {
382 let data = match reg {
383 BMCR if value & bmcr::RESET != 0 => 0,
384 _ => u32::from(value),
385 };
386 regs::write(regs::mac::MII_DATA, data);
387 regs::write(
388 regs::mac::MII_ADDR,
389 current_csr_clock_range() << regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT,
390 );
391}
392
393#[cfg(test)]
394fn complete_test_mdio_read(reg: u8) {
395 let value = match reg {
396 BMCR => 0,
397 BMSR => 0,
398 ANLPAR => 0,
399 PHYIDR1 => 0,
400 PHYIDR2 => 0,
401 _ => regs::read(regs::mac::MII_DATA),
402 };
403
404 regs::write(regs::mac::MII_DATA, value);
405 regs::write(
406 regs::mac::MII_ADDR,
407 current_csr_clock_range() << regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT,
408 );
409}
410
411impl Phy for Ip101 {
412 fn init(&mut self, eth: &mut Ethernet<'_>) -> Result<(), PhyError> {
413 Ip101::init(self, eth)
414 }
415
416 fn poll_link(&mut self, eth: &mut Ethernet<'_>) -> LinkState {
417 match (self.link_up(eth), self.autoneg_complete()) {
418 (Ok(true), Ok(true)) => match self.negotiate(eth) {
419 Ok((speed, duplex)) => LinkState::Up { speed, duplex },
420 Err(_) => LinkState::Down,
421 },
422 _ => LinkState::Down,
423 }
424 }
425}
426
427fn resolve_link_from_anlpar(abilities: u16) -> (Speed, Duplex) {
428 if abilities & anlpar::BASE_100_FULL != 0 {
429 return (Speed::Mbps100, Duplex::Full);
430 }
431
432 if abilities & anlpar::BASE_100_HALF != 0 {
433 return (Speed::Mbps100, Duplex::Half);
434 }
435
436 if abilities & anlpar::BASE_10_FULL != 0 {
437 return (Speed::Mbps10, Duplex::Full);
438 }
439
440 (Speed::Mbps10, Duplex::Half)
441}
442
443#[cfg(test)]
444mod tests {
445 use super::{
446 anlpar, bmcr, bmsr, mdio_read, mdio_write, mii_address_value, resolve_link_from_anlpar,
447 wait_for_mdio_idle, PhyError, ANAR, ANLPAR, BMCR, BMSR, PHYIDR1, PHYIDR2,
448 };
449 use crate::regs;
450
451 #[test]
452 fn phy_id_register_constants_match_standard_register_map() {
453 assert_eq!(BMCR, 0x00);
454 assert_eq!(BMSR, 0x01);
455 assert_eq!(PHYIDR1, 0x02);
456 assert_eq!(PHYIDR2, 0x03);
457 assert_eq!(ANAR, 0x04);
458 assert_eq!(ANLPAR, 0x05);
459 }
460
461 #[test]
462 fn phy_bit_constants_match_clause_22_layout() {
463 assert_eq!(bmcr::RESET, 1 << 15);
464 assert_eq!(bmcr::SPEED_100, 1 << 13);
465 assert_eq!(bmcr::ANEN, 1 << 12);
466 assert_eq!(bmcr::RESTART_AN, 1 << 9);
467 assert_eq!(bmcr::FULL_DUPLEX, 1 << 8);
468 assert_eq!(bmsr::LINK_STATUS, 1 << 2);
469 assert_eq!(bmsr::AN_COMPLETE, 1 << 5);
470 }
471
472 #[test]
473 fn mii_address_value_packs_write_transaction() {
474 let value = mii_address_value(0x1f, 0x12, 0b0001, true);
475
476 assert_ne!(value & regs::bits::miiaddr::MII_BUSY, 0);
477 assert_ne!(value & regs::bits::miiaddr::MII_WRITE, 0);
478 assert_eq!(
479 (value & regs::bits::miiaddr::PHY_ADDR_MASK) >> regs::bits::miiaddr::PHY_ADDR_SHIFT,
480 0x1f
481 );
482 assert_eq!(
483 (value & regs::bits::miiaddr::REG_ADDR_MASK) >> regs::bits::miiaddr::REG_ADDR_SHIFT,
484 0x12
485 );
486 assert_eq!(
487 (value & regs::bits::miiaddr::CSR_CLOCK_RANGE_MASK)
488 >> regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT,
489 0b0001
490 );
491 }
492
493 #[test]
494 fn mii_address_value_packs_read_transaction_without_write_bit() {
495 let value = mii_address_value(0x00, 0x03, 0b0100, false);
496
497 assert_ne!(value & regs::bits::miiaddr::MII_BUSY, 0);
498 assert_eq!(value & regs::bits::miiaddr::MII_WRITE, 0);
499 assert_eq!(
500 (value & regs::bits::miiaddr::REG_ADDR_MASK) >> regs::bits::miiaddr::REG_ADDR_SHIFT,
501 0x03
502 );
503 assert_eq!(
504 (value & regs::bits::miiaddr::CSR_CLOCK_RANGE_MASK)
505 >> regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT,
506 0b0100
507 );
508 }
509
510 #[test]
511 fn mii_address_value_masks_phy_register_and_clock_fields() {
512 let value = mii_address_value(0xff, 0xfe, 0xff, true);
513
514 assert_eq!(
515 (value & regs::bits::miiaddr::PHY_ADDR_MASK) >> regs::bits::miiaddr::PHY_ADDR_SHIFT,
516 0x1f
517 );
518 assert_eq!(
519 (value & regs::bits::miiaddr::REG_ADDR_MASK) >> regs::bits::miiaddr::REG_ADDR_SHIFT,
520 0x1e
521 );
522 assert_eq!(
523 (value & regs::bits::miiaddr::CSR_CLOCK_RANGE_MASK)
524 >> regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT,
525 0x0f
526 );
527 }
528
529 #[test]
530 fn wait_for_mdio_idle_returns_ok_after_busy_clears() {
531 let mut polls = 0usize;
532 let result = wait_for_mdio_idle(|| {
533 polls += 1;
534 if polls < 3 {
535 regs::bits::miiaddr::MII_BUSY
536 } else {
537 0
538 }
539 });
540
541 assert_eq!(result, Ok(()));
542 assert_eq!(polls, 3);
543 }
544
545 #[test]
546 fn wait_for_mdio_idle_times_out_when_busy_stays_set() {
547 let result = wait_for_mdio_idle(|| regs::bits::miiaddr::MII_BUSY);
548
549 assert_eq!(result, Err(PhyError::MdioTimeout));
550 }
551
552 #[test]
553 fn mdio_write_preserves_csr_clock_range_and_writes_data() {
554 regs::reset_test_registers();
555 regs::write(
556 regs::mac::MII_ADDR,
557 0b0101 << regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT,
558 );
559
560 mdio_write(0x03, 0x1f, 0xCAFE).unwrap();
561
562 assert_eq!(regs::read(regs::mac::MII_DATA), 0xCAFE);
563 assert_eq!(
564 regs::read(regs::mac::MII_ADDR) & regs::bits::miiaddr::CSR_CLOCK_RANGE_MASK,
565 0b0101 << regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT
566 );
567 }
568
569 #[test]
570 fn mdio_read_preserves_csr_clock_range_and_returns_data() {
571 regs::reset_test_registers();
572 regs::write(regs::mac::MII_DATA, 0xBEEF);
573 regs::write(
574 regs::mac::MII_ADDR,
575 0b0011 << regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT,
576 );
577
578 let value = mdio_read(0x03, 0x1f).unwrap();
579
580 assert_eq!(value, 0xBEEF);
581 assert_eq!(
582 regs::read(regs::mac::MII_ADDR) & regs::bits::miiaddr::CSR_CLOCK_RANGE_MASK,
583 0b0011 << regs::bits::miiaddr::CSR_CLOCK_RANGE_SHIFT
584 );
585 }
586
587 #[test]
588 fn mdio_transaction_returns_timeout_when_bus_is_busy() {
589 regs::reset_test_registers();
590 regs::write(regs::mac::MII_ADDR, regs::bits::miiaddr::MII_BUSY);
591
592 assert_eq!(mdio_read(0, BMSR), Err(PhyError::MdioTimeout));
593 assert_eq!(mdio_write(0, BMCR, 0), Err(PhyError::MdioTimeout));
594 }
595
596 #[test]
597 fn negotiation_prefers_highest_common_mode() {
598 assert_eq!(
599 resolve_link_from_anlpar(anlpar::BASE_100_FULL),
600 (super::Speed::Mbps100, super::Duplex::Full)
601 );
602 assert_eq!(
603 resolve_link_from_anlpar(anlpar::BASE_100_HALF),
604 (super::Speed::Mbps100, super::Duplex::Half)
605 );
606 assert_eq!(
607 resolve_link_from_anlpar(anlpar::BASE_10_FULL),
608 (super::Speed::Mbps10, super::Duplex::Full)
609 );
610 assert_eq!(
611 resolve_link_from_anlpar(0),
612 (super::Speed::Mbps10, super::Duplex::Half)
613 );
614 }
615
616 #[test]
617 fn negotiation_uses_ethernet_priority_order_for_combined_abilities() {
618 let all_modes = anlpar::BASE_10_HALF
619 | anlpar::BASE_10_FULL
620 | anlpar::BASE_100_HALF
621 | anlpar::BASE_100_FULL;
622
623 assert_eq!(
624 resolve_link_from_anlpar(all_modes),
625 (super::Speed::Mbps100, super::Duplex::Full)
626 );
627 assert_eq!(
628 resolve_link_from_anlpar(anlpar::BASE_10_FULL | anlpar::BASE_100_HALF),
629 (super::Speed::Mbps100, super::Duplex::Half)
630 );
631 }
632}