Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: phy: micrel: fix clk warning when removing the driver

Since the commit 25c6a5ab151f ("net: phy: micrel: Dynamically control
external clock of KSZ PHY"), the clock of Micrel PHY has been enabled
by phy_driver::resume() and disabled by phy_driver::suspend(). However,
devm_clk_get_optional_enabled() is used in kszphy_probe(), so the clock
will automatically be disabled when the device is unbound from the bus.
Therefore, this could cause the clock to be disabled twice, resulting
in clk driver warnings.

For example, this issue can be reproduced on i.MX6ULL platform, and we
can see the following logs when removing the FEC MAC drivers.

$ echo 2188000.ethernet > /sys/bus/platform/drivers/fec/unbind
$ echo 20b4000.ethernet > /sys/bus/platform/drivers/fec/unbind
[ 109.758207] ------------[ cut here ]------------
[ 109.758240] WARNING: drivers/clk/clk.c:1188 at clk_core_disable+0xb4/0xd0, CPU#0: sh/639
[ 109.771011] enet2_ref already disabled
[ 109.793359] Call trace:
[ 109.822006] clk_core_disable from clk_disable+0x28/0x34
[ 109.827340] clk_disable from clk_disable_unprepare+0xc/0x18
[ 109.833029] clk_disable_unprepare from devm_clk_release+0x1c/0x28
[ 109.839241] devm_clk_release from devres_release_all+0x98/0x100
[ 109.845278] devres_release_all from device_unbind_cleanup+0xc/0x70
[ 109.851571] device_unbind_cleanup from device_release_driver_internal+0x1a4/0x1f4
[ 109.859170] device_release_driver_internal from bus_remove_device+0xbc/0xe4
[ 109.866243] bus_remove_device from device_del+0x140/0x458
[ 109.871757] device_del from phy_mdio_device_remove+0xc/0x24
[ 109.877452] phy_mdio_device_remove from mdiobus_unregister+0x40/0xac
[ 109.883918] mdiobus_unregister from fec_enet_mii_remove+0x40/0x78
[ 109.890125] fec_enet_mii_remove from fec_drv_remove+0x4c/0x158
[ 109.896076] fec_drv_remove from device_release_driver_internal+0x17c/0x1f4
[ 109.962748] WARNING: drivers/clk/clk.c:1047 at clk_core_unprepare+0xfc/0x13c, CPU#0: sh/639
[ 109.975805] enet2_ref already unprepared
[ 110.002866] Call trace:
[ 110.031758] clk_core_unprepare from clk_unprepare+0x24/0x2c
[ 110.037440] clk_unprepare from devm_clk_release+0x1c/0x28
[ 110.042957] devm_clk_release from devres_release_all+0x98/0x100
[ 110.048989] devres_release_all from device_unbind_cleanup+0xc/0x70
[ 110.055280] device_unbind_cleanup from device_release_driver_internal+0x1a4/0x1f4
[ 110.062877] device_release_driver_internal from bus_remove_device+0xbc/0xe4
[ 110.069950] bus_remove_device from device_del+0x140/0x458
[ 110.075469] device_del from phy_mdio_device_remove+0xc/0x24
[ 110.081165] phy_mdio_device_remove from mdiobus_unregister+0x40/0xac
[ 110.087632] mdiobus_unregister from fec_enet_mii_remove+0x40/0x78
[ 110.093836] fec_enet_mii_remove from fec_drv_remove+0x4c/0x158
[ 110.099782] fec_drv_remove from device_release_driver_internal+0x17c/0x1f4

After analyzing the process of removing the FEC driver, as shown below,
it can be seen that the clock was disabled twice by the PHY driver.

fec_drv_remove()
--> fec_enet_close()
--> phy_stop()
--> phy_suspend()
--> kszphy_suspend() #1 The clock is disabled
--> fec_enet_mii_remove()
--> mdiobus_unregister()
--> phy_mdio_device_remove()
--> device_del()
--> devm_clk_release() #2 The clock is disabled again

Therefore, devm_clk_get_optional() is used to fix the above issue. And
to avoid the issue mentioned by the commit 985329462723 ("net: phy:
micrel: use devm_clk_get_optional_enabled for the rmii-ref clock"), the
clock is enabled by clk_prepare_enable() to get the correct clock rate.

Fixes: 25c6a5ab151f ("net: phy: micrel: Dynamically control external clock of KSZ PHY")
Signed-off-by: Wei Fang <wei.fang@nxp.com>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20260126081544.983517-1-wei.fang@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Wei Fang and committed by
Jakub Kicinski
2aa1545b a62f7d62

+13 -4
+13 -4
drivers/net/phy/micrel.c
··· 2643 2643 2644 2644 kszphy_parse_led_mode(phydev); 2645 2645 2646 - clk = devm_clk_get_optional_enabled(&phydev->mdio.dev, "rmii-ref"); 2646 + clk = devm_clk_get_optional(&phydev->mdio.dev, "rmii-ref"); 2647 2647 /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */ 2648 2648 if (!IS_ERR_OR_NULL(clk)) { 2649 - unsigned long rate = clk_get_rate(clk); 2650 2649 bool rmii_ref_clk_sel_25_mhz; 2650 + unsigned long rate; 2651 + int err; 2652 + 2653 + err = clk_prepare_enable(clk); 2654 + if (err) { 2655 + phydev_err(phydev, "Failed to enable rmii-ref clock\n"); 2656 + return err; 2657 + } 2658 + 2659 + rate = clk_get_rate(clk); 2660 + clk_disable_unprepare(clk); 2651 2661 2652 2662 if (type) 2653 2663 priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel; ··· 2675 2665 } 2676 2666 } else if (!clk) { 2677 2667 /* unnamed clock from the generic ethernet-phy binding */ 2678 - clk = devm_clk_get_optional_enabled(&phydev->mdio.dev, NULL); 2668 + clk = devm_clk_get_optional(&phydev->mdio.dev, NULL); 2679 2669 } 2680 2670 2681 2671 if (IS_ERR(clk)) 2682 2672 return PTR_ERR(clk); 2683 2673 2684 - clk_disable_unprepare(clk); 2685 2674 priv->clk = clk; 2686 2675 2687 2676 if (ksz8041_fiber_mode(phydev))