eth-phy-lan867x
#![no_std] MDIO driver for the Microchip LAN867x family of
10BASE-T1S Ethernet PHYs (IEEE 802.3cg-2019 Clause 147):
- LAN8670 — 32-VQFN, MII or RMII
- LAN8671 — 24-VQFN, RMII only
- LAN8672 — 36-VQFN, MII only
10BASE-T1S is single-pair, half-duplex, multidrop Ethernet — quite
different from the point-to-point 10/100BASE-T flavours covered by
eth-phy-lan87xx. If you're
plugging into a switch, you want lan87xx; if you're building a
sensor / actuator backbone with up to 8 nodes on a shared single
twisted pair, you want this crate.
Implements eth_mdio_phy::PhyDriver
so any MAC that exposes eth_mdio_phy::MdioBus can drive the chip.
On JetHome boards the MAC is the ESP32 built-in EMAC SMI controller
via esp_emac::mdio::EspMdio.
Installation
[]
= "0.2"
= "0.1"
| Feature | Default | Pulls in |
|---|---|---|
defmt |
off | defmt::Format derives via eth-mdio-phy/defmt |
MSRV: 1.75. Pure #![no_std], no allocations. Works on any target.
Pre-1.0 SemVer note. Cargo's caret on
^0.1will not pick up0.2.x, and vice versa — both digits behave as the major axis below 1.0. Bump explicitly when a new release lands. This crate's first release is0.1, but it depends oneth-mdio-phy 0.2.
Compatibility
| Crate | Version |
|---|---|
eth-mdio-phy |
0.2.x |
For ESP32: esp-emac |
0.2.x |
Quick start
CSMA/CD multidrop bus (no PLCA), PHY at MDIO addr 0:
use PhyLan867x;
use ;
# # #
With PLCA (recommended for > 2-node buses)
PLCA (IEEE 802.3 Clause 148) gives the bus collision-free TDMA-style
arbitration on top of CSMA/CD. One node is the coordinator
(node_id = 0); the rest are followers (1..=0xFE).
use ;
use ;
# # #
For a follower:
# use ;
# use MdioBus;
# # #
Boards with a PHY reset pin
If your board exposes a GPIO-driven PHY RESET_N line — and JetHome
JXD-CPU-E1T1S does, on ESP32 GPIO17 — use PhyLan867xWithReset<P>:
use ;
use ;
use PhyLan867xWithReset;
# # # where
# P: OutputPin,
# D: DelayNs,
# M: MdioBus,
#
#
hardware_reset drives RESET_N low for 10 ms then waits 25 ms
after release before MDIO is allowed — conservative timings that
also match PhyLan87xxWithReset.
What init does
- Soft reset via
BMCR.SW_RESET(self-clearing, bounded poll). - Reset-complete handshake: poll
STS2.RESETCin MMD-31. The chip holdsIRQ_Nlow after every reset until the host readsSTS2; reading it clearsRESETCand releases the line. Without this step, subsequent register writes may not take effect. - PHY identity check via
PHY_ID0/1. Mask0xFFFF_FFF0against0x0000_C560(Microchip OUI 00800Fh + MODEL010110b); silicon revision is intentionally allowed to vary. - Package discrimination from
STRAP_CTRL0.PKGTYP⇒Chip::Lan8670 / 8671 / 8672. Available viachip(). - MIDVER sanity probe: MMD-31
0xCA00must read0x0A10(OPEN Alliance register-map identifier, version 1.0). Confirms the MMD indirection is functional and the silicon implements the standard T1S register layout. - Multidrop enable: RMW
T1SPMACTL.MDE = 1in MMD-1. Required for any > 2-node bus; chip default is point-to-point.
What poll_link does
- PLCA off (CSMA/CD): no MDIO traffic. Returns
Some(LinkStatus { Mbps10, Half })— the bus is "always there". - PLCA on: reads MMD-31
PLCA_STS.PST. Returns linked when set (BEACONs are being TX'd as coordinator or RX'd as follower); returnsNonewhile the bus is still synchronising.
The branch is selected by the driver's internal flag, which
configure_plca sets and disable_plca / init clear. The driver
assumes a single-owner contract: it is the sole writer to the
PHY's registers. If a different host or task flips
PLCA_CTRL0.EN directly via MDIO between calls, this driver will
not notice — call init to resync.
What BMSR.LINK_STATUS does NOT do
It is hard-wired 1 on this chip. Calling
eth_mdio_phy::ieee802_3::is_link_up will always return true,
regardless of bus state. Don't. Use poll_link instead.
Troubleshooting
PhyError::ResetTimeout on init
Two possible sources:
BMCR.SW_RESETnever self-clears (~500-attempt window). Likely the MDIO bus is silently failing the write. VerifyMDIO/MDCwiring and pull-ups.STS2.RESETCnever asserts. The chip never finished its internal POR sequence — usually means the 50 MHzREFCLKIN(RMII) or 25 MHz crystal (MII) is not actually clocking. On JXD-CPU-E1T1S the LAN8671 has its own oscillator and exports the 50 MHz to ESP32 via GPIO0; if ESP32's EMAC is configured wrong (e.g. expecting internal APLL output instead of external clock-in) the PHY runs but the MAC can't talk to it.
PhyError::UnsupportedChip { id: 0 } on init
The PHY is on a different MDIO address than the one passed to
new(), OR the MDIO clock isn't running. LAN8671 latches its SMI
address from PHYAD[3:0] at hardware reset; the JetHome JXD-CPU-
E1T1S pulls all four pins low ⇒ addr = 0. Other boards will differ;
check the schematic and/or read STRAP_CTRL0.SMIADR.
PhyError::UnsupportedChip { id: 0xFFFF_FFFF } on init
MDIO bus reads are floating high — typical signs:
- No external pull-up on
MDIO(Microchip recommends 10 kΩ). - The PHY is held in
RESET_N. UsePhyLan867xWithResetand callhardware_resetfirst.
PLCA configured but poll_link always returns None
Most common cause: every node thinks it is the coordinator
(node_id = 0). Datasheet sec 4.9.2 covers the diagnostic flags
(STS1.UNEXPB is set on coordinator collisions). Other causes:
node_count< actual node count: some followers' TXOPs never come up, but the coordinator still BEACONs and PST will oscillate.node_id >= node_counton a follower: this is rejected byconfigure_plcawithPlcaError::InvalidConfig.
Need PLCA diagnostic counters
STS1 decoding, TOCNT / BCNCNT readers — deferred to v0.2.
For v0.1.x, read MMD-31 registers 0x18 / 0x24-0x27 directly
through your MdioBus if you need them in the meantime.
Hardware verified on
The driver compiles, runs unit tests against a MockMdio, and
matches the datasheet register-by-register against
DS60001573C (silicon revision 2 =
product revision B1). Hardware bring-up on JXD-R6-E1T1S is in
progress and will land in a follow-up release.
License
Licensed under either of:
- GNU General Public License, Version 2.0 or later
- Apache License, Version 2.0
at your option.