net: phy: mscc-miim: add support to set MDIO bus frequency
authorMichael Walle <michael@walle.cc>
Tue, 5 Apr 2022 12:09:51 +0000 (14:09 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 6 Apr 2022 13:04:16 +0000 (14:04 +0100)
Until now, the MDIO bus will have the hardware default bus frequency.
Read the desired frequency of the bus from the device tree and configure
it.

Signed-off-by: Michael Walle <michael@walle.cc>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/mdio/mdio-mscc-miim.c

index c483ba6..c3d1a7e 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <linux/bitops.h>
+#include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
@@ -30,6 +31,8 @@
 #define                MSCC_MIIM_CMD_VLD               BIT(31)
 #define MSCC_MIIM_REG_DATA             0xC
 #define                MSCC_MIIM_DATA_ERROR            (BIT(16) | BIT(17))
+#define MSCC_MIIM_REG_CFG              0x10
+#define                MSCC_MIIM_CFG_PRESCALE_MASK     GENMASK(7, 0)
 
 #define MSCC_PHY_REG_PHY_CFG   0x0
 #define                PHY_CFG_PHY_ENA         (BIT(0) | BIT(1) | BIT(2) | BIT(3))
@@ -50,6 +53,8 @@ struct mscc_miim_dev {
        int mii_status_offset;
        struct regmap *phy_regs;
        const struct mscc_miim_info *info;
+       struct clk *clk;
+       u32 bus_freq;
 };
 
 /* When high resolution timers aren't built-in: we can't use usleep_range() as
@@ -235,9 +240,32 @@ int mscc_miim_setup(struct device *dev, struct mii_bus **pbus, const char *name,
 }
 EXPORT_SYMBOL(mscc_miim_setup);
 
+static int mscc_miim_clk_set(struct mii_bus *bus)
+{
+       struct mscc_miim_dev *miim = bus->priv;
+       unsigned long rate;
+       u32 div;
+
+       /* Keep the current settings */
+       if (!miim->bus_freq)
+               return 0;
+
+       rate = clk_get_rate(miim->clk);
+
+       div = DIV_ROUND_UP(rate, 2 * miim->bus_freq) - 1;
+       if (div == 0 || div & ~MSCC_MIIM_CFG_PRESCALE_MASK) {
+               dev_err(&bus->dev, "Incorrect MDIO clock frequency\n");
+               return -EINVAL;
+       }
+
+       return regmap_update_bits(miim->regs, MSCC_MIIM_REG_CFG,
+                                 MSCC_MIIM_CFG_PRESCALE_MASK, div);
+}
+
 static int mscc_miim_probe(struct platform_device *pdev)
 {
        struct regmap *mii_regmap, *phy_regmap = NULL;
+       struct device_node *np = pdev->dev.of_node;
        void __iomem *regs, *phy_regs;
        struct mscc_miim_dev *miim;
        struct resource *res;
@@ -288,21 +316,47 @@ static int mscc_miim_probe(struct platform_device *pdev)
        if (!miim->info)
                return -EINVAL;
 
-       ret = of_mdiobus_register(bus, pdev->dev.of_node);
+       miim->clk = devm_clk_get_optional(&pdev->dev, NULL);
+       if (IS_ERR(miim->clk))
+               return PTR_ERR(miim->clk);
+
+       of_property_read_u32(np, "clock-frequency", &miim->bus_freq);
+
+       if (miim->bus_freq && !miim->clk) {
+               dev_err(&pdev->dev,
+                       "cannot use clock-frequency without a clock\n");
+               return -EINVAL;
+       }
+
+       ret = clk_prepare_enable(miim->clk);
+       if (ret)
+               return ret;
+
+       ret = mscc_miim_clk_set(bus);
+       if (ret)
+               goto out_disable_clk;
+
+       ret = of_mdiobus_register(bus, np);
        if (ret < 0) {
                dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
-               return ret;
+               goto out_disable_clk;
        }
 
        platform_set_drvdata(pdev, bus);
 
        return 0;
+
+out_disable_clk:
+       clk_disable_unprepare(miim->clk);
+       return ret;
 }
 
 static int mscc_miim_remove(struct platform_device *pdev)
 {
        struct mii_bus *bus = platform_get_drvdata(pdev);
+       struct mscc_miim_dev *miim = bus->priv;
 
+       clk_disable_unprepare(miim->clk);
        mdiobus_unregister(bus);
 
        return 0;