diff mbox series

[net-next,v5,4/5] net: phy: mediatek: Extend 1G TX/RX link pulse time

Message ID 20240530034844.11176-5-SkyLake.Huang@mediatek.com (mailing list archive)
State New, archived
Headers show
Series net: phy: mediatek: Introduce mtk-phy-lib and add 2.5Gphy support | expand

Commit Message

SkyLake Huang (黃啟澤) May 30, 2024, 3:48 a.m. UTC
From: "SkyLake.Huang" <skylake.huang@mediatek.com>

We observe that some 10G devices' (mostly Marvell's chips inside) 1G
training time violates specification, which may last 2230ms and affect
later TX/RX link pulse time. This will invalidate MediaTek series
gigabit Ethernet PHYs' hardware auto downshift mechanism.

Without this patch, if someone is trying to use "4-wire" cable to
connect above devices, MediaTek' gigabit Ethernet PHYs may fail
to downshift to 100Mbps. (If partner 10G devices' downshift mechanism
stops at 1G)

This patch extends our 1G TX/RX link pulse time so that we can still
link up with those 10G devices.

Tested device:
- Netgear GS110EMX's 10G port (Marvell 88X3340P)
- QNAP QSW-M408-4C

v3:
Refactor mtk_gphy_cl22_read_status() with genphy_read_status().

v4:
1. Change extend_an_new_lp_cnt_limit()'s return type and all return values
2. Refactor comments in extend_an_new_lp_cnt_limit()

Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
---
 drivers/net/phy/mediatek/mtk-ge-soc.c  |  2 +
 drivers/net/phy/mediatek/mtk-ge.c      |  1 +
 drivers/net/phy/mediatek/mtk-phy-lib.c | 66 ++++++++++++++++++++++++++
 drivers/net/phy/mediatek/mtk.h         | 16 +++++++
 4 files changed, 85 insertions(+)

Comments

Russell King (Oracle) May 30, 2024, 10:23 a.m. UTC | #1
Hi,

A few suggestions:

On Thu, May 30, 2024 at 11:48:43AM +0800, Sky Huang wrote:
> +static int extend_an_new_lp_cnt_limit(struct phy_device *phydev)
> +{
> +	int mmd_read_ret;
> +	u32 reg_val;
> +	int timeout;
> +
> +	timeout = read_poll_timeout(mmd_read_ret = phy_read_mmd, reg_val,
> +				    (mmd_read_ret < 0) || reg_val & MTK_PHY_FINAL_SPEED_1000,
> +				    10000, 1000000, false, phydev,
> +				    MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC);

	timeout = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
					    MTK_PHY_LINK_STATUS_MISC,
					    reg_val,
					    reg_val & MTK_PHY_FINAL_SPEED_1000,
					    10000, 1000000, false);

> +	if (mmd_read_ret < 0)
> +		return mmd_read_ret;

So, what if the poll times out (timeout == -ETIMEDOUT) ? If you want to
ignore that, then:

	if (timeout < 0 && timeout != -ETIMEDOUT)
		return timeout;

