net: stmmac: dwmac-imx: pause the TXC clock in fixed-link
authorShenwei Wang <shenwei.wang@nxp.com>
Mon, 7 Aug 2023 16:07:16 +0000 (11:07 -0500)
committerJakub Kicinski <kuba@kernel.org>
Fri, 11 Aug 2023 00:26:06 +0000 (17:26 -0700)
When using a fixed-link setup, certain devices like the SJA1105 require a
small pause in the TXC clock line to enable their internal tunable
delay line (TDL).

To satisfy this requirement, this patch temporarily disables the TX clock,
and restarts it after a required period. This provides the required
silent interval on the clock line for SJA1105 to complete the frequency
transition and enable the internal TDLs. This action occurs before the link
is built up, so it does not impact a normal device too. There is no
need to identify if the connected device is an SJA1105 alike or not during
the implementation.

So far we have only enabled this feature on the i.MX93 platform.

Signed-off-by: Shenwei Wang <shenwei.wang@nxp.com>
Reviewed-by: Frank Li <frank.li@nxp.com>
Link: https://lore.kernel.org/r/20230807160716.259072-3-shenwei.wang@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c

index 7c4f3ad..535856f 100644 (file)
@@ -31,6 +31,7 @@
 #define GPR_ENET_QOS_RGMII_EN          (0x1 << 21)
 
 #define MX93_GPR_ENET_QOS_INTF_MODE_MASK       GENMASK(3, 0)
+#define MX93_GPR_ENET_QOS_INTF_MASK            GENMASK(3, 1)
 #define MX93_GPR_ENET_QOS_INTF_SEL_MII         (0x0 << 1)
 #define MX93_GPR_ENET_QOS_INTF_SEL_RMII                (0x4 << 1)
 #define MX93_GPR_ENET_QOS_INTF_SEL_RGMII       (0x1 << 1)
@@ -39,6 +40,7 @@
 #define DMA_BUS_MODE                   0x00001000
 #define DMA_BUS_MODE_SFT_RESET         (0x1 << 0)
 #define RMII_RESET_SPEED               (0x3 << 14)
+#define CTRL_SPEED_MASK                        GENMASK(15, 14)
 
 struct imx_dwmac_ops {
        u32 addr_width;
@@ -47,6 +49,7 @@ struct imx_dwmac_ops {
 
        int (*fix_soc_reset)(void *priv, void __iomem *ioaddr);
        int (*set_intf_mode)(struct plat_stmmacenet_data *plat_dat);
+       void (*fix_mac_speed)(void *priv, unsigned int speed, unsigned int mode);
 };
 
 struct imx_priv_data {
@@ -56,6 +59,7 @@ struct imx_priv_data {
        struct regmap *intf_regmap;
        u32 intf_reg_off;
        bool rmii_refclk_ext;
+       void __iomem *base_addr;
 
        const struct imx_dwmac_ops *ops;
        struct plat_stmmacenet_data *plat_dat;
@@ -212,6 +216,41 @@ static void imx_dwmac_fix_speed(void *priv, unsigned int speed, unsigned int mod
                dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate);
 }
 
+static void imx93_dwmac_fix_speed(void *priv, unsigned int speed, unsigned int mode)
+{
+       struct imx_priv_data *dwmac = priv;
+       unsigned int iface;
+       int ctrl, old_ctrl;
+
+       imx_dwmac_fix_speed(priv, speed, mode);
+
+       if (!dwmac || mode != MLO_AN_FIXED)
+               return;
+
+       if (regmap_read(dwmac->intf_regmap, dwmac->intf_reg_off, &iface))
+               return;
+
+       iface &= MX93_GPR_ENET_QOS_INTF_MASK;
+       if (iface != MX93_GPR_ENET_QOS_INTF_SEL_RGMII)
+               return;
+
+       old_ctrl = readl(dwmac->base_addr + MAC_CTRL_REG);
+       ctrl = old_ctrl & ~CTRL_SPEED_MASK;
+       regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
+                          MX93_GPR_ENET_QOS_INTF_MODE_MASK, 0);
+       writel(ctrl, dwmac->base_addr + MAC_CTRL_REG);
+
+        /* Ensure the settings for CTRL are applied. */
+       readl(dwmac->base_addr + MAC_CTRL_REG);
+
+       usleep_range(10, 20);
+       iface |= MX93_GPR_ENET_QOS_CLK_GEN_EN;
+       regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
+                          MX93_GPR_ENET_QOS_INTF_MODE_MASK, iface);
+
+       writel(old_ctrl, dwmac->base_addr + MAC_CTRL_REG);
+}
+
 static int imx_dwmac_mx93_reset(void *priv, void __iomem *ioaddr)
 {
        struct plat_stmmacenet_data *plat_dat = priv;
@@ -322,6 +361,7 @@ static int imx_dwmac_probe(struct platform_device *pdev)
        plat_dat->fix_mac_speed = imx_dwmac_fix_speed;
        plat_dat->bsp_priv = dwmac;
        dwmac->plat_dat = plat_dat;
+       dwmac->base_addr = stmmac_res.addr;
 
        ret = imx_dwmac_clks_config(dwmac, true);
        if (ret)
@@ -331,6 +371,8 @@ static int imx_dwmac_probe(struct platform_device *pdev)
        if (ret)
                goto err_dwmac_init;
 
+       if (dwmac->ops->fix_mac_speed)
+               plat_dat->fix_mac_speed = dwmac->ops->fix_mac_speed;
        dwmac->plat_dat->fix_soc_reset = dwmac->ops->fix_soc_reset;
 
        ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
@@ -368,6 +410,7 @@ static struct imx_dwmac_ops imx93_dwmac_data = {
        .mac_rgmii_txclk_auto_adj = true,
        .set_intf_mode = imx93_set_intf_mode,
        .fix_soc_reset = imx_dwmac_mx93_reset,
+       .fix_mac_speed = imx93_dwmac_fix_speed,
 };
 
 static const struct of_device_id imx_dwmac_match[] = {