Commit 84c5a3f0 authored by Marek Vasut's avatar Marek Vasut Committed by Jakub Kicinski
Browse files

net: phy: realtek: Add property to enable SSC



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: default avatarMarek Vasut <marek.vasut@mailbox.org>
Link: https://patch.msgid.link/20260405233008.148974-3-marek.vasut@mailbox.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent bfb859a5
Loading
Loading
Loading
Loading
+127 −0
Original line number Diff line number Diff line
@@ -82,10 +82,18 @@

#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",