netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] [88q2xxx] Add support for handling master/slave in forced mode
@ 2025-08-19 21:29 Ilya A. Evenbach
  2025-08-19 22:13 ` Andrew Lunn
  0 siblings, 1 reply; 7+ messages in thread
From: Ilya A. Evenbach @ 2025-08-19 21:29 UTC (permalink / raw)
  To: netdev; +Cc: Ilya A. Evenbach

88q2xxx PHYs have non-standard way of setting master/slave in
forced mode.
This change adds support for changing and reporting this setting
correctly through ethtool.

Signed-off-by: Ilya A. Evenbach <ievenbach@aurora.tech>
---
 drivers/net/phy/marvell-88q2xxx.c | 107 ++++++++++++++++++++++++++++--
 1 file changed, 102 insertions(+), 5 deletions(-)

diff --git a/drivers/net/phy/marvell-88q2xxx.c b/drivers/net/phy/marvell-88q2xxx.c
index f3d83b04c953..1ab450056e86 100644
--- a/drivers/net/phy/marvell-88q2xxx.c
+++ b/drivers/net/phy/marvell-88q2xxx.c
@@ -9,6 +9,7 @@
 #include <linux/ethtool_netlink.h>
 #include <linux/hwmon.h>
 #include <linux/marvell_phy.h>
+#include <linux/mdio.h>
 #include <linux/of.h>
 #include <linux/phy.h>
 
@@ -118,6 +119,11 @@
 #define MV88Q2XXX_LED_INDEX_TX_ENABLE			0
 #define MV88Q2XXX_LED_INDEX_GPIO			1
 
+/* Marvell vendor PMA/PMD control for forced master/slave when AN is disabled */
+#define PMAPMD_MVL_PMAPMD_CTL				0x0834
+#define MASTER_MODE					BIT(14)
+#define MODE_MASK					BIT(14)
+
 struct mv88q2xxx_priv {
 	bool enable_led0;
 };
@@ -377,13 +383,57 @@ static int mv88q2xxx_read_link(struct phy_device *phydev)
 static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev)
 {
 	int ret;
+	int adv_l, adv_m, stat, stat2;
+
+	/* In forced mode, state and config are controlled via PMAPMD 0x834 */
+	if (phydev->autoneg == AUTONEG_DISABLE) {
+		ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_MVL_PMAPMD_CTL);
+		if (ret < 0)
+			return ret;
+
+		if (ret & MASTER_MODE) {
+			phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
+			phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+		} else {
+			phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
+			phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
+		}
+		return 0;
+	}
 
-	phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
-	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
-	if (ret < 0)
-		return ret;
 
-	if (ret & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER)
+	adv_l = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_L);
+	if (adv_l < 0)
+		return adv_l;
+	adv_m = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_M);
+	if (adv_m < 0)
+		return adv_m;
+
+	if (adv_l & MDIO_AN_T1_ADV_L_FORCE_MS)
+		phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+	else if (adv_m & MDIO_AN_T1_ADV_M_MST)
+		phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_PREFERRED;
+	else
+		phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
+
+	stat = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
+	if (stat < 0)
+		return stat;
+
+	if (stat & MDIO_MMD_AN_MV_STAT_MS_CONF_FAULT) {
+		phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
+		return 0;
+	}
+
+	stat2 = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT2);
+	if (stat2 < 0)
+		return stat2;
+	if (!(stat2 & MDIO_MMD_AN_MV_STAT2_AN_RESOLVED)) {
+		phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+		return 0;
+	}
+
+	if (stat & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER)
 		phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
 	else
 		phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
@@ -391,6 +441,34 @@ static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev)
 	return 0;
 }
 
+static int mv88q2xxx_setup_master_slave_forced(struct phy_device *phydev)
+{
+	int ret = 0;
+
+	switch (phydev->master_slave_set) {
+	case MASTER_SLAVE_CFG_MASTER_FORCE:
+	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+		ret = phy_modify_mmd_changed(phydev, MDIO_MMD_PMAPMD,
+					     PMAPMD_MVL_PMAPMD_CTL,
+					     MODE_MASK, MASTER_MODE);
+		break;
+	case MASTER_SLAVE_CFG_SLAVE_FORCE:
+	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+		ret = phy_modify_mmd_changed(phydev, MDIO_MMD_PMAPMD,
+					     PMAPMD_MVL_PMAPMD_CTL,
+					     MODE_MASK, 0);
+		break;
+	case MASTER_SLAVE_CFG_UNKNOWN:
+	case MASTER_SLAVE_CFG_UNSUPPORTED:
+	default:
+		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
 static int mv88q2xxx_read_aneg_speed(struct phy_device *phydev)
 {
 	int ret;
@@ -448,6 +526,11 @@ static int mv88q2xxx_read_status(struct phy_device *phydev)
 	if (ret < 0)
 		return ret;
 
+	/* Populate master/slave status also for forced modes */
+	ret = mv88q2xxx_read_master_slave_state(phydev);
+	if (ret < 0 && ret != -EOPNOTSUPP)
+		return ret;
+
 	return genphy_c45_read_pma(phydev);
 }
 
@@ -478,6 +561,20 @@ static int mv88q2xxx_config_aneg(struct phy_device *phydev)
 	if (ret)
 		return ret;
 
+	/* Configure Base-T1 master/slave per phydev->master_slave_set.
+	 * For AN disabled, program PMAPMD role directly; otherwise rely on
+	 * the standard Base-T1 AN advertisement bits.
+	 */
+	if (phydev->autoneg == AUTONEG_DISABLE) {
+		ret = mv88q2xxx_setup_master_slave_forced(phydev);
+		if (ret)
+			return ret;
+	} else {
+		ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
+		if (ret)
+			return ret;
+	}
+
 	return phydev->drv->soft_reset(phydev);
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] [88q2xxx] Add support for handling master/slave in forced mode
  2025-08-19 21:29 [PATCH] [88q2xxx] Add support for handling master/slave in forced mode Ilya A. Evenbach
@ 2025-08-19 22:13 ` Andrew Lunn
  2025-08-20 18:11   ` Ilya A. Evenbach
  0 siblings, 1 reply; 7+ messages in thread
From: Andrew Lunn @ 2025-08-19 22:13 UTC (permalink / raw)
  To: Ilya A. Evenbach; +Cc: netdev

On Tue, Aug 19, 2025 at 02:29:01PM -0700, Ilya A. Evenbach wrote:
> 88q2xxx PHYs have non-standard way of setting master/slave in
> forced mode.
> This change adds support for changing and reporting this setting
> correctly through ethtool.

Please could you Cc: Dimitri Fedrau <dima.fedrau@gmail.com>. He has
done most of the work on this driver.

> Signed-off-by: Ilya A. Evenbach <ievenbach@aurora.tech>
> ---
>  drivers/net/phy/marvell-88q2xxx.c | 107 ++++++++++++++++++++++++++++--
>  1 file changed, 102 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/phy/marvell-88q2xxx.c b/drivers/net/phy/marvell-88q2xxx.c
> index f3d83b04c953..1ab450056e86 100644
> --- a/drivers/net/phy/marvell-88q2xxx.c
> +++ b/drivers/net/phy/marvell-88q2xxx.c
> @@ -9,6 +9,7 @@
>  #include <linux/ethtool_netlink.h>
>  #include <linux/hwmon.h>
>  #include <linux/marvell_phy.h>
> +#include <linux/mdio.h>

I'm curious. What is needed from this?

	Andrew

^ permalink raw reply	[flat|nested] 7+ messages in thread

* (no subject)
  2025-08-19 22:13 ` Andrew Lunn
