net: zynq_gem: Don't hardcode the MDC clock divisor
authorVenkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>
Mon, 19 Jun 2023 03:49:22 +0000 (09:19 +0530)
committerMichal Simek <michal.simek@amd.com>
Fri, 21 Jul 2023 07:00:38 +0000 (09:00 +0200)
As per spec MDC must not exceed 2.5MHz, read the pclk clock
from the device tree and update the MDC clock divisor.
GEM devices support larger clock divisors and have a different
range of divisors.  Program the MDIO clock divisors based on
the clock rate of the pclk clock.

Signed-off-by: Venkatesh Yadav Abbarapu <venkatesh.abbarapu@amd.com>
Link: https://lore.kernel.org/r/20230619034922.24019-1-venkatesh.abbarapu@amd.com
Signed-off-by: Michal Simek <michal.simek@amd.com>
drivers/net/zynq_gem.c

index 211b2c6..f3cdfb0 100644 (file)
@@ -30,6 +30,7 @@
 #include <asm/arch/hardware.h>
 #include <asm/arch/sys_proto.h>
 #include <dm/device_compat.h>
+#include <linux/bitfield.h>
 #include <linux/bitops.h>
 #include <linux/err.h>
 #include <linux/errno.h>
 #define ZYNQ_GEM_NWCFG_FSREM           0x00020000 /* FCS removal */
 #define ZYNQ_GEM_NWCFG_SGMII_ENBL      0x08000000 /* SGMII Enable */
 #define ZYNQ_GEM_NWCFG_PCS_SEL         0x00000800 /* PCS select */
-#ifdef CONFIG_ARM64
-#define ZYNQ_GEM_NWCFG_MDCCLKDIV       0x00100000 /* Div pclk by 64, max 160MHz */
-#else
-#define ZYNQ_GEM_NWCFG_MDCCLKDIV       0x000c0000 /* Div pclk by 48, max 120MHz */
-#endif
 
 #ifdef CONFIG_ARM64
 # define ZYNQ_GEM_DBUS_WIDTH   (1 << 21) /* 64 bit bus */
@@ -81,8 +77,7 @@
 
 #define ZYNQ_GEM_NWCFG_INIT            (ZYNQ_GEM_DBUS_WIDTH | \
                                        ZYNQ_GEM_NWCFG_FDEN | \
-                                       ZYNQ_GEM_NWCFG_FSREM | \
-                                       ZYNQ_GEM_NWCFG_MDCCLKDIV)
+                                       ZYNQ_GEM_NWCFG_FSREM)
 
 #define ZYNQ_GEM_NWSR_MDIOIDLE_MASK    0x00000004 /* PHY management idle */
 
 
 #define RXCLK_EN               BIT(0)
 
+/* GEM specific constants for CLK. */
+#define GEM_CLK_DIV8           0
+#define GEM_CLK_DIV16          1
+#define GEM_CLK_DIV32          2
+#define GEM_CLK_DIV48          3
+#define GEM_CLK_DIV64          4
+#define GEM_CLK_DIV96          5
+#define GEM_CLK_DIV128         6
+#define GEM_CLK_DIV224         7
+
+#define GEM_MDC_SET(val)       FIELD_PREP(GENMASK(20, 18), val)
+
 /* Device registers */
 struct zynq_gem_regs {
        u32 nwctrl; /* 0x0 - Network Control reg */
@@ -220,6 +227,7 @@ struct zynq_gem_priv {
        struct mii_dev *bus;
        struct clk rx_clk;
        struct clk tx_clk;
+       struct clk pclk;
        u32 max_speed;
        bool int_pcs;
        bool dma_64bit;
@@ -352,6 +360,32 @@ static int zynq_phy_init(struct udevice *dev)
        return phy_config(priv->phydev);
 }
 
+static u32 gem_mdc_clk_div(struct zynq_gem_priv *priv)
+{
+       u32 config;
+       unsigned long pclk_hz;
+
+       pclk_hz = clk_get_rate(&priv->pclk);
+       if (pclk_hz <= 20000000)
+               config = GEM_MDC_SET(GEM_CLK_DIV8);
+       else if (pclk_hz <= 40000000)
+               config = GEM_MDC_SET(GEM_CLK_DIV16);
+       else if (pclk_hz <= 80000000)
+               config = GEM_MDC_SET(GEM_CLK_DIV32);
+       else if (pclk_hz <= 120000000)
+               config = GEM_MDC_SET(GEM_CLK_DIV48);
+       else if (pclk_hz <= 160000000)
+               config = GEM_MDC_SET(GEM_CLK_DIV64);
+       else if (pclk_hz <= 240000000)
+               config = GEM_MDC_SET(GEM_CLK_DIV96);
+       else if (pclk_hz <= 320000000)
+               config = GEM_MDC_SET(GEM_CLK_DIV128);
+       else
+               config = GEM_MDC_SET(GEM_CLK_DIV224);
+
+       return config;
+}
+
 static int zynq_gem_init(struct udevice *dev)
 {
        u32 i, nwconfig;
@@ -460,7 +494,8 @@ static int zynq_gem_init(struct udevice *dev)
                return -1;
        }
 
-       nwconfig = ZYNQ_GEM_NWCFG_INIT;
+       nwconfig = gem_mdc_clk_div(priv);
+       nwconfig |= ZYNQ_GEM_NWCFG_INIT;
 
        /*
         * Set SGMII enable PCS selection only if internal PCS/PMA
@@ -828,6 +863,12 @@ static int zynq_gem_probe(struct udevice *dev)
                }
        }
 
+       ret = clk_get_by_name(dev, "pclk", &priv->pclk);
+       if (ret < 0) {
+               dev_err(dev, "failed to get pclk clock\n");
+               goto err2;
+       }
+
        if (IS_ENABLED(CONFIG_DM_ETH_PHY))
                priv->bus = eth_phy_get_mdio_bus(dev);