[PATCH net-next 1/1] net: mdio: reset PHY before attempting to access ID register

Buday Csaba
2025-11-25T11:16:02Z

When the ID of an Ethernet PHY is not provided by the 'compatible'
string in the device tree, its actual ID is read via the MDIO bus.
For some PHYs this could be unsafe, since a hard reset may be
necessary to safely access the MDIO registers.

Add a fallback mechanism for such devices: when reading the ID
fails, the reset will be asserted, and the ID read is retried.

This allows such devices to be used with an autodetected ID.

The fallback mechanism is activated in the error handling path, and
the return code of fwnode_mdiobus_register_phy() is unaltered, except
when the reset fails with -EPROBE_DEFER, which is propagated to the
caller.

Signed-off-by: Buday Csaba <buday.csaba@prolan.hu>
---
Patch split from a larger series:
https://lore.kernel.org/all/cover.1761732347.git.buday.csaba@prolan.hu/

The refactoring parts of the previous patchset were already merged,
leaving this one. Functionally identical to:
https://lore.kernel.org/all/5f8d93021a7aa6eeb4fb67ab27ddc7de9101c59f.1761732347.git.buday.csaba@prolan.hu/

Comments were added for clarity.
---
drivers/net/mdio/fwnode_mdio.c | 40 +++++++++++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c
index ba7091518..c1988cc37 100644
--- a/drivers/net/mdio/fwnode_mdio.c
+++ b/drivers/net/mdio/fwnode_mdio.c
@@ -12,6 +12,7 @@
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/pse-pd/pse.h>
+#include "../phy/mdio-private.h"

MODULE_AUTHOR("Calvin Johnson <calvin.johnson@oss.nxp.com>");
MODULE_LICENSE("GPL");
@@ -114,6 +115,34 @@ int fwnode_mdiobus_phy_device_register(struct mii_bus *mdio,
}
EXPORT_SYMBOL(fwnode_mdiobus_phy_device_register);

+/* Hard-reset a PHY before registration */
+static int fwnode_reset_phy(struct mii_bus *bus, u32 addr,
+ struct fwnode_handle *phy_node)
+{
+ struct mdio_device *tmpdev;
+ int rc;
+
+ /* Create a temporary MDIO device to allocate reset resources */
+ tmpdev = mdio_device_create(bus, addr);
+ if (IS_ERR(tmpdev))
+ return PTR_ERR(tmpdev);
+
+ device_set_node(&tmpdev->dev, fwnode_handle_get(phy_node));
+ rc = mdio_device_register_reset(tmpdev);
+ if (rc) {
+ mdio_device_free(tmpdev);
+ return rc;
+ }
+
+ mdio_device_reset(tmpdev, 1);
+ mdio_device_reset(tmpdev, 0);
+
+ mdio_device_unregister_reset(tmpdev);
+ mdio_device_free(tmpdev);
+
+ return 0;
+}
+
int fwnode_mdiobus_register_phy(struct mii_bus *bus,
struct fwnode_handle *child, u32 addr)
{
@@ -129,8 +158,17 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus,
return PTR_ERR(mii_ts);

is_c45 = fwnode_device_is_compatible(child, "ethernet-phy-ieee802.3-c45");
- if (is_c45 || fwnode_get_phy_id(child, &phy_id))
+ if (is_c45 || fwnode_get_phy_id(child, &phy_id)) {
phy = get_phy_device(bus, addr, is_c45);
+ if (IS_ERR(phy)) {
+ /* get_phy_device() failed, retry after a reset */
+ rc = fwnode_reset_phy(bus, addr, child);
+ if (rc == -EPROBE_DEFER)
+ goto clean_mii_ts;
+ else if (!rc)
+ phy = get_phy_device(bus, addr, is_c45);
+ }
+ }
else
phy = phy_device_create(bus, addr, phy_id, 0, NULL);
if (IS_ERR(phy)) {

base-commit: e2c20036a8879476c88002730d8a27f4e3c32d4b
--
2.39.5