From: Markus Stockhausen <markus.stockhausen@gmx.de>
To: andrew@lunn.ch, hkallweit1@gmail.com, linux@armlinux.org.uk,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, netdev@vger.kernel.org,
chris.packham@alliedtelesis.co.nz, daniel@makrotopia.org,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
devicetree@vger.kernel.org
Cc: Markus Stockhausen <markus.stockhausen@gmx.de>
Subject: [PATCH net-next v13 03/11] net: phy: add (*notify_phy_attach/detach)() hooks to struct mii_bus
Date: Fri, 7 Aug 2026 19:10:50 +0200 [thread overview]
Message-ID: <20260807171058.522833-4-markus.stockhausen@gmx.de> (raw)
In-Reply-To: <20260807171058.522833-1-markus.stockhausen@gmx.de>
From: Daniel Golle <daniel@makrotopia.org>
Some MDIO buses require programming PHY polling registers depending
on the PHY type. RealTek switch SoCs are the most prominent example
of a DSA switch which doesn't allow to program MAC speed, duplex and
flow-control settings without using PHY polling to do so [1].
Avoid a half-baked solution in the MDIO bus driver because
- it must reinvent the bus scanning to determine the PHYs and
- it must anticipate the right point in time (e.g. deferred PHYs).
Hence there is a need to inform the MDIO bus driver that a PHY is
being attached or detached. Provide two hooks in struct mii_bus
- notify_phy_attach(): called in phy_attach_direct() after PHY
hardware has been initialized and just before PHY is resumed.
- notify_phy_detach(): called in phy_detach() right after PHY
has been suspended.
Worth to notice: As of now phy_detach() is not 100% LIFO symmetric
to phy_attach_direct(). E.g. sysfs links are torn down before
suspend while being created before resume. Without reordering of the
detach function the above mentioned notifier placement is the best
possible symmetric implementation. For this
- Relocate code from phy_detach() into phy_detach_internal(). This
naming was selected to avoid confusion with "unlocked" helper that
usually start with two underscores.
- The helper takes an additional parameter notify_bus that decides
if the bus notification should be sent or not.
- Call the helper with notification from slimmed down version of
phy_detach() and without notification from phy_attach_direct()
error path.
- An unconditional notify_phy_detach() was favoured [3]
Remark! A slightly different version of this patch was part of a
former series [2]. The discussion already showed that an initialization
hook should be placed somewhere late during the whole setup. This
commit implants it right after phy_init_hw() as suggested. On top of
this it adds the detach hook.
[1] https://github.com/openwrt/openwrt/pull/21515#discussion_r2714069716
[2] https://lore.kernel.org/netdev/cover.1769053496.git.daniel@makrotopia.org/
[3] https://lore.kernel.org/netdev/9e40f50b-357a-4a93-9f59-94847850835d@lunn.ch/#t
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
drivers/net/phy/phy_device.c | 180 +++++++++++++++++++----------------
include/linux/phy.h | 18 ++++
2 files changed, 116 insertions(+), 82 deletions(-)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 94b2e85e00a3..1a43fec022aa 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1734,6 +1734,96 @@ static bool phy_drv_supports_irq(const struct phy_driver *phydrv)
return phydrv->config_intr && phydrv->handle_interrupt;
}
+static void phy_detach_internal(struct phy_device *phydev, bool notify_bus)
+{
+ struct net_device *dev = phydev->attached_dev;
+ struct module *ndev_owner = NULL;
+ struct mii_bus *bus;
+
+ if (phydev->devlink) {
+ device_link_del(phydev->devlink);
+ phydev->devlink = NULL;
+ }
+
+ if (phydev->sysfs_links) {
+ if (dev)
+ sysfs_remove_link(&dev->dev.kobj, "phydev");
+ sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
+ }
+
+ if (!phydev->attached_dev)
+ sysfs_remove_file(&phydev->mdio.dev.kobj,
+ &dev_attr_phy_standalone.attr);
+
+ phy_suspend(phydev);
+
+ if (notify_bus && phydev->mdio.bus->notify_phy_detach)
+ phydev->mdio.bus->notify_phy_detach(phydev);
+
+ if (dev) {
+ struct hwtstamp_provider *hwprov;
+
+ /* hwprov may technically be protected by ops lock but
+ * not for devices with a phydev, see phy_link_topo_add_phy()
+ */
+ hwprov = rtnl_dereference(dev->hwprov);
+ /* Disable timestamp if it is the one selected */
+ if (hwprov && hwprov->phydev == phydev) {
+ rcu_assign_pointer(dev->hwprov, NULL);
+ kfree_rcu(hwprov, rcu_head);
+ }
+
+ phydev->attached_dev->phydev = NULL;
+ phydev->attached_dev = NULL;
+ phy_link_topo_del_phy(dev, phydev);
+ }
+
+ phydev->phy_link_change = NULL;
+ phydev->phylink = NULL;
+
+ if (phydev->mdio.dev.driver)
+ module_put(phydev->mdio.dev.driver->owner);
+
+ /* If the device had no specific driver before (i.e. - it
+ * was using the generic driver), we unbind the device
+ * from the generic driver so that there's a chance a
+ * real driver could be loaded
+ */
+ if (phydev->is_genphy_driven) {
+ device_release_driver(&phydev->mdio.dev);
+ phydev->is_genphy_driven = 0;
+ }
+
+ /* Assert the reset signal */
+ phy_device_reset(phydev, 1);
+
+ /*
+ * The phydev might go away on the put_device() below, so avoid
+ * a use-after-free bug by reading the underlying bus first.
+ */
+ bus = phydev->mdio.bus;
+
+ put_device(&phydev->mdio.dev);
+ if (dev)
+ ndev_owner = dev->dev.parent->driver->owner;
+ if (ndev_owner != bus->owner)
+ module_put(bus->owner);
+}
+
+/**
+ * phy_detach - detach a PHY device from its network device
+ * @phydev: target phy_device struct
+ *
+ * This detaches the phy device from its network device and the phy
+ * driver, and drops the reference count taken in phy_attach_direct().
+ */
+void phy_detach(struct phy_device *phydev)
+{
+ /* cleanup including bus notification */
+ phy_detach_internal(phydev, true);
+}
+EXPORT_SYMBOL(phy_detach);
+
/**
* phy_attach_direct - attach a network device to a given PHY device pointer
* @dev: network device to attach
@@ -1876,6 +1966,12 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
if (err)
goto error;
+ if (phydev->mdio.bus->notify_phy_attach) {
+ err = phydev->mdio.bus->notify_phy_attach(phydev);
+ if (err)
+ goto error;
+ }
+
phy_resume(phydev);
/**
@@ -1890,8 +1986,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
return err;
error:
- /* phy_detach() does all of the cleanup below */
- phy_detach(phydev);
+ /* cleanup without bus notification */
+ phy_detach_internal(phydev, false);
return err;
error_module_put:
@@ -1906,86 +2002,6 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
}
EXPORT_SYMBOL(phy_attach_direct);
-/**
- * phy_detach - detach a PHY device from its network device
- * @phydev: target phy_device struct
- *
- * This detaches the phy device from its network device and the phy
- * driver, and drops the reference count taken in phy_attach_direct().
- */
-void phy_detach(struct phy_device *phydev)
-{
- struct net_device *dev = phydev->attached_dev;
- struct module *ndev_owner = NULL;
- struct mii_bus *bus;
-
- if (phydev->devlink) {
- device_link_del(phydev->devlink);
- phydev->devlink = NULL;
- }
-
- if (phydev->sysfs_links) {
- if (dev)
- sysfs_remove_link(&dev->dev.kobj, "phydev");
- sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
- }
-
- if (!phydev->attached_dev)
- sysfs_remove_file(&phydev->mdio.dev.kobj,
- &dev_attr_phy_standalone.attr);
-
- phy_suspend(phydev);
- if (dev) {
- struct hwtstamp_provider *hwprov;
-
- /* hwprov may technically be protected by ops lock but
- * not for devices with a phydev, see phy_link_topo_add_phy()
- */
- hwprov = rtnl_dereference(dev->hwprov);
- /* Disable timestamp if it is the one selected */
- if (hwprov && hwprov->phydev == phydev) {
- rcu_assign_pointer(dev->hwprov, NULL);
- kfree_rcu(hwprov, rcu_head);
- }
-
- phydev->attached_dev->phydev = NULL;
- phydev->attached_dev = NULL;
- phy_link_topo_del_phy(dev, phydev);
- }
-
- phydev->phy_link_change = NULL;
- phydev->phylink = NULL;
-
- if (phydev->mdio.dev.driver)
- module_put(phydev->mdio.dev.driver->owner);
-
- /* If the device had no specific driver before (i.e. - it
- * was using the generic driver), we unbind the device
- * from the generic driver so that there's a chance a
- * real driver could be loaded
- */
- if (phydev->is_genphy_driven) {
- device_release_driver(&phydev->mdio.dev);
- phydev->is_genphy_driven = 0;
- }
-
- /* Assert the reset signal */
- phy_device_reset(phydev, 1);
-
- /*
- * The phydev might go away on the put_device() below, so avoid
- * a use-after-free bug by reading the underlying bus first.
- */
- bus = phydev->mdio.bus;
-
- put_device(&phydev->mdio.dev);
- if (dev)
- ndev_owner = dev->dev.parent->driver->owner;
- if (ndev_owner != bus->owner)
- module_put(bus->owner);
-}
-EXPORT_SYMBOL(phy_detach);
-
int phy_suspend(struct phy_device *phydev)
{
struct net_device *netdev = phydev->attached_dev;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 5f8d65868e0f..3d8afe6b7f1c 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -376,6 +376,24 @@ struct mii_bus {
int regnum, u16 val);
/** @reset: Perform a reset of the bus */
int (*reset)(struct mii_bus *bus);
+ /**
+ * @notify_phy_attach: Perform post-attach handling for MDIO bus
+ * drivers. Optional and independent of @notify_phy_detach. Called
+ * in phy_attach_direct() right before phy_resume(). Runs in process
+ * context, may sleep and may be called with RTNL held. Must not
+ * acquire or rely on RTNL. Returns 0 on success or negative errno
+ * on failure. Must unwind its own state on error as attachment is
+ * aborted.
+ */
+ int (*notify_phy_attach)(struct phy_device *phydev);
+ /**
+ * @notify_phy_detach: Perform pre-detach handling for MDIO bus
+ * drivers. Optional and independent of @notify_phy_attach. Called
+ * in phy_detach() right after phy_suspend(). Runs in process context,
+ * may sleep and may be called with RTNL held. Must not acquire or
+ * rely on RTNL.
+ */
+ void (*notify_phy_detach)(struct phy_device *phydev);
/** @stats: Statistic counters per device on the bus */
struct mdio_bus_stats stats[PHY_MAX_ADDR];
--
2.55.0
next prev parent reply other threads:[~2026-08-07 17:11 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 17:10 [PATCH net-next v13 00/11] net: mdio: realtek-rtl9300: Add RTL83xx support Markus Stockhausen
2026-08-07 17:10 ` [PATCH net-next v13 01/11] dt-bindings: net: realtek,rtl9301-mdio: Add RTL83xx series Markus Stockhausen
2026-08-07 17:10 ` [PATCH net-next v13 02/11] net: mdio: realtek-rtl9300: Add polling documentation Markus Stockhausen
2026-08-07 17:10 ` Markus Stockhausen [this message]
2026-08-08 17:12 ` [PATCH net-next v13 03/11] net: phy: add (*notify_phy_attach/detach)() hooks to struct mii_bus sashiko-bot
2026-08-07 17:10 ` [PATCH net-next v13 04/11] net: mdio: realtek-rtl9300: suppress sysfs bind/unbind attributes Markus Stockhausen
2026-08-08 17:12 ` sashiko-bot
2026-08-07 17:10 ` [PATCH net-next v13 05/11] net: mdio: realtek-rtl9300: Configure hardware polling during probing Markus Stockhausen
2026-08-07 17:10 ` [PATCH net-next v13 06/11] net: mdio: realtek-rtl9300: Add page tracking Markus Stockhausen
2026-08-07 17:10 ` [PATCH net-next v13 07/11] net: mdio: realtek-rtl9300: Increase MDIO timeout Markus Stockhausen
2026-08-07 17:10 ` [PATCH net-next v13 08/11] net: mdio: realtek-rtl9300: Check for C45 support during setup Markus Stockhausen
2026-08-08 17:12 ` sashiko-bot
2026-08-07 17:10 ` [PATCH net-next v13 09/11] net: mdio: realtek-rtl9300: Add support for RTL838x Markus Stockhausen
2026-08-08 17:12 ` sashiko-bot
2026-08-07 17:10 ` [PATCH net-next v13 10/11] net: mdio: realtek-rtl9300: Add support for RTL839x Markus Stockhausen
2026-08-07 17:10 ` [PATCH net-next v13 11/11] net: mdio: realtek-rtl9300: reword Kconfig and module description Markus Stockhausen
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=20260807171058.522833-4-markus.stockhausen@gmx.de \
--to=markus.stockhausen@gmx.de \
--cc=andrew@lunn.ch \
--cc=chris.packham@alliedtelesis.co.nz \
--cc=conor+dt@kernel.org \
--cc=daniel@makrotopia.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@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