From: Aleksei Sviridkin <f@lex.la>
To: netdev@vger.kernel.org
Cc: andrew@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, Aleksei Sviridkin <f@lex.la>
Subject: [RFC PATCH net-next 7/9] net: mdio: en8811h: add the nested pass-through bus
Date: Sat, 29 Aug 2026 05:25:44 +0000 [thread overview]
Message-ID: <20260829052546.1152446-8-f@lex.la> (raw)
In-Reply-To: <20260829052546.1152446-1-f@lex.la>
Registering the bus is what publishes the PHY, so it must happen only
once the MD32 is running its firmware. Put the PHY on a bus of its
own rather than on the parent so that the device tree can describe it
normally, interrupts included, and so that the MCU keeps ownership of the
reset line the PHY must not touch.
Only the address the MD32 answers on is passed through; every other
address returns -ENODEV, so scanning this bus cannot produce anything but
this chip's PHY. phy_mask would express the same thing but is not usable
here: of_mdiobus_register() overwrites it before walking the children.
Take the parent lock with MDIO_MUTEX_NESTED and use the __mdiobus
accessors, which is how the DSA drivers reach through a child bus into
their parent.
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
---
drivers/net/mdio/mdio-airoha-en8811h.c | 128 +++++++++++++++++++++++++
1 file changed, 128 insertions(+)
diff --git a/drivers/net/mdio/mdio-airoha-en8811h.c b/drivers/net/mdio/mdio-airoha-en8811h.c
index e16211da3d70..b28e116289e6 100644
--- a/drivers/net/mdio/mdio-airoha-en8811h.c
+++ b/drivers/net/mdio/mdio-airoha-en8811h.c
@@ -17,6 +17,8 @@
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
#include <linux/property.h>
#include <linux/workqueue.h>
@@ -47,6 +49,112 @@ struct en8811h_mcu {
bool warned;
};
+static int en8811h_mcu_read(struct mii_bus *bus, int addr, int regnum)
+{
+ struct en8811h_mcu *mcu = bus->priv;
+ struct mii_bus *parent = mcu->mdiodev->bus;
+ int ret;
+
+ if (addr != mcu->mdiodev->addr)
+ return -ENODEV;
+
+ mutex_lock_nested(&parent->mdio_lock, MDIO_MUTEX_NESTED);
+ ret = __mdiobus_read(parent, addr, regnum);
+ mutex_unlock(&parent->mdio_lock);
+
+ return ret;
+}
+
+static int en8811h_mcu_write(struct mii_bus *bus, int addr, int regnum, u16 val)
+{
+ struct en8811h_mcu *mcu = bus->priv;
+ struct mii_bus *parent = mcu->mdiodev->bus;
+ int ret;
+
+ if (addr != mcu->mdiodev->addr)
+ return -ENODEV;
+
+ mutex_lock_nested(&parent->mdio_lock, MDIO_MUTEX_NESTED);
+ ret = __mdiobus_write(parent, addr, regnum, val);
+ mutex_unlock(&parent->mdio_lock);
+
+ return ret;
+}
+
+static int en8811h_mcu_read_c45(struct mii_bus *bus, int addr, int devad,
+ int regnum)
+{
+ struct en8811h_mcu *mcu = bus->priv;
+ struct mii_bus *parent = mcu->mdiodev->bus;
+ int ret;
+
+ if (addr != mcu->mdiodev->addr)
+ return -ENODEV;
+
+ mutex_lock_nested(&parent->mdio_lock, MDIO_MUTEX_NESTED);
+ ret = __mdiobus_c45_read(parent, addr, devad, regnum);
+ mutex_unlock(&parent->mdio_lock);
+
+ return ret;
+}
+
+static int en8811h_mcu_write_c45(struct mii_bus *bus, int addr, int devad,
+ int regnum, u16 val)
+{
+ struct en8811h_mcu *mcu = bus->priv;
+ struct mii_bus *parent = mcu->mdiodev->bus;
+ int ret;
+
+ if (addr != mcu->mdiodev->addr)
+ return -ENODEV;
+
+ mutex_lock_nested(&parent->mdio_lock, MDIO_MUTEX_NESTED);
+ ret = __mdiobus_c45_write(parent, addr, devad, regnum, val);
+ mutex_unlock(&parent->mdio_lock);
+
+ return ret;
+}
+
+static int en8811h_mcu_bus_register(struct en8811h_mcu *mcu)
+{
+ struct device *dev = &mcu->mdiodev->dev;
+ struct mii_bus *parent = mcu->mdiodev->bus;
+ struct device_node *np;
+ struct mii_bus *bus;
+ int ret;
+
+ np = of_get_child_by_name(dev->of_node, "mdio");
+ if (!np) {
+ dev_err(dev, "no mdio node describing the PHY\n");
+ return -ENODEV;
+ }
+
+ bus = devm_mdiobus_alloc(dev);
+ if (!bus) {
+ of_node_put(np);
+ return -ENOMEM;
+ }
+
+ bus->name = "airoha-en8811h";
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+ bus->priv = mcu;
+ bus->parent = dev;
+
+ if (parent->read) {
+ bus->read = en8811h_mcu_read;
+ bus->write = en8811h_mcu_write;
+ }
+ if (parent->read_c45) {
+ bus->read_c45 = en8811h_mcu_read_c45;
+ bus->write_c45 = en8811h_mcu_write_c45;
+ }
+
+ ret = devm_of_mdiobus_register(dev, bus, np);
+ of_node_put(np);
+
+ return ret;
+}
+
static void en8811h_mcu_fw_poll(struct work_struct *work)
{
struct en8811h_mcu *mcu = container_of(to_delayed_work(work),
@@ -60,6 +168,15 @@ static void en8811h_mcu_fw_poll(struct work_struct *work)
if (!ret) {
dev_dbg(dev, "firmware %08x running after %ums\n",
mcu->fw_version, mcu->waited_ms);
+
+ /* Unlike a missing firmware file, this does not resolve by
+ * itself, so unlike the poll there is no retry: surface it
+ * once and stop.
+ */
+ ret = en8811h_mcu_bus_register(mcu);
+ if (ret)
+ dev_err(dev, "failed to register the PHY's bus: %pe\n",
+ ERR_PTR(ret));
return;
}
@@ -86,6 +203,7 @@ static int en8811h_mcu_probe(struct mdio_device *mdiodev)
{
struct device *dev = &mdiodev->dev;
struct en8811h_mcu *mcu;
+ struct device_node *np;
u32 deassert_us = 0;
mcu = devm_kzalloc(dev, sizeof(*mcu), GFP_KERNEL);
@@ -95,6 +213,16 @@ static int en8811h_mcu_probe(struct mdio_device *mdiodev)
mcu->mdiodev = mdiodev;
mdiodev_set_drvdata(mdiodev, mcu);
+ /* The bus registration only needs this once the firmware runs, but
+ * a DT hole should fail the bind now, not as a work-item error a
+ * second after probe already returned success.
+ */
+ np = of_get_child_by_name(dev->of_node, "mdio");
+ if (!np)
+ return dev_err_probe(dev, -ENODEV,
+ "no mdio node describing the PHY\n");
+ of_node_put(np);
+
/*
* The core only claims reset-gpios for devices flagged as PHYs
* (mdiobus_register_device()), so claim it here. Owning it at this
--
2.53.0
next prev parent reply other threads:[~2026-08-29 5:26 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-29 5:25 [RFC PATCH net-next 0/9] net: survive a PHY whose firmware arrives after the MAC probes Aleksei Sviridkin
2026-08-29 5:25 ` [RFC PATCH net-next 1/9] dt-bindings: net: add Airoha EN8811H PHY MCU Aleksei Sviridkin
2026-08-29 5:25 ` [RFC PATCH net-next 2/9] dt-bindings: net: ethernet-controller: add slow-to-probe Aleksei Sviridkin
2026-08-30 5:26 ` sashiko-bot
2026-09-04 0:29 ` Andrew Lunn
2026-08-29 5:25 ` [RFC PATCH net-next 3/9] net: phy: air: type the buckpbus core on the bus and address Aleksei Sviridkin
2026-09-04 0:48 ` Andrew Lunn
2026-08-29 5:25 ` [RFC PATCH net-next 4/9] net: phy: air: move the EN8811H firmware download into the library Aleksei Sviridkin
2026-09-04 1:16 ` Andrew Lunn
2026-08-29 5:25 ` [RFC PATCH net-next 5/9] net: phy: air: skip the download when the MD32 is already running Aleksei Sviridkin
2026-08-29 5:25 ` [RFC PATCH net-next 6/9] net: mdio: add Airoha EN8811H MDIO device driver Aleksei Sviridkin
2026-08-30 5:26 ` sashiko-bot
2026-09-04 1:36 ` Andrew Lunn
2026-08-29 5:25 ` Aleksei Sviridkin [this message]
2026-09-04 1:43 ` [RFC PATCH net-next 7/9] net: mdio: en8811h: add the nested pass-through bus Andrew Lunn
2026-08-29 5:25 ` [RFC PATCH net-next 8/9] net: phylink: wait for PHYs that are known to probe late Aleksei Sviridkin
2026-08-30 5:26 ` sashiko-bot
2026-08-29 5:25 ` [RFC PATCH net-next 9/9] net: phylink: report no link modes while a late PHY is missing Aleksei Sviridkin
2026-08-30 5:26 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260829052546.1152446-8-f@lex.la \
--to=f@lex.la \
--cc=andrew@lunn.ch \
--cc=conor+dt@kernel.org \
--cc=davem@davemloft.net \
--cc=devicetree@vger.kernel.org \
--cc=edumazet@google.com \
--cc=hkallweit1@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox