kernel: backport RTL8211F clkout-disable patch
In Linux v5.14 an extra feature was introduced for the RTL8211F phy, allowing to disable a clock output from the phy. Part of that patch is to always (soft) reset the phy upon initialisation. This phy reset is required to have a working ethernet on the TP-Link EAP225-Outdoor v3 and EAP225 v4 after a reboot. Otherwise the ethernet port will only function properly on cold boots. Tested-by: Andre Klärner <kandre@ak-online.be> # EAP225-Outdoor v3 Tested-by: Sven Hauer <sven.hauer+github@uniku.de> # EAP225 v4 Signed-off-by: Sander Vanheule <sander@svanheule.net>
This commit is contained in:
		| @@ -0,0 +1,119 @@ | |||||||
|  | From 0a4355c2b7f8ecd5e61cc262ecdbd4a2cce1ea7e Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Joakim Zhang <qiangqing.zhang@nxp.com> | ||||||
|  | Date: Tue, 8 Jun 2021 11:15:33 +0800 | ||||||
|  | Subject: [PATCH] net: phy: realtek: add dt property to disable CLKOUT clock | ||||||
|  |  | ||||||
|  | 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 | 42 ++++++++++++++++++++++++++++++++++++++- | ||||||
|  |  1 file changed, 41 insertions(+), 1 deletion(-) | ||||||
|  |  | ||||||
|  | --- a/drivers/net/phy/realtek.c | ||||||
|  | +++ b/drivers/net/phy/realtek.c | ||||||
|  | @@ -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_IER				0x13 | ||||||
|  |   | ||||||
|  | @@ -62,6 +66,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); | ||||||
|  | @@ -72,6 +80,28 @@ static int rtl821x_write_page(struct phy | ||||||
|  |  	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; | ||||||
|  | @@ -180,6 +210,7 @@ static int rtl8211c_config_init(struct p | ||||||
|  |   | ||||||
|  |  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; | ||||||
|  | @@ -243,7 +274,15 @@ static int rtl8211f_config_init(struct p | ||||||
|  |  			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 rtl821x_resume(struct phy_device *phydev) | ||||||
|  | @@ -633,6 +672,7 @@ static struct phy_driver realtek_drvs[] | ||||||
|  |  	}, { | ||||||
|  |  		PHY_ID_MATCH_EXACT(0x001cc916), | ||||||
|  |  		.name		= "RTL8211F Gigabit Ethernet", | ||||||
|  | +		.probe		= rtl821x_probe, | ||||||
|  |  		.config_init	= &rtl8211f_config_init, | ||||||
|  |  		.ack_interrupt	= &rtl8211f_ack_interrupt, | ||||||
|  |  		.config_intr	= &rtl8211f_config_intr, | ||||||
		Reference in New Issue
	
	Block a user
	 Sander Vanheule
					Sander Vanheule