net: phy: dp83867: Add ability to disable output clock
authorGrygorii Strashko <grygorii.strashko@ti.com>
Mon, 18 Nov 2019 21:04:43 +0000 (23:04 +0200)
committerJoe Hershberger <joe.hershberger@ni.com>
Mon, 9 Dec 2019 15:47:42 +0000 (09:47 -0600)
Based on commit 13c83cf8af0d ("net: phy: dp83867: Add ability to disable
output clock") of mainline linux kernel.

Generally, the output clock pin is only used for testing and only serves as
a source of RF noise after this.  It could be used to daisy-chain PHYs, but
this is uncommon.  Since the PHY can disable the output, make doing so an
option.  I do this by adding another enumeration to the allowed values of
ti,clk-output-sel.

The code was not using the value DP83867_CLK_O_SEL_REF_CLK as one might
expect: to select the REF_CLK as the output.  Rather it meant "keep clock
output setting as is", which, depending on PHY strapping, might not be
outputting REF_CLK.

Change this so DP83867_CLK_O_SEL_REF_CLK means enable REF_CLK output.
Omitting the property will leave the setting as is (which was the previous
behavior in this case).

Out of range values were silently converted into DP83867_CLK_O_SEL_REF_CLK.
Change this so they generate an error.

Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
drivers/net/phy/dp83867.c

index 8dc2163..cd3c1c5 100644 (file)
@@ -83,6 +83,7 @@
 
 #define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX    0x0
 #define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN    0x1f
+#define DP83867_IO_MUX_CFG_CLK_O_DISABLE       BIT(6)
 #define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT     8
 #define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK      \
                GENMASK(0x1f, DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT)
@@ -103,6 +104,7 @@ struct dp83867_private {
        int io_impedance;
        bool rxctrl_strap_quirk;
        int port_mirroring;
+       bool set_clk_output;
        unsigned int clk_output_sel;
 };
 
@@ -134,16 +136,28 @@ static int dp83867_of_init(struct phy_device *phydev)
 {
        struct dp83867_private *dp83867 = phydev->priv;
        ofnode node;
-       u16 val;
+       int ret;
 
        node = phy_get_ofnode(phydev);
        if (!ofnode_valid(node))
                return -EINVAL;
 
-       /* Keep the default value if ti,clk-output-sel is not set */
-       dp83867->clk_output_sel =
-               ofnode_read_u32_default(node, "ti,clk-output-sel",
-                                       DP83867_CLK_O_SEL_REF_CLK);
+       /* Optional configuration */
+       ret = ofnode_read_u32(node, "ti,clk-output-sel",
+                             &dp83867->clk_output_sel);
+       /* If not set, keep default */
+       if (!ret) {
+               dp83867->set_clk_output = true;
+               /* Valid values are 0 to DP83867_CLK_O_SEL_REF_CLK or
+                * DP83867_CLK_O_SEL_OFF.
+                */
+               if (dp83867->clk_output_sel > DP83867_CLK_O_SEL_REF_CLK &&
+                   dp83867->clk_output_sel != DP83867_CLK_O_SEL_OFF) {
+                       pr_debug("ti,clk-output-sel value %u out of range\n",
+                                dp83867->clk_output_sel);
+                       return -EINVAL;
+               }
+       }
 
        if (ofnode_read_bool(node, "ti,max-output-impedance"))
                dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX;
@@ -170,18 +184,6 @@ static int dp83867_of_init(struct phy_device *phydev)
        if (ofnode_read_bool(node, "enet-phy-lane-no-swap"))
                dp83867->port_mirroring = DP83867_PORT_MIRRORING_DIS;
 
-
-       /* Clock output selection if muxing property is set */
-       if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) {
-               val = phy_read_mmd(phydev, DP83867_DEVADDR,
-                                  DP83867_IO_MUX_CFG);
-               val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK;
-               val |= (dp83867->clk_output_sel <<
-                       DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT);
-               phy_write_mmd(phydev, DP83867_DEVADDR,
-                             DP83867_IO_MUX_CFG, val);
-       }
-
        return 0;
 }
 #else
@@ -313,6 +315,23 @@ static int dp83867_config(struct phy_device *phydev)
        if (dp83867->port_mirroring != DP83867_PORT_MIRRORING_KEEP)
                dp83867_config_port_mirroring(phydev);
 
+       /* Clock output selection if muxing property is set */
+       if (dp83867->set_clk_output) {
+               val = phy_read_mmd(phydev, DP83867_DEVADDR,
+                                  DP83867_IO_MUX_CFG);
+
+               if (dp83867->clk_output_sel == DP83867_CLK_O_SEL_OFF) {
+                       val |= DP83867_IO_MUX_CFG_CLK_O_DISABLE;
+               } else {
+                       val &= ~(DP83867_IO_MUX_CFG_CLK_O_SEL_MASK |
+                                DP83867_IO_MUX_CFG_CLK_O_DISABLE);
+                       val |= dp83867->clk_output_sel <<
+                              DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT;
+               }
+               phy_write_mmd(phydev, DP83867_DEVADDR,
+                             DP83867_IO_MUX_CFG, val);
+       }
+
        genphy_config_aneg(phydev);
        return 0;