[RFC PATCH net-next 0/9] net: survive a PHY whose firmware arrives after the MAC probes

From: Aleksei Sviridkin

Date: Sat Aug 29 2026 - 01:26:13 EST


The Airoha EN8811H answers its PHY ID from power-on, but it is an MD32
microcontroller until the host loads firmware into its volatile RAM,
and on systems that keep the firmware files in a filesystem those
files become readable long after the MDIO bus was scanned. Today the
DSA port that names such a PHY is dropped at switch probe and stays
dead for the whole uptime.

This follows the direction Andrew sketched in [1]: describe the chip
as an MDIO device that owns the download and the reset line, publish
the PHY on a child bus only once the firmware runs, and teach phylink
to wait for a PHY that is expected to probe late.

Patches 1-2 add the bindings. Patches 3-5 rework the download into a
library helper typed on a bus and address, shared per [2]: the PHY
driver keeps its behavior through wrappers, and the helper skips the
download when the MD32 already runs firmware - which is what lets the
MDIO device and the PHY driver coexist, whichever runs first. Patch 6
adds the MDIO device driver, patch 7 the pass-through bus, patches
8-9 the phylink half. Patches 8-9 alone carry a board whose chip
answers its ID before firmware: the MDIO layer buys the general case
(chips mute before firmware, quad-PHY packages, reset ownership),
not this board's necessity.

Tested on an MT7981B board (MT7531 switch, EN8811H on a 2500base-x
port), warm boots only - I have no remote way to cut power:

- download path: U-Boot leaves the MD32 in its bootloader on every
reboot here, so each boot exercises the MCU driver's pulse-reset
and download; ~144KB lands in about half a second and the version
register reads back
- adopt path: rebinding the MDIO device against a running MD32 takes
the no-reset branch and registers the bus in ~70ms
- the PHY driver, probing on the child bus right after, finds the
firmware running and skips its own download through the same check
- phylink attaches the PHY ~0.7s later with its interrupt from DT
(the poll-tick latency tax below), the port reaches forwarding,
and the interrupt line counts link events across forced
renegotiations
- an ifdown/ifup cycle disconnects and reconnects cleanly

The polling costs latency: the attach lands anywhere in
[0, poll interval) after the PHY becomes ready. Three consecutive
boots measured attach timestamps within 47us of each other, which is
the tick phase showing through; mean tax ~500ms, worst case a full
second. The exact event exists - BUS_NOTIFY_BOUND_DRIVER fires at
probe completion - but mdio_bus_type is internal to phylib, so an
event-driven follow-up means phylib owning the notifier behind a
small API. Polling first was the plan agreed in [1]. Is that API a
direction you want?

Two more questions. The property is "slow-to-probe" on the port node,
documented in ethernet-controller.yaml; better-scoped names welcome.
And the MCU driver cycles reset only when the MD32 sits in its
bootloader, since firmware lives in volatile RAM - so on a board
without a reset line, running older firmware is adopted as-is and a
newer file on disk takes effect only after a cold start. If that
trade reads wrong, the alternative is pulsing reset on every probe
and always downloading.

Known and left out: the retry never gives up, because the errno out
of a failed bringup cannot distinguish "still filling in link modes"
from "genuinely incompatible" - the backoff exists since a failed
bringup ends in phy_detach(), which pulses a PHY-node reset line.
Unbinding the MDIO device at runtime while the port is up removes
the child bus under an attached PHY and a later phy_stop() oopses;
that path predates this series (any mdio-mux unbind does the same)
and wants a phy-core fix rather than a workaround here.

This is based on net-next at 91ec20351349. It textually overlaps in
phylink_disconnect_phy() with the pending fix series [3]; a non-RFC
respin will rebase over whichever lands first.

[1] https://lore.kernel.org/netdev/a230d199-5d4d-4637-aff3-e725a37e1da1@xxxxxxx/
[2] https://lore.kernel.org/netdev/29f973e4-980d-4198-bbec-452f7421d416@xxxxxxx/
[3] https://lore.kernel.org/netdev/20260827211638.63395-1-f@xxxxxx/

Aleksei Sviridkin (9):
dt-bindings: net: add Airoha EN8811H PHY MCU
dt-bindings: net: ethernet-controller: add slow-to-probe
net: phy: air: type the buckpbus core on the bus and address
net: phy: air: move the EN8811H firmware download into the library
net: phy: air: skip the download when the MD32 is already running
net: mdio: add Airoha EN8811H MDIO device driver
net: mdio: en8811h: add the nested pass-through bus
net: phylink: wait for PHYs that are known to probe late
net: phylink: report no link modes while a late PHY is missing

.../bindings/net/airoha,en8811h-mcu.yaml | 83 ++++
.../bindings/net/ethernet-controller.yaml | 9 +
MAINTAINERS | 7 +
drivers/net/mdio/Kconfig | 11 +
drivers/net/mdio/Makefile | 1 +
drivers/net/mdio/mdio-airoha-en8811h.c | 300 ++++++++++++++
drivers/net/phy/air_en8811h.c | 148 +------
drivers/net/phy/air_phy_lib.c | 381 ++++++++++++++++--
drivers/net/phy/air_phy_lib.h | 27 ++
drivers/net/phy/phylink.c | 225 ++++++++++-
10 files changed, 1011 insertions(+), 181 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/airoha,en8811h-mcu.yaml
create mode 100644 drivers/net/mdio/mdio-airoha-en8811h.c

--
2.53.0