DMA_MODE.SWR resets the whole MAC core and only self-clears once all
of the MAC's clock domains are running.  On SoCs where the RGMII RX
clock is sourced by the PHY (e.g. Rockchip RK3568), the RX clock
domain stays dead until the PHY has been brought up: PHYs like the
Maxio MAE0621A need a vendor-specific init sequence (crystal clock
mode plus analog tuning) before they start outputting RXC at all.

eqos_start() asserted SWR before phy_connect()/phy_config(), so with
such a PHY the reset never completed, eqos_start() bailed out, and the
PHY was never initialized -- a structural deadlock (raising swr_wait
only delays the failure).

Move the SWR, together with the us_tic_counter programming which must
follow a successful reset, to after the PHY has been connected,
configured and its link is up, just before eqos_adjust_link().  This
mirrors Linux stmmac_open(), which runs stmmac_init_phy() before
stmmac_hw_setup() (containing the DMA reset).

The i.MX8M Plus GPR[1] interface-mode value latched by SWR is written
at probe time and is not touched by phy_connect()/phy_config(), so the
new order is safe there.

On a Rockchip RK3568 board with two MAE0621A PHYs the old order always
fails with "EQOS_DMA_MODE_SWR stuck" and no link; with this change the
interfaces come up and pass ping.  Moving the SWR block back to its
old position reproduces the failure.

Signed-off-by: kongchen <[email protected]>
---
 drivers/net/dwc_eth_qos.c | 51 +++++++++++++++++++++++----------------
 1 file changed, 30 insertions(+), 21 deletions(-)

diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index e7cf68d011c..f370593649c 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -702,21 +702,11 @@ static int eqos_start(struct udevice *dev)
        eqos->reg_access_ok = true;
 
        /*
-        * Assert the SWR first, the actually reset the MAC and to latch in
-        * e.g. i.MX8M Plus GPR[1] content, which selects interface mode.
+        * Bring the PHY up before triggering the DMA soft reset: on SoCs
+        * where the RGMII RX clock is sourced by the PHY (e.g. Rockchip,
+        * with PHYs that need a vendor init to start their clock, like the
+        * Maxio MAE0621A), DMA_MODE_SWR cannot complete until the PHY runs.
         */
-       setbits_le32(&eqos->dma_regs->mode, EQOS_DMA_MODE_SWR);
-
-       if (eqos->config->ops->eqos_fix_soc_reset)
-               eqos->config->ops->eqos_fix_soc_reset(dev);
-
-       ret = wait_for_bit_le32(&eqos->dma_regs->mode,
-                               EQOS_DMA_MODE_SWR, false,
-                               eqos->config->swr_wait, false);
-       if (ret) {
-               pr_err("EQOS_DMA_MODE_SWR stuck\n");
-               goto err_stop_resets;
-       }
 
        ret = eqos->config->ops->eqos_calibrate_pads(dev);
        if (ret < 0) {
@@ -724,13 +714,6 @@ static int eqos_start(struct udevice *dev)
                goto err_stop_resets;
        }
 
-       if (eqos->config->ops->eqos_get_tick_clk_rate) {
-               rate = eqos->config->ops->eqos_get_tick_clk_rate(dev);
-
-               val = (rate / 1000000) - 1;
-               writel(val, &eqos->mac_regs->us_tic_counter);
-       }
-
        /*
         * if PHY was already connected and configured,
         * don't need to reconnect/reconfigure again
@@ -786,6 +769,32 @@ static int eqos_start(struct udevice *dev)
                goto err_shutdown_phy;
        }
 
+       /*
+        * Assert the SWR first, the actually reset the MAC and to latch in
+        * e.g. i.MX8M Plus GPR[1] content, which selects interface mode.
+        * The PHY is running by now, so its RX clock keeps the MAC clock
+        * domains alive and the reset can complete.
+        */
+       setbits_le32(&eqos->dma_regs->mode, EQOS_DMA_MODE_SWR);
+
+       if (eqos->config->ops->eqos_fix_soc_reset)
+               eqos->config->ops->eqos_fix_soc_reset(dev);
+
+       ret = wait_for_bit_le32(&eqos->dma_regs->mode,
+                               EQOS_DMA_MODE_SWR, false,
+                               eqos->config->swr_wait, false);
+       if (ret) {
+               pr_err("EQOS_DMA_MODE_SWR stuck\n");
+               goto err_shutdown_phy;
+       }
+
+       if (eqos->config->ops->eqos_get_tick_clk_rate) {
+               rate = eqos->config->ops->eqos_get_tick_clk_rate(dev);
+
+               val = (rate / 1000000) - 1;
+               writel(val, &eqos->mac_regs->us_tic_counter);
+       }
+
        ret = eqos_adjust_link(dev);
        if (ret < 0) {
                pr_err("eqos_adjust_link() failed: %d\n", ret);
-- 
2.55.0

Reply via email to