* [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).