@ 2025-08-20 18:11   ` Ilya A. Evenbach
  2025-08-20 18:11     ` [PATCH] [88q2xxx] Add support for handling master/slave in forced mode Ilya A. Evenbach
  0 siblings, 1 reply; 7+ messages in thread
From: Ilya A. Evenbach @ 2025-08-20 18:11 UTC (permalink / raw)
  To: netdev; +Cc: dima.fedrau

> Please could you Cc: Dimitri Fedrau <dima.fedrau@gmail.com>. He has
> done most of the work on this driver.
Done

>> +#include <linux/mdio.h>

> I'm curious. What is needed from this?
Oops. This is, indeed, unneeded. Fixed.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH] [88q2xxx] Add support for handling master/slave in forced mode
  2025-08-20 18:11   ` Ilya A. Evenbach
@ 2025-08-20 18:11     ` Ilya A. Evenbach
  2025-08-21  0:35       ` Jakub Kicinski
  2025-08-21  8:02       ` Dimitri Fedrau
  0 siblings, 2 replies; 7+ messages in thread
From: Ilya A. Evenbach @ 2025-08-20 18:11 UTC (permalink / raw)
  To: netdev; +Cc: dima.fedrau, Ilya A. Evenbach

88q2xxx PHYs have non-standard way of setting master/slave in
forced mode.
This change adds support for changing and reporting this setting
correctly through ethtool.

