[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250819212901.1559962-1-ievenbach@aurora.tech>
Date: Tue, 19 Aug 2025 14:29:01 -0700
From: "Ilya A. Evenbach" <ievenbach@...ora.tech>
To: netdev@...r.kernel.org
Cc: "Ilya A. Evenbach" <ievenbach@...ora.tech>
Subject: [PATCH] [88q2xxx] Add support for handling master/slave in forced mode
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@...ora.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
Powered by blists - more mailing lists