]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: phy: realtek: Add property to enable SSC
authorMarek Vasut <marek.vasut@mailbox.org>
Sun, 5 Apr 2026 23:29:58 +0000 (01:29 +0200)
committerJakub Kicinski <kuba@kernel.org>
Fri, 10 Apr 2026 03:01:52 +0000 (20:01 -0700)
Add support for spread spectrum clocking (SSC) on RTL8211F(D)(I)-CG,
RTL8211FS(I)(-VS)-CG, RTL8211FG(I)(-VS)-CG PHYs. The implementation
follows EMI improvement application note Rev. 1.2 for these PHYs.

The current implementation enables SSC for both RXC and SYSCLK clock
signals. Introduce DT properties 'realtek,clkout-ssc-enable',
'realtek,rxc-ssc-enable' and 'realtek,sysclk-ssc-enable' which control
CLKOUT, RXC and SYSCLK SSC spread spectrum clocking enablement on these
signals.

Signed-off-by: Marek Vasut <marek.vasut@mailbox.org>
Link: https://patch.msgid.link/20260405233008.148974-3-marek.vasut@mailbox.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/realtek/realtek_main.c

index c3604e1c45d9c1a3e2aa43485273ad5f3bbda985..bdb1221023fb6b1f5f268fc533c2a31c8728695c 100644 (file)
 
 #define RTL8211F_PHYCR2                                0x19
 #define RTL8211F_CLKOUT_EN                     BIT(0)
+#define RTL8211F_SYSCLK_SSC_EN                 BIT(3)
 #define RTL8211F_PHYCR2_PHY_EEE_ENABLE         BIT(5)
+#define RTL8211F_CLKOUT_SSC_EN                 BIT(7)
+#define RTL8211F_CLKOUT_SSC_CAP                        GENMASK(13, 12)
 
 #define RTL8211F_INSR                          0x1d
 
+/* RTL8211F SSC settings */
+#define RTL8211F_SSC_PAGE                      0xc44
+#define RTL8211F_SSC_RXC                       0x13
+#define RTL8211F_SSC_SYSCLK                    0x17
+
 /* RTL8211F LED configuration */
 #define RTL8211F_LEDCR_PAGE                    0xd04
 #define RTL8211F_LEDCR                         0x10
@@ -222,6 +230,9 @@ MODULE_LICENSE("GPL");
 struct rtl821x_priv {
        bool enable_aldps;
        bool disable_clk_out;
+       bool enable_clkout_ssc;
+       bool enable_rxc_ssc;
+       bool enable_sysclk_ssc;
        struct clk *clk;
        /* rtl8211f */
        u16 iner;
@@ -285,6 +296,12 @@ static int rtl821x_probe(struct phy_device *phydev)
                                                   "realtek,aldps-enable");
        priv->disable_clk_out = of_property_read_bool(dev->of_node,
                                                      "realtek,clkout-disable");
+       priv->enable_clkout_ssc = of_property_read_bool(dev->of_node,
+                                                       "realtek,clkout-ssc-enable");
+       priv->enable_rxc_ssc = of_property_read_bool(dev->of_node,
+                                                    "realtek,rxc-ssc-enable");
+       priv->enable_sysclk_ssc = of_property_read_bool(dev->of_node,
+                                                       "realtek,sysclk-ssc-enable");
 
        phydev->priv = priv;
 
@@ -716,6 +733,104 @@ static int rtl8211f_config_phy_eee(struct phy_device *phydev)
                          RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0);
 }
 
