OSDN Git Service

net: phy: realtek: add dt property to disable CLKOUT clock
authorJoakim Zhang <qiangqing.zhang@nxp.com>
Tue, 8 Jun 2021 03:15:33 +0000 (11:15 +0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 8 Jun 2021 18:41:23 +0000 (11:41 -0700)
CLKOUT is enabled by default after PHY hardware reset, this patch adds
"realtek,clkout-disable" property for user to disable CLKOUT clock
to save PHY power.

Per RTL8211F guide, a PHY reset should be issued after setting these
bits in PHYCR2 register. After this patch, CLKOUT clock output to be
disabled.

Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/realtek.c

index 821e85a..ca258f2 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright (c) 2004 Freescale Semiconductor, Inc.
  */
 #include <linux/bitops.h>
+#include <linux/of.h>
 #include <linux/phy.h>
 #include <linux/module.h>
 #include <linux/delay.h>
@@ -27,6 +28,7 @@
 #define RTL821x_PAGE_SELECT                    0x1f
 
 #define RTL8211F_PHYCR1                                0x18
+#define RTL8211F_PHYCR2                                0x19
 #define RTL8211F_INSR                          0x1d
 
 #define RTL8211F_TX_DELAY                      BIT(8)
@@ -40,6 +42,8 @@
 #define RTL8211E_TX_DELAY                      BIT(12)
 #define RTL8211E_RX_DELAY                      BIT(11)
 
+#define RTL8211F_CLKOUT_EN                     BIT(0)
+
 #define RTL8201F_ISR                           0x1e
 #define RTL8201F_ISR_ANERR                     BIT(15)
 #define RTL8201F_ISR_DUPLEX                    BIT(13)
@@ -71,6 +75,10 @@ MODULE_DESCRIPTION("Realtek PHY driver");
 MODULE_AUTHOR("Johnson Leung");
 MODULE_LICENSE("GPL");
 
+struct rtl821x_priv {
+       u16 phycr2;
+};
+
 static int rtl821x_read_page(struct phy_device *phydev)
 {
        return __phy_read(phydev, RTL821x_PAGE_SELECT);
@@ -81,6 +89,28 @@ static int rtl821x_write_page(struct phy_device *phydev, int page)
        return __phy_write(phydev, RTL821x_PAGE_SELECT, page);
 }
 
+static int rtl821x_probe(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->mdio.dev;
+       struct rtl821x_priv *priv;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->phycr2 = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR2);
+       if (priv->phycr2 < 0)
+               return priv->phycr2;
+
+       priv->phycr2 &= RTL8211F_CLKOUT_EN;
+       if (of_property_read_bool(dev->of_node, "realtek,clkout-disable"))
+               priv->phycr2 &= ~RTL8211F_CLKOUT_EN;
+
+       phydev->priv = priv;
+
+       return 0;
+}
+
 static int rtl8201_ack_interrupt(struct phy_device *phydev)
 {
        int err;
@@ -291,6 +321,7 @@ static int rtl8211c_config_init(struct phy_device *phydev)
 
 static int rtl8211f_config_init(struct phy_device *phydev)
 {
+       struct rtl821x_priv *priv = phydev->priv;
        struct device *dev = &phydev->mdio.dev;
        u16 val_txdly, val_rxdly;
        u16 val;
@@ -354,7 +385,15 @@ static int rtl8211f_config_init(struct phy_device *phydev)
                        val_rxdly ? "enabled" : "disabled");
        }
 
-       return 0;
+       ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2,
+                              RTL8211F_CLKOUT_EN, priv->phycr2);
+       if (ret < 0) {
+               dev_err(dev, "clkout configuration failed: %pe\n",
+                       ERR_PTR(ret));
+               return ret;
+       }
+
+       return genphy_soft_reset(phydev);
 }
 
 static int rtl8211e_config_init(struct phy_device *phydev)
@@ -847,6 +886,7 @@ static struct phy_driver realtek_drvs[] = {
        }, {
                PHY_ID_MATCH_EXACT(0x001cc916),
                .name           = "RTL8211F Gigabit Ethernet",
+               .probe          = rtl821x_probe,
                .config_init    = &rtl8211f_config_init,
                .read_status    = rtlgen_read_status,
                .config_intr    = &rtl8211f_config_intr,