[RFC PATCH net-next 5/9] net: phy: air: skip the download when the MD32 is already running

From: Aleksei Sviridkin

Date: Sat Aug 29 2026 - 01:29:19 EST


The download is unconditional, so a chip whose firmware was loaded by
something else - a bootloader, an earlier bind of the PHY driver, or
an MDIO device serving the chip - is reprogrammed with what it is
already running, at 144KB per probe.

Read the status register the loader already polls for readiness and
skip the download when it reports ready, only picking up the running
firmware's version. The wait that follows is what makes this safe: a
chip that was not in fact running fails there instead of coming up
misprogrammed.

Living in the shared helper, the check covers every caller, and it is
what lets the PHY driver and the coming MDIO device driver coexist:
whichever runs second finds the firmware already up and leaves it
alone.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@xxxxxx>
---
drivers/net/phy/air_en8811h.c | 5 ++++-
drivers/net/phy/air_phy_lib.c | 28 ++++++++++++++++++++++++++++
drivers/net/phy/air_phy_lib.h | 1 +
3 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
index fdc64362a565..fcc6e373edd6 100644
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -1030,7 +1030,10 @@ static int en8811h_probe(struct phy_device *phydev)
if (ret < 0)
return ret;

- /* mcu has just restarted after firmware load */
+ /* Freshly downloaded firmware has just started; firmware adopted
+ * from the bootloader is already past its own start. Neither needs
+ * the restart a later resume would.
+ */
priv->mcu_needs_restart = false;

/* MDIO_DEVS1/2 empty, so set mmds_present bits here */
diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c
index 1ed5c69d7073..c1187f357f4c 100644
--- a/drivers/net/phy/air_phy_lib.c
+++ b/drivers/net/phy/air_phy_lib.c
@@ -374,6 +374,12 @@ static int air_mmd_status_read(struct mii_bus *bus, int addr, bool is_c45)
return ret;
}

+bool air_en8811h_mcu_running(struct mii_bus *bus, int addr, bool is_c45)
+{
+ return air_mmd_status_read(bus, addr, is_c45) == EN8811H_PHY_READY;
+}
+EXPORT_SYMBOL_GPL(air_en8811h_mcu_running);
+
int air_en8811h_wait_mcu_ready(struct mii_bus *bus, int addr, bool is_c45,
struct device *dev)
{
@@ -409,6 +415,28 @@ int air_en8811h_fw_download(struct mii_bus *bus, int addr, bool is_c45,
const struct firmware *fw1, *fw2;
int ret;

+ if (air_en8811h_mcu_running(bus, addr, is_c45)) {
+ /* Loaded by a bootloader, an earlier bind, or another
+ * device serving the chip. The wait below is what makes
+ * trusting the status register safe: a chip that was not
+ * in fact running fails there instead of coming up
+ * misprogrammed.
+ */
+ ret = air_en8811h_wait_mcu_ready(bus, addr, is_c45, dev);
+ if (ret < 0)
+ return ret;
+
+ ret = air_mdio_buckpbus_reg_read(bus, addr,
+ EN8811H_FW_VERSION,
+ fw_version);
+ if (ret < 0)
+ return ret;
+
+ dev_info(dev, "MD32 already running, firmware %08x\n",
+ *fw_version);
+ return 0;
+ }
+
ret = request_firmware_direct(&fw1, EN8811H_MD32_DM, dev);
if (ret < 0)
return ret;
diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h
index 6b11dbeaea9b..8d9f24da1271 100644
--- a/drivers/net/phy/air_phy_lib.h
+++ b/drivers/net/phy/air_phy_lib.h
@@ -59,6 +59,7 @@ struct firmware;

int air_fw_write_buf(struct mii_bus *bus, int addr, u32 address,
const struct firmware *fw);
+bool air_en8811h_mcu_running(struct mii_bus *bus, int addr, bool is_c45);
int air_en8811h_wait_mcu_ready(struct mii_bus *bus, int addr, bool is_c45,
struct device *dev);
int air_en8811h_fw_download(struct mii_bus *bus, int addr, bool is_c45,
--
2.53.0