+static int rtl8211f_config_clkout_ssc(struct phy_device *phydev)
+{
+       struct rtl821x_priv *priv = phydev->priv;
+       struct device *dev = &phydev->mdio.dev;
+       int ret;
+
+       /* The value is preserved if the device tree property is absent */
+       if (!priv->enable_clkout_ssc)
+               return 0;
+
+       /* RTL8211FVD has PHYCR2 register, but configuration of CLKOUT SSC
+        * is not currently supported by this driver due to different bit
+        * layout.
+        */
+       if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
+               return 0;
+
+       /* Unnamed registers from EMI improvement parameters application note 1.2 */
+       ret = phy_write_paged(phydev, 0xd09, 0x10, 0xcf00);
+       if (ret < 0) {
+               dev_err(dev, "CLKOUT SSC initialization failed: %pe\n", ERR_PTR(ret));
+               return ret;
+       }
+
+       /* Enable CLKOUT SSC and CLKOUT SSC Capability using PHYCR2
+        * bits 7, 12, 13. This matches the register 25 write 0x38C3
+        * from the EMI improvement parameters application note 1.2
+        * section 2.3, without affecting unrelated bits.
+        */
+       ret = phy_set_bits(phydev, RTL8211F_PHYCR2,
+                          RTL8211F_CLKOUT_SSC_CAP | RTL8211F_CLKOUT_SSC_EN);
+       if (ret < 0) {
+               dev_err(dev, "CLKOUT SSC enable failed: %pe\n", ERR_PTR(ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rtl8211f_config_rxc_ssc(struct phy_device *phydev)
+{
+       struct rtl821x_priv *priv = phydev->priv;
+       struct device *dev = &phydev->mdio.dev;
+       int ret;
+
+       /* The value is preserved if the device tree property is absent */
+       if (!priv->enable_rxc_ssc)
+               return 0;
+
+       /* RTL8211FVD has PHYCR2 register, but configuration of RXC SSC
+        * is not currently supported by this driver due to different bit
+        * layout.
+        */
+       if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
+               return 0;
+
+       ret = phy_write_paged(phydev, RTL8211F_SSC_PAGE, RTL8211F_SSC_RXC, 0x5f00);
+       if (ret < 0) {
+               dev_err(dev, "RXC SSC configuration failed: %pe\n", ERR_PTR(ret));
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rtl8211f_config_sysclk_ssc(struct phy_device *phydev)
+{
+       struct rtl821x_priv *priv = phydev->priv;
+       struct device *dev = &phydev->mdio.dev;
+       int ret;
+
+       /* The value is preserved if the device tree property is absent */
+       if (!priv->enable_sysclk_ssc)
+               return 0;
+
+       /* RTL8211FVD has PHYCR2 register, but configuration of SYSCLK SSC
+        * is not currently supported by this driver due to different bit
+        * layout.
+        */
+       if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
+               return 0;
+
+       ret = phy_write_paged(phydev, RTL8211F_SSC_PAGE, RTL8211F_SSC_SYSCLK, 0x4f00);
+       if (ret < 0) {
+               dev_err(dev, "SYSCLK SSC configuration failed: %pe\n", ERR_PTR(ret));
+               return ret;
+       }
+
+       /* Enable SSC */
+       ret = phy_set_bits(phydev, RTL8211F_PHYCR2, RTL8211F_SYSCLK_SSC_EN);
+       if (ret < 0) {
+               dev_err(dev, "SYSCLK SSC enable failed: %pe\n", ERR_PTR(ret));
+               return ret;
+       }
+
+       return 0;
+}
+
 static int rtl8211f_config_init(struct phy_device *phydev)
 {
        struct device *dev = &phydev->mdio.dev;
@@ -732,6 +847,18 @@ static int rtl8211f_config_init(struct phy_device *phydev)
        if (ret)
                return ret;
 
+       ret = rtl8211f_config_rxc_ssc(phydev);
+       if (ret)
+               return ret;
+
+       ret = rtl8211f_config_sysclk_ssc(phydev);
+       if (ret)
+               return ret;
+
+       ret = rtl8211f_config_clkout_ssc(phydev);
+       if (ret)
+               return ret;
+
        ret = rtl8211f_config_clk_out(phydev);
        if (ret) {
                dev_err(dev, "clkout configuration failed: %pe\n",