net: phy: mscc: make clock-output configurable on vsc85xx

The vsc8530/8531/8540/8541 phys have a configurable clock output that
can emit 25, 50 and 125 MHz rates, which in turn may be needed for
stable network connections.

This follows a similar change introduced into the Linux kernel at
  https://lore.kernel.org/netdev/20200609133140.1421109-2-heiko@sntech.de

Signed-off-by: Heiko Stuebner <heiko.stuebner@theobroma-systems.com>
Reviewed-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c
index 709979f..64e9093 100644
--- a/drivers/net/phy/mscc.c
+++ b/drivers/net/phy/mscc.c
@@ -157,6 +157,14 @@
 #define INT_MEM_DATA_M			GENMASK(7, 0)
 #define INT_MEM_DATA(x)			(INT_MEM_DATA_M & (x))
 
+/* Extended page GPIO register 13G */
+#define MSCC_CLKOUT_CNTL		13
+#define CLKOUT_ENABLE			BIT(15)
+#define CLKOUT_FREQ_MASK		GENMASK(14, 13)
+#define CLKOUT_FREQ_25M			(0x0 << 13)
+#define CLKOUT_FREQ_50M			(0x1 << 13)
+#define CLKOUT_FREQ_125M		(0x2 << 13)
+
 /* Extended page GPIO register 18G */
 #define MSCC_PHY_PROC_CMD		  18
 #define PROC_CMD_NCOMPLETED		  BIT(15)
@@ -1210,6 +1218,47 @@
 	return 0;
 }
 
+static int vsc8531_vsc8541_clkout_config(struct phy_device *phydev)
+{
+	struct ofnode_phandle_args phandle_args;
+	u32 clkout_rate = 0;
+	u16 reg_val;
+	int retval;
+
+	retval = dev_read_phandle_with_args(phydev->dev, "phy-handle", NULL,
+					    0, 0, &phandle_args);
+	if (!retval)
+		clkout_rate = ofnode_read_u32_default(phandle_args.node,
+						"vsc8531,clk-out-frequency", 0);
+
+	switch (clkout_rate) {
+	case 0:
+		reg_val = 0;
+		break;
+	case 25000000:
+		reg_val = CLKOUT_FREQ_25M | CLKOUT_ENABLE;
+		break;
+	case 50000000:
+		reg_val = CLKOUT_FREQ_50M | CLKOUT_ENABLE;
+		break;
+	case 125000000:
+		reg_val = CLKOUT_FREQ_125M | CLKOUT_ENABLE;
+		break;
+	default:
+		printf("PHY 8530/31 invalid clkout rate %u\n",
+		       clkout_rate);
+		return -EINVAL;
+	}
+
+	phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+		  MSCC_PHY_PAGE_GPIO);
+	phy_write(phydev, MDIO_DEVAD_NONE, MSCC_CLKOUT_CNTL, reg_val);
+	phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
+		  MSCC_PHY_PAGE_STD);
+
+	return 0;
+}
+
 static int vsc8531_config(struct phy_device *phydev)
 {
 	int  retval = -EINVAL;
@@ -1267,6 +1316,11 @@
 	phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
 		  MSCC_PHY_PAGE_STD);
 
+	/* Configure the clk output */
+	retval = vsc8531_vsc8541_clkout_config(phydev);
+	if (retval != 0)
+		return retval;
+
 	return genphy_config_aneg(phydev);
 }
 
@@ -1327,6 +1381,11 @@
 	phy_write(phydev, MDIO_DEVAD_NONE, MSCC_EXT_PAGE_ACCESS,
 		  MSCC_PHY_PAGE_STD);
 
+	/* Configure the clk output */
+	retval = vsc8531_vsc8541_clkout_config(phydev);
+	if (retval != 0)
+		return retval;
+
 	return genphy_config_aneg(phydev);
 }