net/fsl: xgmac_mdio: Support setting the MDC frequency
authorTobias Waldekranz <tobias@waldekranz.com>
Wed, 26 Jan 2022 16:05:42 +0000 (17:05 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 27 Jan 2022 13:41:29 +0000 (13:41 +0000)
Support the standard "clock-frequency" attribute to set the generated
MDC frequency. If not specified, the driver will leave the divisor
bits untouched.

Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/freescale/xgmac_mdio.c

index 18bf237..d38d0c3 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/acpi.h>
 #include <linux/acpi_mdio.h>
+#include <linux/clk.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/mdio.h>
@@ -36,7 +37,7 @@ struct tgec_mdio_controller {
 } __packed;
 
 #define MDIO_STAT_ENC          BIT(6)
-#define MDIO_STAT_CLKDIV(x)    (((x>>1) & 0xff) << 8)
+#define MDIO_STAT_CLKDIV(x)    (((x) & 0x1ff) << 7)
 #define MDIO_STAT_BSY          BIT(0)
 #define MDIO_STAT_RD_ER                BIT(1)
 #define MDIO_STAT_PRE_DIS      BIT(5)
@@ -51,6 +52,8 @@ struct tgec_mdio_controller {
 
 struct mdio_fsl_priv {
        struct  tgec_mdio_controller __iomem *mdio_base;
+       struct  clk *enet_clk;
+       u32     mdc_freq;
        bool    is_little_endian;
        bool    has_a009885;
        bool    has_a011043;
@@ -255,6 +258,35 @@ irq_restore:
        return ret;
 }
 
+static int xgmac_mdio_set_mdc_freq(struct mii_bus *bus)
+{
+       struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv;
+       struct tgec_mdio_controller __iomem *regs = priv->mdio_base;
+       struct device *dev = bus->parent;
+       u32 mdio_stat, div;
+
+       if (device_property_read_u32(dev, "clock-frequency", &priv->mdc_freq))
+               return 0;
+
+       priv->enet_clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(priv->enet_clk)) {
+               dev_err(dev, "Input clock unknown, not changing MDC frequency");
+               return PTR_ERR(priv->enet_clk);
+       }
+
+       div = ((clk_get_rate(priv->enet_clk) / priv->mdc_freq) - 1) / 2;
+       if (div < 5 || div > 0x1ff) {
+               dev_err(dev, "Requested MDC frequecy is out of range, ignoring");
+               return -EINVAL;
+       }
+
+       mdio_stat = xgmac_read32(&regs->mdio_stat, priv->is_little_endian);
+       mdio_stat &= ~MDIO_STAT_CLKDIV(0x1ff);
+       mdio_stat |= MDIO_STAT_CLKDIV(div);
+       xgmac_write32(mdio_stat, &regs->mdio_stat, priv->is_little_endian);
+       return 0;
+}
+
 static void xgmac_mdio_set_suppress_preamble(struct mii_bus *bus)
 {
        struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv;
@@ -319,6 +351,10 @@ static int xgmac_mdio_probe(struct platform_device *pdev)
 
        xgmac_mdio_set_suppress_preamble(bus);
 
+       ret = xgmac_mdio_set_mdc_freq(bus);
+       if (ret)
+               return ret;
+
        fwnode = pdev->dev.fwnode;
        if (is_of_node(fwnode))
                ret = of_mdiobus_register(bus, to_of_node(fwnode));