> +int mtk_gphy_cl22_read_status(struct phy_device *phydev)
> +{
> +	int ret;
> +
> +	ret = genphy_read_status(phydev);
> +	if (ret)
> +		return ret;
> +
> +	if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) {
> +		ret = phy_read(phydev, MII_CTRL1000);
> +		if ((ret & ADVERTISE_1000FULL) || (ret & ADVERTISE_1000HALF)) {

This is equivalent to:

		if (ret & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) {

which is easier to read.

Thanks.
SkyLake Huang (黃啟澤) May 30, 2024, 4:01 p.m. UTC | #2
On Thu, 2024-05-30 at 11:23 +0100, Russell King (Oracle) wrote:
>  	 
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
>  Hi,
> 
> A few suggestions:
> 
> On Thu, May 30, 2024 at 11:48:43AM +0800, Sky Huang wrote:
> > +static int extend_an_new_lp_cnt_limit(struct phy_device *phydev)
> > +{
> > +int mmd_read_ret;
> > +u32 reg_val;
> > +int timeout;
> > +
> > +timeout = read_poll_timeout(mmd_read_ret = phy_read_mmd, reg_val,
> > +    (mmd_read_ret < 0) || reg_val & MTK_PHY_FINAL_SPEED_1000,
> > +    10000, 1000000, false, phydev,
> > +    MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC);
> 
> timeout = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
>     MTK_PHY_LINK_STATUS_MISC,
>     reg_val,
>     reg_val & MTK_PHY_FINAL_SPEED_1000,
>     10000, 1000000, false);
> 
> > +if (mmd_read_ret < 0)
> > +return mmd_read_ret;
> 
> So, what if the poll times out (timeout == -ETIMEDOUT) ? If you want
> to
> ignore that, then:
> 
> if (timeout < 0 && timeout != -ETIMEDOUT)
> return timeout;
> 
I'm not going to handle timeout case here. If we can't detect
MTK_PHY_FINAL_SPEED_1000 in 1 second, let it go and we'll detect it
next round.

> > +int mtk_gphy_cl22_read_status(struct phy_device *phydev)
> > +{
> > +int ret;
> > +
> > +ret = genphy_read_status(phydev);
> > +if (ret)
> > +return ret;
> > +
> > +if (phydev->autoneg == AUTONEG_ENABLE && !phydev-
> >autoneg_complete) {
> > +ret = phy_read(phydev, MII_CTRL1000);
> > +if ((ret & ADVERTISE_1000FULL) || (ret & ADVERTISE_1000HALF)) {
> 
> This is equivalent to:
> 
> if (ret & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) {
> 
> which is easier to read.
> 
> Thanks.
> 
> -- 
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

Agree. I'll modify this in next version.

Sky
Russell King (Oracle) May 30, 2024, 4:10 p.m. UTC | #3
On Thu, May 30, 2024 at 04:01:08PM +0000, SkyLake Huang (黃啟澤) wrote:
> I'm not going to handle timeout case here. If we can't detect
> MTK_PHY_FINAL_SPEED_1000 in 1 second, let it go and we'll detect it
> next round.

With this waiting up to one second for MTK_PHY_FINAL_SPEED_1000 to be
set...

> > > +int mtk_gphy_cl22_read_status(struct phy_device *phydev)
> > > +{
> > > +int ret;
> > > +
> > > +ret = genphy_read_status(phydev);
> > > +if (ret)
> > > +return ret;
> > > +
> > > +if (phydev->autoneg == AUTONEG_ENABLE && !phydev-
> > >autoneg_complete) {

Are you sure you want this condition like this? When the link is down,
and 1G speeds are being advertised, it means that you'll call
extend_an_new_lp_cnt_limit(). If MTK_PHY_FINAL_SPEED_1000 doesn't get
set, that'll take one second each and every time we poll the PHY for
its status - which will be done while holding phydev->lock.

This doesn't sound very good.
SkyLake Huang (黃啟澤) June 3, 2024, 3:15 a.m. UTC | #4
On Thu, 2024-05-30 at 17:10 +0100, Russell King (Oracle) wrote:
>  	 
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
>  On Thu, May 30, 2024 at 04:01:08PM +0000, SkyLake Huang (黃啟澤) wrote:
> > I'm not going to handle timeout case here. If we can't detect
> > MTK_PHY_FINAL_SPEED_1000 in 1 second, let it go and we'll detect it
> > next round.
> 
> With this waiting up to one second for MTK_PHY_FINAL_SPEED_1000 to be
> set...
> 
> > > > +int mtk_gphy_cl22_read_status(struct phy_device *phydev)
> > > > +{
> > > > +int ret;
> > > > +
> > > > +ret = genphy_read_status(phydev);
> > > > +if (ret)
> > > > +return ret;
> > > > +
> > > > +if (phydev->autoneg == AUTONEG_ENABLE && !phydev-
> > > >autoneg_complete) {
> 
> Are you sure you want this condition like this? When the link is
> down,
> and 1G speeds are being advertised, it means that you'll call
> extend_an_new_lp_cnt_limit(). If MTK_PHY_FINAL_SPEED_1000 doesn't get
> set, that'll take one second each and every time we poll the PHY for
> its status - which will be done while holding phydev->lock.
> 
> This doesn't sound very good.
> 
> -- 
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

I add another condition to make sure we enter
extend_an_new_lp_cnt_limit() only in first few seconds when we plug in
cable.

It will look like this:
===============================================================
#define MTK_PHY_AUX_CTRL_AND_STATUS		0x14
#define   MTK_PHY_LP_DETECTED_MASK		GENMASK(7, 6)

if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) {
	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_1);
	ret = __phy_read(phydev, MTK_PHY_AUX_CTRL_AND_STATUS);
	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);

/* Once LP_DETECTED is set, it means that"ability_match" in IEEE 802.3
 * Figure 28-18 is set. This happens after we plug in cable. Also,
LP_DETECTED
 * will be cleared after AN complete.
 */
	if (!FIELD_GET(MTK_PHY_LP_DETECTED_MASK, ret))
		return 0;

	ret = phy_read(phydev, MII_CTRL1000);
	if (ret & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) {
		ret = extend_an_new_lp_cnt_limit(phydev);
		if (ret < 0)
			return ret;
	}
}

return 0;
===============================================================
This is tested OK on my mt7988 platform.

Sky
Russell King (Oracle) June 3, 2024, 8:06 a.m. UTC | #5
On Mon, Jun 03, 2024 at 03:15:36AM +0000, SkyLake Huang (黃啟澤) wrote:
> On Thu, 2024-05-30 at 17:10 +0100, Russell King (Oracle) wrote:
> >  	 
> > External email : Please do not click links or open attachments until
> > you have verified the sender or the content.
> >  On Thu, May 30, 2024 at 04:01:08PM +0000, SkyLake Huang (黃啟澤) wrote:
> > > I'm not going to handle timeout case here. If we can't detect
> > > MTK_PHY_FINAL_SPEED_1000 in 1 second, let it go and we'll detect it
> > > next round.
> > 
> > With this waiting up to one second for MTK_PHY_FINAL_SPEED_1000 to be
> > set...
> > 
> > > > > +int mtk_gphy_cl22_read_status(struct phy_device *phydev)
> > > > > +{
> > > > > +int ret;
> > > > > +
> > > > > +ret = genphy_read_status(phydev);
> > > > > +if (ret)
> > > > > +return ret;
> > > > > +
> > > > > +if (phydev->autoneg == AUTONEG_ENABLE && !phydev-
> > > > >autoneg_complete) {
> > 
> > Are you sure you want this condition like this? When the link is
> > down,
> > and 1G speeds are being advertised, it means that you'll call
> > extend_an_new_lp_cnt_limit(). If MTK_PHY_FINAL_SPEED_1000 doesn't get
> > set, that'll take one second each and every time we poll the PHY for
> > its status - which will be done while holding phydev->lock.
> > 
> > This doesn't sound very good.
> > 
> > -- 
> > RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> > FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
> 
> I add another condition to make sure we enter
> extend_an_new_lp_cnt_limit() only in first few seconds when we plug in
> cable.
> 
> It will look like this:
> ===============================================================
> #define MTK_PHY_AUX_CTRL_AND_STATUS		0x14
> #define   MTK_PHY_LP_DETECTED_MASK		GENMASK(7, 6)
> 
> if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) {
> 	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_1);
> 	ret = __phy_read(phydev, MTK_PHY_AUX_CTRL_AND_STATUS);
> 	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);

We provide a helper for this:

	ret = phy_read_paged(phydev, MTK_PHY_PAGE_EXTENDED_1,
			     MTK_PHY_AUX_CTRL_AND_STATUS);

but please check "ret" for errors.
SkyLake Huang (黃啟澤) June 3, 2024, 11:51 a.m. UTC | #6
On Mon, 2024-06-03 at 09:06 +0100, Russell King (Oracle) wrote:
>  	 
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
>  On Mon, Jun 03, 2024 at 03:15:36AM +0000, SkyLake Huang (黃啟澤) wrote:
> > On Thu, 2024-05-30 at 17:10 +0100, Russell King (Oracle) wrote:
> > >   
> > > External email : Please do not click links or open attachments
> until
> > > you have verified the sender or the content.
> > >  On Thu, May 30, 2024 at 04:01:08PM +0000, SkyLake Huang (黃啟澤)
> wrote:
> > > > I'm not going to handle timeout case here. If we can't detect
> > > > MTK_PHY_FINAL_SPEED_1000 in 1 second, let it go and we'll
> detect it
> > > > next round.
> > > 
> > > With this waiting up to one second for MTK_PHY_FINAL_SPEED_1000
> to be
> > > set...
> > > 
> > > > > > +int mtk_gphy_cl22_read_status(struct phy_device *phydev)
> > > > > > +{
> > > > > > +int ret;
> > > > > > +
> > > > > > +ret = genphy_read_status(phydev);
> > > > > > +if (ret)
> > > > > > +return ret;
> > > > > > +
> > > > > > +if (phydev->autoneg == AUTONEG_ENABLE && !phydev-
> > > > > >autoneg_complete) {
> > > 
> > > Are you sure you want this condition like this? When the link is
> > > down,
> > > and 1G speeds are being advertised, it means that you'll call
> > > extend_an_new_lp_cnt_limit(). If MTK_PHY_FINAL_SPEED_1000 doesn't
> get
> > > set, that'll take one second each and every time we poll the PHY
> for
> > > its status - which will be done while holding phydev->lock.
> > > 
> > > This doesn't sound very good.
> > > 
> > > -- 
> > > RMK's Patch system: 
> https://www.armlinux.org.uk/developer/patches/
> > > FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
> > 
> > I add another condition to make sure we enter
> > extend_an_new_lp_cnt_limit() only in first few seconds when we plug
> in
> > cable.
> > 
> > It will look like this:
> > ===============================================================
> > #define MTK_PHY_AUX_CTRL_AND_STATUS0x14
> > #define   MTK_PHY_LP_DETECTED_MASKGENMASK(7, 6)
> > 
> > if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) 
> {
> > phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_1);
> > ret = __phy_read(phydev, MTK_PHY_AUX_CTRL_AND_STATUS);
> > phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
> 
> We provide a helper for this:
> 
> ret = phy_read_paged(phydev, MTK_PHY_PAGE_EXTENDED_1,
>      MTK_PHY_AUX_CTRL_AND_STATUS);
> 
> but please check "ret" for errors.
> 
> -- 
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

OK, I'll fix this in v6.

Sky
diff mbox series

Patch

diff --git a/drivers/net/phy/mediatek/mtk-ge-soc.c b/drivers/net/phy/mediatek/mtk-ge-soc.c
index c566bf9..d7b7fb2 100644
--- a/drivers/net/phy/mediatek/mtk-ge-soc.c
+++ b/drivers/net/phy/mediatek/mtk-ge-soc.c
@@ -1339,6 +1339,7 @@  static struct phy_driver mtk_socphy_driver[] = {
 		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981),
 		.name		= "MediaTek MT7981 PHY",
 		.config_init	= mt798x_phy_config_init,
+		.read_status	= mtk_gphy_cl22_read_status,
 		.config_intr	= genphy_no_config_intr,
 		.handle_interrupt = genphy_handle_interrupt_no_ack,
 		.probe		= mt7981_phy_probe,
@@ -1356,6 +1357,7 @@  static struct phy_driver mtk_socphy_driver[] = {
 		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988),
 		.name		= "MediaTek MT7988 PHY",
 		.config_init	= mt798x_phy_config_init,
+		.read_status	= mtk_gphy_cl22_read_status,
 		.config_intr	= genphy_no_config_intr,
 		.handle_interrupt = genphy_handle_interrupt_no_ack,
 		.probe		= mt7988_phy_probe,
diff --git a/drivers/net/phy/mediatek/mtk-ge.c b/drivers/net/phy/mediatek/mtk-ge.c
index 5c0226d..c832c90 100644
--- a/drivers/net/phy/mediatek/mtk-ge.c
+++ b/drivers/net/phy/mediatek/mtk-ge.c
@@ -211,6 +211,7 @@  static struct phy_driver mtk_gephy_driver[] = {
 		.name		= "MediaTek MT7531 PHY",
 		.probe		= mt7531_phy_probe,
 		.config_init	= mt7531_phy_config_init,
+		.read_status	= mtk_gphy_cl22_read_status,
 		/* Interrupts are handled by the switch, not the PHY
 		 * itself.
 		 */
diff --git a/drivers/net/phy/mediatek/mtk-phy-lib.c b/drivers/net/phy/mediatek/mtk-phy-lib.c
index 3f1a24c..41bd1b0 100644
--- a/drivers/net/phy/mediatek/mtk-phy-lib.c
+++ b/drivers/net/phy/mediatek/mtk-phy-lib.c
@@ -106,6 +106,72 @@  int mtk_phy_write_page(struct phy_device *phydev, int page)
 }
 EXPORT_SYMBOL_GPL(mtk_phy_write_page);
 
+static int extend_an_new_lp_cnt_limit(struct phy_device *phydev)
+{
+	int mmd_read_ret;
+	u32 reg_val;
+	int timeout;
+
+	timeout = read_poll_timeout(mmd_read_ret = phy_read_mmd, reg_val,
+				    (mmd_read_ret < 0) || reg_val & MTK_PHY_FINAL_SPEED_1000,
+				    10000, 1000000, false, phydev,
+				    MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC);
+	if (mmd_read_ret < 0)
+		return mmd_read_ret;
+
+	/* [When cable is plugged in]
+	 * We expect final_speed_1000 will be set, which means this phy starts 1G link-up
+	 * training. In this case, try to extend timeout period of auto downshift.
+	 * [When cable is unplugged]
+	 * We expect that reading MTK_PHY_FINAL_SPEED_1000 will time out. Do nothing but
+	 * return.
+	 */
+	if (!timeout) {
+		tr_modify(phydev, 0x0, 0xf, 0x3c, AN_NEW_LP_CNT_LIMIT_MASK,
+			  FIELD_PREP(AN_NEW_LP_CNT_LIMIT_MASK, 0xf));
+		mdelay(1500);
+
+		timeout = read_poll_timeout(tr_read, reg_val,
+					    (reg_val & AN_STATE_MASK) !=
+					    (AN_STATE_TX_DISABLE << AN_STATE_SHIFT),
+					    10000, 1000000, false, phydev,
+					    0x0, 0xf, 0x2);
+		if (!timeout) {
+			mdelay(625);
+			tr_modify(phydev, 0x0, 0xf, 0x3c, AN_NEW_LP_CNT_LIMIT_MASK,
+				  FIELD_PREP(AN_NEW_LP_CNT_LIMIT_MASK, 0x8));
+			mdelay(500);
+			tr_modify(phydev, 0x0, 0xf, 0x3c, AN_NEW_LP_CNT_LIMIT_MASK,
+				  FIELD_PREP(AN_NEW_LP_CNT_LIMIT_MASK, 0xf));
+		} else {
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+int mtk_gphy_cl22_read_status(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = genphy_read_status(phydev);
+	if (ret)
+		return ret;
+
+	if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) {
+		ret = phy_read(phydev, MII_CTRL1000);
+		if ((ret & ADVERTISE_1000FULL) || (ret & ADVERTISE_1000HALF)) {
+			ret = extend_an_new_lp_cnt_limit(phydev);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_gphy_cl22_read_status);
+
 int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, unsigned long rules,
 				unsigned long supported_triggers)
 {
diff --git a/drivers/net/phy/mediatek/mtk.h b/drivers/net/phy/mediatek/mtk.h
index 10ee9be..32fa3f1 100644
--- a/drivers/net/phy/mediatek/mtk.h
+++ b/drivers/net/phy/mediatek/mtk.h
@@ -12,6 +12,20 @@ 
 #define MTK_PHY_PAGE_STANDARD			0x0000
 #define MTK_PHY_PAGE_EXTENDED_52B5		0x52b5
 
+/* Registers on Token Ring debug nodes */
+/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x2 */
+#define   AN_STATE_MASK			GENMASK(22, 19)
+#define   AN_STATE_SHIFT		19
+#define   AN_STATE_TX_DISABLE		1
+
+/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */
+#define AN_NEW_LP_CNT_LIMIT_MASK		GENMASK(23, 20)
+#define AUTO_NP_10XEN				BIT(6)
+
+/* Registers on MDIO_MMD_VEND1 */
+#define MTK_PHY_LINK_STATUS_MISC	(0xa2)
+#define   MTK_PHY_FINAL_SPEED_1000	BIT(3)
+
 /* Registers on MDIO_MMD_VEND2 */
 #define MTK_PHY_LED0_ON_CTRL			0x24
 #define MTK_PHY_LED1_ON_CTRL			0x26
@@ -75,6 +89,8 @@  void __tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, u8 data_
 int mtk_phy_read_page(struct phy_device *phydev);
 int mtk_phy_write_page(struct phy_device *phydev, int page);
 
+int mtk_gphy_cl22_read_status(struct phy_device *phydev);
+
 int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, unsigned long rules,
 				unsigned long supported_triggers);
 int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index, unsigned long rules,