The device tree example in 6.12 hard codes the Ethernet Phy rather than fixing this bug which did not exist in the NXP 5.15 kernel we were using.
We ended up writing a patch because we would like the flexibility of the kernel detecting the PHY which is broken in the fec module. This patch causes the phy registers to be read when the driver is loaded, rather than circumvention in device tree.
On i.MX6UL boards that hang both RMII PHYs on FEC2's MDIO bus, PHY@0
uses the FEC1 (ENET1) RMII reference clock. Kernel 6.12 routes that
clock through ENET1_REF_SEL, which may not be active when FEC2 reads
PHY@0's ID during of_mdiobus_register(), yielding a bogus ID (0x01080108)
and binding the generic PHY driver.
Briefly enable FEC1's enet_clk_ref (looked up from DT, not via probe
defer) around of_mdiobus_register() so the PHY ID read succeeds without
changing FEC probe order and breaking FEC_QUIRK_SINGLE_MDIO.
---
drivers/net/ethernet/freescale/fec_main.c | 59 +++++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 811a66062..46f37be0c 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -2540,6 +2540,39 @@ static int fec_enet_mii_probe(struct net_device *ndev)
return 0;
}
+static bool fec_enet_mdio_has_phy_addr0(struct device_node *mdio)
+{
+ struct device_node *child;
+ u32 reg;
+
+ for_each_available_child_of_node(mdio, child) {
+ if (of_property_read_u32(child, "reg", ®))
+ continue;
+ if (reg == 0) {
+ of_node_put(child);
+ return true;
+ }
+ }
+ return false;
+}
+
+static struct clk *fec_enet_get_rmii_master_refclk(void)
+{
+ struct device_node *np = NULL;
+ struct clk *clk;
+
+ while ((np = of_find_compatible_node(np, NULL, "fsl,imx6ul-fec"))) {
+ if (of_get_child_by_name(np, "mdio")) {
+ of_node_put(np);
+ continue;
+ }
+ clk = of_clk_get_by_name(np, "enet_clk_ref");
+ of_node_put(np);
+ return clk;
+ }
+ return NULL;
+}
+
static int fec_enet_mii_init(struct platform_device *pdev)
{
static struct mii_bus *fec0_mii_bus;
@@ -2553,6 +2586,8 @@ static int fec_enet_mii_init(struct platform_device *pdev)
u32 mii_speed, holdtime;
u32 bus_freq;
int addr;
+ struct clk *rmii_master_refclk = NULL;
+ bool peer_ref_enabled = false;
/*
* The i.MX28 dual fec interfaces are not equal.
@@ -2662,7 +2697,26 @@ static int fec_enet_mii_init(struct platform_device *pdev)
fep->mii_bus->priv = fep;
fep->mii_bus->parent = &pdev->dev;
+ if (node && fec_enet_mdio_has_phy_addr0(node)) {
+ rmii_master_refclk = fec_enet_get_rmii_master_refclk();
+ if (IS_ERR(rmii_master_refclk)) {
+ err = PTR_ERR(rmii_master_refclk);
+ goto err_out_free_mdiobus;
+ }
+ if (rmii_master_refclk) {
+ err = clk_prepare_enable(rmii_master_refclk);
+ if (err)
+ goto err_out_free_mdiobus;
+ peer_ref_enabled = true;
+ usleep_range(100, 200);
+ }
+ }
+
err = of_mdiobus_register(fep->mii_bus, node);
+ if (peer_ref_enabled)
+ clk_disable_unprepare(rmii_master_refclk);
+ if (!IS_ERR_OR_NULL(rmii_master_refclk))
+ clk_put(rmii_master_refclk);
if (err)
goto err_out_free_mdiobus;
of_node_put(node);Hello,
Thanks for sharing the analysis and patch.
Have you been able to validate this on an NXP EVK board?
Best regards.