Signed-off-by: Ilya A. Evenbach <ievenbach@aurora.tech>
---
 drivers/net/phy/marvell-88q2xxx.c | 106 ++++++++++++++++++++++++++++--
 1 file changed, 101 insertions(+), 5 deletions(-)

diff --git a/drivers/net/phy/marvell-88q2xxx.c b/drivers/net/phy/marvell-88q2xxx.c
index f3d83b04c953..b94d574fd9b7 100644
--- a/drivers/net/phy/marvell-88q2xxx.c
+++ b/drivers/net/phy/marvell-88q2xxx.c
@@ -118,6 +118,11 @@
 #define MV88Q2XXX_LED_INDEX_TX_ENABLE			0
 #define MV88Q2XXX_LED_INDEX_GPIO			1
 
+/* Marvell vendor PMA/PMD control for forced master/slave when AN is disabled */
+#define PMAPMD_MVL_PMAPMD_CTL				0x0834
+#define MASTER_MODE					BIT(14)
+#define MODE_MASK					BIT(14)
+
 struct mv88q2xxx_priv {
 	bool enable_led0;
 };
@@ -377,13 +382,57 @@ static int mv88q2xxx_read_link(struct phy_device *phydev)
 static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev)
 {
 	int ret;
+	int adv_l, adv_m, stat, stat2;
+
+	/* In forced mode, state and config are controlled via PMAPMD 0x834 */
+	if (phydev->autoneg == AUTONEG_DISABLE) {
+		ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_MVL_PMAPMD_CTL);
+		if (ret < 0)
+			return ret;
+
+		if (ret & MASTER_MODE) {
+			phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
+			phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+		} else {
+			phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
+			phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
+		}
+		return 0;
+	}
 
-	phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
-	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
-	if (ret < 0)
-		return ret;
 
-	if (ret & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER)
+	adv_l = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_L);
+	if (adv_l < 0)
+		return adv_l;
+	adv_m = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_M);
+	if (adv_m < 0)
+		return adv_m;
+
+	if (adv_l & MDIO_AN_T1_ADV_L_FORCE_MS)
+		phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+	else if (adv_m & MDIO_AN_T1_ADV_M_MST)
+		phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_PREFERRED;
+	else
+		phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
+
+	stat = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
+	if (stat < 0)
+		return stat;
+
+	if (stat & MDIO_MMD_AN_MV_STAT_MS_CONF_FAULT) {
+		phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
+		return 0;
+	}
+
+	stat2 = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT2);
+	if (stat2 < 0)
+		return stat2;
+	if (!(stat2 & MDIO_MMD_AN_MV_STAT2_AN_RESOLVED)) {
+		phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+		return 0;
+	}
+
+	if (stat & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER)
 		phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
 	else
 		phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
@@ -391,6 +440,34 @@ static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev)
 	return 0;
 }
 
+static int mv88q2xxx_setup_master_slave_forced(struct phy_device *phydev)
+{
+	int ret = 0;
+
+	switch (phydev->master_slave_set) {
+	case MASTER_SLAVE_CFG_MASTER_FORCE:
+	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+		ret = phy_modify_mmd_changed(phydev, MDIO_MMD_PMAPMD,
+					     PMAPMD_MVL_PMAPMD_CTL,
+					     MODE_MASK, MASTER_MODE);
+		break;
+	case MASTER_SLAVE_CFG_SLAVE_FORCE:
+	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+		ret = phy_modify_mmd_changed(phydev, MDIO_MMD_PMAPMD,
+					     PMAPMD_MVL_PMAPMD_CTL,
+					     MODE_MASK, 0);
+		break;
+	case MASTER_SLAVE_CFG_UNKNOWN:
+	case MASTER_SLAVE_CFG_UNSUPPORTED:
+	default:
+		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
 static int mv88q2xxx_read_aneg_speed(struct phy_device *phydev)
 {
 	int ret;
@@ -448,6 +525,11 @@ static int mv88q2xxx_read_status(struct phy_device *phydev)
 	if (ret < 0)
 		return ret;
 
+	/* Populate master/slave status also for forced modes */
+	ret = mv88q2xxx_read_master_slave_state(phydev);
+	if (ret < 0 && ret != -EOPNOTSUPP)
+		return ret;
+
 	return genphy_c45_read_pma(phydev);
 }
 
@@ -478,6 +560,20 @@ static int mv88q2xxx_config_aneg(struct phy_device *phydev)
 	if (ret)
 		return ret;
 
+	/* Configure Base-T1 master/slave per phydev->master_slave_set.
+	 * For AN disabled, program PMAPMD role directly; otherwise rely on
+	 * the standard Base-T1 AN advertisement bits.
+	 */
+	if (phydev->autoneg == AUTONEG_DISABLE) {
+		ret = mv88q2xxx_setup_master_slave_forced(phydev);
+		if (ret)
+			return ret;
+	} else {
+		ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
+		if (ret)
+			return ret;
+	}
+
 	return phydev->drv->soft_reset(phydev);
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] [88q2xxx] Add support for handling master/slave in forced mode
  2025-08-20 18:11     ` [PATCH] [88q2xxx] Add support for handling master/slave in forced mode Ilya A. Evenbach
@ 2025-08-21  0:35       ` Jakub Kicinski
  2025-08-21  8:02       ` Dimitri Fedrau
  1 sibling, 0 replies; 7+ messages in thread
From: Jakub Kicinski @ 2025-08-21  0:35 UTC (permalink / raw)
  To: Ilya A. Evenbach; +Cc: netdev, dima.fedrau

A few drive-by comments...

On Wed, 20 Aug 2025 11:11:43 -0700 Ilya A. Evenbach wrote:
> In-Reply-To: <20250820181143.2288755-1-ievenbach@aurora.tech>

Please start a new thread for each new revision.
Change subject to [PATCH vN], where vN is the revision.
Add

v1: https://lore.kernel.org/20250820181143.2288755-2-ievenbach@aurora.tech

under the --- for reference to older versions

> Cc: dima.fedrau@gmail.com, "Ilya A. Evenbach" <ievenbach@aurora.tech>

The cc list is still pretty sparse

> Subject: [PATCH] [88q2xxx] Add support for handling master/slave in forced mode

the [88q2xxx] will be stripped by git when applying. Please look thru
the history of the file and find the correct way to prefix.

> 88q2xxx PHYs have non-standard way of setting master/slave in
> forced mode.
> This change adds support for changing and reporting this setting
> correctly through ethtool.

imperative mood:

 Add support for changing...


> +/* Marvell vendor PMA/PMD control for forced master/slave when AN is disabled */
> +#define PMAPMD_MVL_PMAPMD_CTL				0x0834
> +#define MASTER_MODE					BIT(14)
> +#define MODE_MASK					BIT(14)

Other defines in context seem to be prefixed with  MV88Q2XXX_
please prefer prefixing local defines.

>  struct mv88q2xxx_priv {
>  	bool enable_led0;
>  };
> @@ -377,13 +382,57 @@ static int mv88q2xxx_read_link(struct phy_device *phydev)
>  static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev)
>  {
>  	int ret;
> +	int adv_l, adv_m, stat, stat2;

nit: in networking we like variable declaration lines to be sorted longest to shortest.
-- 
pw-bot: cr

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] [88q2xxx] Add support for handling master/slave in forced mode
  2025-08-20 18:11     ` [PATCH] [88q2xxx] Add support for handling master/slave in forced mode Ilya A. Evenbach
  2025-08-21  0:35       ` Jakub Kicinski
@ 2025-08-21  8:02       ` Dimitri Fedrau
       [not found]         ` <CAJmffrpUTzH0_siTUZodX7Gu5JPRvkgUb+73CgXSWu1QSzegSA@mail.gmail.com>
  1 sibling, 1 reply; 7+ messages in thread
From: Dimitri Fedrau @ 2025-08-21  8:02 UTC (permalink / raw)
  To: Ilya A. Evenbach; +Cc: netdev

Hi Ilya,

Am Wed, Aug 20, 2025 at 11:11:43AM -0700 schrieb Ilya A. Evenbach:
> 88q2xxx PHYs have non-standard way of setting master/slave in
> forced mode.
> This change adds support for changing and reporting this setting
> correctly through ethtool.
> 
> Signed-off-by: Ilya A. Evenbach <ievenbach@aurora.tech>
> ---
>  drivers/net/phy/marvell-88q2xxx.c | 106 ++++++++++++++++++++++++++++--
>  1 file changed, 101 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/phy/marvell-88q2xxx.c b/drivers/net/phy/marvell-88q2xxx.c
> index f3d83b04c953..b94d574fd9b7 100644
> --- a/drivers/net/phy/marvell-88q2xxx.c
> +++ b/drivers/net/phy/marvell-88q2xxx.c
> @@ -118,6 +118,11 @@
>  #define MV88Q2XXX_LED_INDEX_TX_ENABLE			0
>  #define MV88Q2XXX_LED_INDEX_GPIO			1
>  
> +/* Marvell vendor PMA/PMD control for forced master/slave when AN is disabled */
> +#define PMAPMD_MVL_PMAPMD_CTL				0x0834

Already defined, see MDIO_PMA_PMD_BT1_CTRL.

> +#define MASTER_MODE					BIT(14)

Already defines, see MDIO_PMA_PMD_BT1_CTRL_CFG_MST.

> +#define MODE_MASK					BIT(14)
> +
>  struct mv88q2xxx_priv {
>  	bool enable_led0;
>  };
> @@ -377,13 +382,57 @@ static int mv88q2xxx_read_link(struct phy_device *phydev)
>  static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev)
>  {
>  	int ret;
> +	int adv_l, adv_m, stat, stat2;
> +
> +	/* In forced mode, state and config are controlled via PMAPMD 0x834 */
> +	if (phydev->autoneg == AUTONEG_DISABLE) {
> +		ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, PMAPMD_MVL_PMAPMD_CTL);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (ret & MASTER_MODE) {
> +			phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
> +			phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
> +		} else {
> +			phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
> +			phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
> +		}
> +		return 0;
> +	}
>  
> -	phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
> -	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
> -	if (ret < 0)
> -		return ret;
>  
> -	if (ret & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER)
> +	adv_l = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_L);
> +	if (adv_l < 0)
> +		return adv_l;
> +	adv_m = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_M);
> +	if (adv_m < 0)
> +		return adv_m;
> +
> +	if (adv_l & MDIO_AN_T1_ADV_L_FORCE_MS)
> +		phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
> +	else if (adv_m & MDIO_AN_T1_ADV_M_MST)
> +		phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_PREFERRED;
> +	else
> +		phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
> +
> +	stat = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
> +	if (stat < 0)
> +		return stat;
> +
> +	if (stat & MDIO_MMD_AN_MV_STAT_MS_CONF_FAULT) {
> +		phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
> +		return 0;
> +	}
> +
> +	stat2 = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT2);
> +	if (stat2 < 0)
> +		return stat2;
> +	if (!(stat2 & MDIO_MMD_AN_MV_STAT2_AN_RESOLVED)) {
> +		phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
> +		return 0;
> +	}
> +
> +	if (stat & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER)
>  		phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
>  	else
>  		phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
> @@ -391,6 +440,34 @@ static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev)
>  	return 0;
>  }
>  

Is there a issue with the function you are trying to fix ? Seems that
you copied some generic functions into it.

> +static int mv88q2xxx_setup_master_slave_forced(struct phy_device *phydev)
> +{
> +	int ret = 0;
> +
> +	switch (phydev->master_slave_set) {
> +	case MASTER_SLAVE_CFG_MASTER_FORCE:
> +	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
> +		ret = phy_modify_mmd_changed(phydev, MDIO_MMD_PMAPMD,
> +					     PMAPMD_MVL_PMAPMD_CTL,
> +					     MODE_MASK, MASTER_MODE);
> +		break;
> +	case MASTER_SLAVE_CFG_SLAVE_FORCE:
> +	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
> +		ret = phy_modify_mmd_changed(phydev, MDIO_MMD_PMAPMD,
> +					     PMAPMD_MVL_PMAPMD_CTL,
> +					     MODE_MASK, 0);
> +		break;
> +	case MASTER_SLAVE_CFG_UNKNOWN:
> +	case MASTER_SLAVE_CFG_UNSUPPORTED:
> +	default:
> +		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
> +		ret = 0;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +

This function does the same as genphy_c45_pma_baset1_setup_master_slave.
Please use the generic function. Besides you are introducing register
PMAPMD_MVL_PMAPMD_CTL which is MDIO_PMA_PMD_BT1_CTRL.

>  static int mv88q2xxx_read_aneg_speed(struct phy_device *phydev)
>  {
>  	int ret;
> @@ -448,6 +525,11 @@ static int mv88q2xxx_read_status(struct phy_device *phydev)
>  	if (ret < 0)
>  		return ret;
>  
> +	/* Populate master/slave status also for forced modes */
> +	ret = mv88q2xxx_read_master_slave_state(phydev);
> +	if (ret < 0 && ret != -EOPNOTSUPP)
> +		return ret;
> +
>  	return genphy_c45_read_pma(phydev);
>  }
>  

Why ? This function is only used in case AUTONEG_ENABLE.

> @@ -478,6 +560,20 @@ static int mv88q2xxx_config_aneg(struct phy_device *phydev)
>  	if (ret)
>  		return ret;
>  
> +	/* Configure Base-T1 master/slave per phydev->master_slave_set.
> +	 * For AN disabled, program PMAPMD role directly; otherwise rely on
> +	 * the standard Base-T1 AN advertisement bits.
> +	 */
> +	if (phydev->autoneg == AUTONEG_DISABLE) {
> +		ret = mv88q2xxx_setup_master_slave_forced(phydev);
> +		if (ret)
> +			return ret;
> +	} else {
> +		ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
> +		if (ret)
> +			return ret;
> +	}
> +
>  	return phydev->drv->soft_reset(phydev);
>  }
>  

I don't see any reason why genphy_c45_config_aneg isn't sufficient here.
In case AUTONEG_DISABLE, genphy_c45_pma_setup_forced is called and calls
genphy_c45_pma_baset1_setup_master_slave which is basically the same as
mv88q2xxx_setup_master_slave_forced.
In case AUTONEG_ENABLE, calling genphy_c45_pma_baset1_setup_master_slave is
wrong, please look how genphy_c45_an_config_aneg is implemented.

Please take other users of the driver into CC, they did a lot of
reviewing and testing in the past. If there is some issue with the
driver, they should know:

"Niklas Söderlund" <niklas.soderlund+renesas@ragnatech.se>
"Gregor Herburger" <gregor.herburger@ew.tq-group.com>
"Stefan Eichenberger" <eichest@gmail.com>
"Geert Uytterhoeven" <geert@linux-m68k.org>

Which device are you using, and why did you need this patch ? Is there
any issue you are trying to fix ? On my side I did a lot of testing with
the different modes and never experienced any problems so far.

Best regards,
Dimitri Fedrau

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] [88q2xxx] Add support for handling master/slave in forced mode
       [not found]         ` <CAJmffrpUTzH0_siTUZodX7Gu5JPRvkgUb+73CgXSWu1QSzegSA@mail.gmail.com>
@ 2025-08-22  6:58           ` Dimitri Fedrau
  0 siblings, 0 replies; 7+ messages in thread
From: Dimitri Fedrau @ 2025-08-22  6:58 UTC (permalink / raw)
  To: Ilya Evenbach, Jakub Kicinski, Andrew Lunn, Niklas Söderlund,
	Gregor Herburger, Stefan Eichenberger
  Cc: netdev

Hi Ilya,

Am Thu, Aug 21, 2025 at 01:53:04PM -0700 schrieb Ilya Evenbach:
> I am using 88Q2221, This PHY has a bug - MDIO_PMA_EXTABLE is not properly
> implemented (reads as 0x20).
> As a result, genphy_c45_baset1_able() returns false, and master/slave is
> not set up or read properly in forced mode.

That is not 88Q2221 specific, please have a look at function
mv88q2xxx_config_init which solves this issue:

/* The 88Q2XXX PHYs do have the extended ability register available, but
 * register MDIO_PMA_EXTABLE where they should signalize it does not
 * work according to specification. Therefore, we force it here.
 */
phydev->pma_extable = MDIO_PMA_EXTABLE_BT1;

> I will try to clean up this patch to better utilize generic functions.
> 

What you are trying to do is adding support for 88Q2221 which has the
same phy_id as the 88Q2220 but needs a different(additional) setup to
work properly(init sequence, ...).
Please change your commit message then.

> On Thu, Aug 21, 2025 at 1:02 AM Dimitri Fedrau <dima.fedrau@gmail.com>
> wrote:
> >
> > Hi Ilya,
> >
> > Am Wed, Aug 20, 2025 at 11:11:43AM -0700 schrieb Ilya A. Evenbach:
> > > 88q2xxx PHYs have non-standard way of setting master/slave in
> > > forced mode.
> > > This change adds support for changing and reporting this setting
> > > correctly through ethtool.
> > >
> > > Signed-off-by: Ilya A. Evenbach <ievenbach@aurora.tech>
> > > ---
> > >  drivers/net/phy/marvell-88q2xxx.c | 106 ++++++++++++++++++++++++++++--
> > >  1 file changed, 101 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/drivers/net/phy/marvell-88q2xxx.c
> b/drivers/net/phy/marvell-88q2xxx.c
> > > index f3d83b04c953..b94d574fd9b7 100644
> > > --- a/drivers/net/phy/marvell-88q2xxx.c
> > > +++ b/drivers/net/phy/marvell-88q2xxx.c
> > > @@ -118,6 +118,11 @@
> > >  #define MV88Q2XXX_LED_INDEX_TX_ENABLE                        0
> > >  #define MV88Q2XXX_LED_INDEX_GPIO                     1
> > >
> > > +/* Marvell vendor PMA/PMD control for forced master/slave when AN is
> disabled */
> > > +#define PMAPMD_MVL_PMAPMD_CTL                                0x0834
> >
> > Already defined, see MDIO_PMA_PMD_BT1_CTRL.
> >
> > > +#define MASTER_MODE                                  BIT(14)
> >
> > Already defines, see MDIO_PMA_PMD_BT1_CTRL_CFG_MST.
> >
> > > +#define MODE_MASK                                    BIT(14)
> > > +
> > >  struct mv88q2xxx_priv {
> > >       bool enable_led0;
> > >  };
> > > @@ -377,13 +382,57 @@ static int mv88q2xxx_read_link(struct phy_device
> *phydev)
> > >  static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev)
> > >  {
> > >       int ret;
> > > +     int adv_l, adv_m, stat, stat2;
> > > +
> > > +     /* In forced mode, state and config are controlled via PMAPMD
> 0x834 */
> > > +     if (phydev->autoneg == AUTONEG_DISABLE) {
> > > +             ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
> PMAPMD_MVL_PMAPMD_CTL);
> > > +             if (ret < 0)
> > > +                     return ret;
> > > +
> > > +             if (ret & MASTER_MODE) {
> > > +                     phydev->master_slave_state =
> MASTER_SLAVE_STATE_MASTER;
> > > +                     phydev->master_slave_get =
> MASTER_SLAVE_CFG_MASTER_FORCE;
> > > +             } else {
> > > +                     phydev->master_slave_state =
> MASTER_SLAVE_STATE_SLAVE;
> > > +                     phydev->master_slave_get =
> MASTER_SLAVE_CFG_SLAVE_FORCE;
> > > +             }
> > > +             return 0;
> > > +     }
> > >
> > > -     phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
> > > -     ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
> > > -     if (ret < 0)
> > > -             return ret;
> > >
> > > -     if (ret & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER)
> > > +     adv_l = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_L);
> > > +     if (adv_l < 0)
> > > +             return adv_l;
> > > +     adv_m = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_M);
> > > +     if (adv_m < 0)
> > > +             return adv_m;
> > > +
> > > +     if (adv_l & MDIO_AN_T1_ADV_L_FORCE_MS)
> > > +             phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
> > > +     else if (adv_m & MDIO_AN_T1_ADV_M_MST)
> > > +             phydev->master_slave_get =
> MASTER_SLAVE_CFG_MASTER_PREFERRED;
> > > +     else
> > > +             phydev->master_slave_get =
> MASTER_SLAVE_CFG_SLAVE_PREFERRED;
> > > +
> > > +     stat = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
> > > +     if (stat < 0)
> > > +             return stat;
> > > +
> > > +     if (stat & MDIO_MMD_AN_MV_STAT_MS_CONF_FAULT) {
> > > +             phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
> > > +             return 0;
> > > +     }
> > > +
> > > +     stat2 = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT2);
> > > +     if (stat2 < 0)
> > > +             return stat2;
> > > +     if (!(stat2 & MDIO_MMD_AN_MV_STAT2_AN_RESOLVED)) {
> > > +             phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
> > > +             return 0;
> > > +     }
> > > +
> > > +     if (stat & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER)
> > >               phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
> > >       else
> > >               phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
> > > @@ -391,6 +440,34 @@ static int
> mv88q2xxx_read_master_slave_state(struct phy_device *phydev)
> > >       return 0;
> > >  }
> > >
> >
> > Is there a issue with the function you are trying to fix ? Seems that
> > you copied some generic functions into it.
> >
> > > +static int mv88q2xxx_setup_master_slave_forced(struct phy_device
> *phydev)
> > > +{
> > > +     int ret = 0;
> > > +
> > > +     switch (phydev->master_slave_set) {
> > > +     case MASTER_SLAVE_CFG_MASTER_FORCE:
> > > +     case MASTER_SLAVE_CFG_MASTER_PREFERRED:
> > > +             ret = phy_modify_mmd_changed(phydev, MDIO_MMD_PMAPMD,
> > > +                                          PMAPMD_MVL_PMAPMD_CTL,
> > > +                                          MODE_MASK, MASTER_MODE);
> > > +             break;
> > > +     case MASTER_SLAVE_CFG_SLAVE_FORCE:
> > > +     case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
> > > +             ret = phy_modify_mmd_changed(phydev, MDIO_MMD_PMAPMD,
> > > +                                          PMAPMD_MVL_PMAPMD_CTL,
> > > +                                          MODE_MASK, 0);
> > > +             break;
> > > +     case MASTER_SLAVE_CFG_UNKNOWN:
> > > +     case MASTER_SLAVE_CFG_UNSUPPORTED:
> > > +     default:
> > > +             phydev_warn(phydev, "Unsupported Master/Slave mode\n");
> > > +             ret = 0;
> > > +             break;
> > > +     }
> > > +
> > > +     return ret;
> > > +}
> > > +
> >
> > This function does the same as genphy_c45_pma_baset1_setup_master_slave.
> > Please use the generic function. Besides you are introducing register
> > PMAPMD_MVL_PMAPMD_CTL which is MDIO_PMA_PMD_BT1_CTRL.
> >
> > >  static int mv88q2xxx_read_aneg_speed(struct phy_device *phydev)
> > >  {
> > >       int ret;
> > > @@ -448,6 +525,11 @@ static int mv88q2xxx_read_status(struct phy_device
> *phydev)
> > >       if (ret < 0)
> > >               return ret;
> > >
> > > +     /* Populate master/slave status also for forced modes */
> > > +     ret = mv88q2xxx_read_master_slave_state(phydev);
> > > +     if (ret < 0 && ret != -EOPNOTSUPP)
> > > +             return ret;
> > > +
> > >       return genphy_c45_read_pma(phydev);
> > >  }
> > >
> >
> > Why ? This function is only used in case AUTONEG_ENABLE.
> >
> > > @@ -478,6 +560,20 @@ static int mv88q2xxx_config_aneg(struct phy_device
> *phydev)
> > >       if (ret)
> > >               return ret;
> > >
> > > +     /* Configure Base-T1 master/slave per phydev->master_slave_set.
> > > +      * For AN disabled, program PMAPMD role directly; otherwise rely
> on
> > > +      * the standard Base-T1 AN advertisement bits.
> > > +      */
> > > +     if (phydev->autoneg == AUTONEG_DISABLE) {
> > > +             ret = mv88q2xxx_setup_master_slave_forced(phydev);
> > > +             if (ret)
> > > +                     return ret;
> > > +     } else {
> > > +             ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
> > > +             if (ret)
> > > +                     return ret;
> > > +     }
> > > +
> > >       return phydev->drv->soft_reset(phydev);
> > >  }
> > >
> >
> > I don't see any reason why genphy_c45_config_aneg isn't sufficient here.
> > In case AUTONEG_DISABLE, genphy_c45_pma_setup_forced is called and calls
> > genphy_c45_pma_baset1_setup_master_slave which is basically the same as
> > mv88q2xxx_setup_master_slave_forced.
> > In case AUTONEG_ENABLE, calling genphy_c45_pma_baset1_setup_master_slave
> is
> > wrong, please look how genphy_c45_an_config_aneg is implemented.
> >
> > Please take other users of the driver into CC, they did a lot of
> > reviewing and testing in the past. If there is some issue with the
> > driver, they should know:
> >
> > "Niklas Söderlund" <niklas.soderlund+renesas@ragnatech.se>
> > "Gregor Herburger" <gregor.herburger@ew.tq-group.com>
> > "Stefan Eichenberger" <eichest@gmail.com>
> > "Geert Uytterhoeven" <geert@linux-m68k.org>
> >
> > Which device are you using, and why did you need this patch ? Is there
> > any issue you are trying to fix ? On my side I did a lot of testing with
> > the different modes and never experienced any problems so far.
> >
> > Best regards,
> > Dimitri Fedrau

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-08-22  6:58 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-19 21:29 [PATCH] [88q2xxx] Add support for handling master/slave in forced mode Ilya A. Evenbach
2025-08-19 22:13 ` Andrew Lunn
2025-08-20 18:11   ` Ilya A. Evenbach
2025-08-20 18:11     ` [PATCH] [88q2xxx] Add support for handling master/slave in forced mode Ilya A. Evenbach
2025-08-21  0:35       ` Jakub Kicinski
2025-08-21  8:02       ` Dimitri Fedrau
     [not found]         ` <CAJmffrpUTzH0_siTUZodX7Gu5JPRvkgUb+73CgXSWu1QSzegSA@mail.gmail.com>
2025-08-22  6:58           ` Dimitri Fedrau

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).