[PATCH net-next v?] [PATCH net-next] net: phy: motorcomm: add YT8831 2.5G PHY support

DORMANTno replies

From: "Frank.sae" <frank.sae@motor-comm.com>
Date: 2026-10-22 05:46:53
Also in: lkml
Subsystem: ethernet phy library, motorcomm phy driver, networking drivers, the rest · Maintainers: Andrew Lunn, Heiner Kallweit, Frank, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

Add a driver for motorcomm yt8831/yt8821-vd ethernet phy.
Verified the driver in BPI-R3(with MTK Filogic-830 Soc)development board
YT8831 works in AUTO_BX2500_SGMII or FORCE_BX2500 interface,
supports 2.5G/1000M/100M/10M speeds, and wol(magic package)

Signed-off-by: Frank.sae <frank.sae@motor-comm.com>
---
 drivers/net/phy/motorcomm.c | 548 +++++++++++++++++++++++++++++++++++-
 1 file changed, 546 insertions(+), 2 deletions(-)
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index 463e1ea3efc9..04852b4a5ba5 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Motorcomm 8511/8521/8522/8531/8531S/861x/8821 PHY driver.
+ * Motorcomm 8511/8521/8522/8531/8531S/861x/8821/8831 PHY driver.
  *
  * Author: Peter Geis <pgwipeout@gmail.com>
  * Author: Frank <Frank.Sae@motor-comm.com>
@@ -22,6 +22,7 @@
 /* Live captured from YT921x switch internal MDIO bus */
 #define PHY_ID_INT861X		0x01e04281
 #define PHY_ID_YT8821		0x4f51ea19
+#define PHY_ID_YT8831		0x4f51e938
 /* YT8521/YT8531S/YT8821 Register Overview
  *	UTP Register space	|	FIBER Register space
  *  ------------------------------------------------------------
@@ -171,6 +172,11 @@
 #define YT8521_RSSR_UTP_SPACE			(0x0 << 1)
 #define YT8521_RSSR_TO_BE_ARBITRATED		(0xFF)
 
+#define YT8831_REG_SPACE_SELECT_REG		0x8020
+#define YT8831_RSSR_SPACE_MASK			(BIT(1) | BIT(0))
+#define YT8831_RSSR_FIBER_SAPCE			BIT(1)
+#define YT8831_RSSR_UTP_SAPCE			BIT(0)
+
 #define YT8521_CHIP_CONFIG_REG			0xA001
 #define YT8521_CCR_SW_RST			BIT(15)
 #define YT8531_RGMII_LDO_VOL_MASK		GENMASK(5, 4)
@@ -258,6 +264,53 @@
 #define YTPHY_WCR_INTR_SEL			BIT(6)
 #define YTPHY_WCR_ENABLE			BIT(3)
 
+#define YT8831_WOL_CONFIG_REG			0x160a
+#define YT8831_WOL_ENABLE_MASK			BIT(0)
+#define YT8831_WOL_ENABLE			BIT(0)
+#define YT8831_WOL_DISABLE			0x0
+#define YT8831_WOL_PULSE_WIDTH_MASK             (BIT(3) | BIT(2))
+#define YT8831_WOL_PULSE_WIDTH_168MS            BIT(2)
+#define YT8831_WOL_OUTPUT_TYPE_SEL_MASK         BIT(1)
+#define YT8831_WOL_OUTPUT_TYPE_SEL_PULSE        BIT(1)
+
+#define YT8831_WOL_MACADDR2_REG	                0x1604
+#define YT8831_WOL_MACADDR1_REG			0xA006
+#define YT8831_WOL_MACADDR0_REG			0xA008
+
+#define YT8831_WCR_INTR_REG                     0x8301
+#define YT8831_WCR_INTR_SEL_MASK		(BIT(1) | BIT(0))
+#define YT8831_WCR_INTR_SEL_WOL_ONLY		0x2
+#define YT8831_WCR_INTR_SEL_ALL			0x1
+
+
+#define YT8831_SDS_WRITE_PROTECT_REG		0x873
+#define YT8831_SDS_SGMII_OUTPUT_REG		0x1002
+
+#define YT8831_UTP_WRITE_PROTECT_REG			0x1
+#define YT8831_UTP_DAC_DATA_DLY_REG			0x0
+#define YT8831_UTP_REF_DLAY_SEL_REG			0x3
+#define YT8831_UTP_REF_DLAY_SEL_FEXT_REG		0x4
+#define YT8831_UTP_COE_MANUAL_SHIFT_SEL_2500_REG	0x723
+#define YT8831_UTP_EEE_OPTIM_1_REG			0x727
+#define YT8831_UTP_EEE_OPTIM_2_REG			0x339
+#define YT8831_UTP_EEE_MSE1_REG				0xc3
+#define YT8831_UTP_EEE_MSE2_REG				0x32
+#define YT8831_UTP_EEE_OPTIM_3_REG			0xff
+#define YT8831_UTP_EEE_OPTIM_4_REG			0x372
+#define YT8831_UTP_EEE_OPTIM_5_REG			0x336
+#define YT8831_UTP_EEE_OPTIM_6_REG			0x340
+#define YT8831_UTP_PSAN_OPTIM_REG			0x1e58
+#define YT8831_UTP_1000M_TEMPLATE_OPTIM_1_REG		0x209e
+#define YT8831_UTP_1000M_TEMPLATE_OPTIM_2_REG		0x20a4
+#define YT8831_UTP_1000M_TEMPLATE_OPTIM_3_REG		0x20a9
+#define YT8831_UTP_1000M_TEMPLATE_OPTIM_4_REG		0x20af
+#define YT8831_UTP_1000M_TEMPLATE_OPTIM_5_REG		0x20b4
+#define YT8831_UTP_1000M_TEMPLATE_OPTIM_6_REG		0x20ba
+#define YT8831_UTP_1000M_TEMPLATE_OPTIM_7_REG		0x20bf
+#define YT8831_UTP_1000M_TEMPLATE_OPTIM_8_REG		0x20c5
+#define YT8831_UTP_2500M_TEMPLATE_OPTIM_1_REG		0x20ff
+#define YT8831_UTP_2500M_TEMPLATE_OPTIM_2_REG		0x2100
+
 /* 2b00 84ms
  * 2b01 168ms  *default*
  * 2b10 336ms
@@ -397,6 +450,16 @@
 #define YT8821_CHIP_MODE_AUTO_BX2500_SGMII	0
 #define YT8821_CHIP_MODE_FORCE_BX2500		1
 
+#define YT8831_CHIP_MODE_SEL				0x8802
+#define YT8831_CHIP_MODE_SEL_MASK			GENMASK(2, 0)
+#define YT8831_CHIP_MODE_AUTO_BX2500_SGMII		0x0
+#define YT8831_CHIP_MODE_FORCE_2500BASEX		BIT(0)
+#define YT8831_CHIP_MODE_UTP_TO_FIBER_FORCE		(BIT(2) | BIT(0))
+
+#define YT8831_MDIO_ADDRESS_CTL				0x8004
+#define YT8831_MDIO_ADDRESS_CTL_EN_BROADCAST_MASK	BIT(8)
+#define YT8831_MDIO_ADDRESS_CTL_EN_BROADCAST		BIT(8)
+
 struct yt8521_priv {
 	/* combo_advertising is used for case of YT8521 in combo mode,
 	 * this means that yt8521 may work in utp or fiber mode which depends
@@ -543,6 +606,29 @@ static int ytphy_modify_ext_with_lock(struct phy_device *phydev, u16 regnum,
 	return ret;
 }
 
+/**
+ * yt8831_get_wol() - report whether wake-on-lan is enabled
+ * @phydev: a pointer to a &struct phy_device
+ * @wol: a pointer to a &struct ethtool_wolinfo
+ *
+ * NOTE: YT8831_WOL_CONFIG_REG is common ext reg.
+ */
+static void yt8831_get_wol(struct phy_device *phydev,
+			  struct ethtool_wolinfo *wol)
+{
+	int wol_config;
+
+	wol->supported = WAKE_MAGIC;
+	wol->wolopts = 0;
+
+	wol_config = ytphy_read_ext_with_lock(phydev, YT8831_WOL_CONFIG_REG);
+	if (wol_config < 0)
+		return;
+
+	if (wol_config & YT8831_WOL_ENABLE_MASK)
+		wol->wolopts |= WAKE_MAGIC;
+}
+
 /**
  * ytphy_get_wol() - report whether wake-on-lan is enabled
  * @phydev: a pointer to a &struct phy_device
@@ -704,6 +790,74 @@ static int yt8531_set_wol(struct phy_device *phydev,
 	return 0;
 }
 
+/**
+ * yt8831_set_wol() - turn wake-on-lan on or off
+ * @phydev: a pointer to a &struct phy_device
+ * @wol: a pointer to a &struct ethtool_wolinfo
+ *
+ * NOTE: YT8831_WOL_CONFIG_REG is common ext reg.
+ */
+static int yt8831_set_wol(struct phy_device *phydev,
+			  struct ethtool_wolinfo *wol)
+{
+	const u16 mac_addr_reg[] = {
+		YT8831_WOL_MACADDR2_REG,
+		YT8831_WOL_MACADDR1_REG,
+		YT8831_WOL_MACADDR0_REG,
+	};
+	const u8 *mac_addr;
+	u16 mask, val;
+	int ret;
+	u8 i;
+
+	if (wol->wolopts & WAKE_MAGIC) {
+		mac_addr = phydev->attached_dev->dev_addr;
+
+		/* Store the device address for the magic packet */
+		for (i = 0; i < 3; i++) {
+			ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i],
+							((mac_addr[i * 2] << 8)) |
+							(mac_addr[i * 2 + 1]));
+			if (ret < 0)
+				return ret;
+		}
+
+		/* Enable WOL feature */
+		mask = YT8831_WOL_ENABLE_MASK
+			| YT8831_WOL_PULSE_WIDTH_MASK
+			| YT8831_WOL_OUTPUT_TYPE_SEL_MASK;
+
+		val = YT8831_WOL_ENABLE
+			| YT8831_WOL_PULSE_WIDTH_168MS
+			| YT8831_WOL_OUTPUT_TYPE_SEL_PULSE;
+
+		ret = ytphy_modify_ext_with_lock(phydev, YT8831_WOL_CONFIG_REG,
+						 mask, val);
+		if (ret < 0)
+			return ret;
+
+		/* Enable WOL interrupt */
+		ret = ytphy_modify_ext_with_lock(phydev, YT8831_WCR_INTR_REG,
+				YT8831_WCR_INTR_SEL_MASK,
+				YT8831_WCR_INTR_SEL_WOL_ONLY);
+		if (ret < 0)
+			return ret;
+	} else {
+		/* Disable WOL feature */
+		mask = YT8831_WOL_ENABLE_MASK;
+		ret = ytphy_modify_ext_with_lock(phydev, YT8831_WOL_CONFIG_REG,
+							mask, YT8831_WOL_DISABLE);
+
+		/* Disable WOL interrupt */
+		ret = ytphy_modify_ext_with_lock(phydev, YT8831_WCR_INTR_REG,
+					YT8831_WCR_INTR_SEL_MASK, YT8831_WCR_INTR_SEL_ALL);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
 static int yt8511_read_page(struct phy_device *phydev)
 {
 	return __phy_read(phydev, YT8511_PAGE_SELECT);
@@ -798,6 +952,27 @@ static int yt8521_read_page(struct phy_device *phydev)
 	return YT8521_RSSR_UTP_SPACE;
 };
 
+/**
+ * yt8831_read_page() - read reg page
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns current reg space of yt8831 (YT8831_RSSR_FIBER_SAPCE/
+ * YT8831_RSSR_UTP_SAPCE) or negative errno code
+ */
+static int yt8831_read_page(struct phy_device *phydev)
+{
+	int old_page;
+
+	old_page = ytphy_read_ext(phydev, YT8831_REG_SPACE_SELECT_REG);
+	if (old_page < 0)
+		return old_page;
+
+	if ((old_page & YT8831_RSSR_SPACE_MASK) == YT8831_RSSR_FIBER_SAPCE)
+		return YT8831_RSSR_FIBER_SAPCE;
+
+	return YT8831_RSSR_UTP_SAPCE;
+};
+
 /**
  * yt8521_write_page() - write reg page
  * @phydev: a pointer to a &struct phy_device
@@ -818,6 +993,26 @@ static int yt8521_write_page(struct phy_device *phydev, int page)
 	return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set);
 };
 
+/**
+ * yt8831_write_page() - write reg page
+ * @phydev: a pointer to a &struct phy_device
+ * @page: The reg page(YT8831_RSSR_FIBER_SAPCE/YT8831_RSSR_UTP_SAPCE) to write.
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8831_write_page(struct phy_device *phydev, int page)
+{
+	int mask = YT8831_RSSR_SPACE_MASK;
+	int set;
+
+	if ((page & YT8831_RSSR_SPACE_MASK) == YT8831_RSSR_FIBER_SAPCE)
+		set = YT8831_RSSR_FIBER_SAPCE;
+	else
+		set = YT8831_RSSR_UTP_SAPCE;
+
+	return ytphy_modify_ext(phydev, YT8831_REG_SPACE_SELECT_REG, mask, set);
+};
+
 /**
  * struct ytphy_cfg_reg_map - map a config value to a register value
  * @cfg: value in device configuration
@@ -2554,6 +2749,187 @@ static int yt8521_get_features(struct phy_device *phydev)
 	return ret;
 }
 
+static int yt8831_config_aneg(struct phy_device *phydev)
+{
+	int ret;
+
+	ret = genphy_config_aneg(phydev);
+	if (ret < 0)
+		return ret;
+
+	return genphy_c45_config_aneg(phydev);
+}
+
+/**
+ * yt8831_serdes_init() - serdes init
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8831_serdes_init(struct phy_device *phydev)
+{
+	int old_page = YT8831_RSSR_UTP_SAPCE;
+	int ret = -EINVAL;
+
+
+	old_page = phy_select_page(phydev, YT8831_RSSR_FIBER_SAPCE);
+	if (old_page < 0) {
+		phydev_err(phydev, "Failed to select page: %d\n",
+				old_page);
+		goto err_restore_page;
+	}
+
+	ret = ytphy_write_ext(phydev, YT8831_SDS_WRITE_PROTECT_REG, 0x1);
+	if (ret < 0)
+		goto err_restore_page;
+
+	ret = ytphy_write_ext(phydev, YT8831_SDS_SGMII_OUTPUT_REG, 0xc4);
+	if (ret < 0)
+		goto err_restore_page;
+
+	/* softreset sds space */
+	ret = ytphy_modify_ext(phydev, MII_BMCR, BMCR_RESET, BMCR_RESET);
+	if (ret < 0)
+		goto err_restore_page;
+
+
+err_restore_page:
+	return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8831_utp_init() - utp init
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8831_utp_init(struct phy_device *phydev)
+{
+	int ret = -EINVAL;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_WRITE_PROTECT_REG, 0x3);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_DAC_DATA_DLY_REG, 0xc8d);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_REF_DLAY_SEL_REG, 0xa426);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_REF_DLAY_SEL_FEXT_REG, 0x8009);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_COE_MANUAL_SHIFT_SEL_2500_REG, 6);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_EEE_OPTIM_1_REG, 0x1001);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_EEE_OPTIM_2_REG, 0x7c52);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_EEE_MSE1_REG, 0x3848);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_EEE_MSE2_REG, 0xa8e8);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_EEE_OPTIM_3_REG, 0x5200);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_EEE_OPTIM_4_REG, 0x5849);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_EEE_OPTIM_5_REG, 0xb00a);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_EEE_OPTIM_6_REG, 0x3022);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_PSAN_OPTIM_REG, 0x1f10);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_1000M_TEMPLATE_OPTIM_1_REG, 0x22c8);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_1000M_TEMPLATE_OPTIM_2_REG, 0x2437);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_1000M_TEMPLATE_OPTIM_3_REG, 0x22c8);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_1000M_TEMPLATE_OPTIM_4_REG, 0x2437);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_1000M_TEMPLATE_OPTIM_5_REG, 0x22c8);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_1000M_TEMPLATE_OPTIM_6_REG, 0x2437);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_1000M_TEMPLATE_OPTIM_7_REG, 0x22c8);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_1000M_TEMPLATE_OPTIM_8_REG, 0x2437);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_2500M_TEMPLATE_OPTIM_1_REG, 0x1111);
+	if (ret < 0)
+		goto err;
+
+	ret = ytphy_write_ext_with_lock(phydev, YT8831_UTP_2500M_TEMPLATE_OPTIM_2_REG, 0x1111);
+	if (ret < 0)
+		goto err;
+
+err:
+	return ret;
+}
+
+/**
+ * yt8831_get_rate_matching - read register to get phy chip mode
+ * @phydev: target phy_device struct
+ * @iface: PHY data interface type
+ *
+ * Returns: rate matching type or negative errno code
+ */
+static int yt8831_get_rate_matching(struct phy_device *phydev,
+				    phy_interface_t iface)
+{
+	int val;
+
+	val = ytphy_read_ext_with_lock(phydev, YT8831_CHIP_MODE_SEL);
+	if (val < 0)
+		return val;
+
+	if (FIELD_GET(YT8831_CHIP_MODE_SEL_MASK, val) ==
+	    YT8831_CHIP_MODE_FORCE_2500BASEX)
+		return RATE_MATCH_PAUSE;
+
+	return RATE_MATCH_NONE;
+}
+
 /**
  * yt861x_config_init() - apply the auto sleep setting to the PHY
  * @phydev: a pointer to a &struct phy_device
@@ -2992,6 +3368,67 @@ static int yt8821_soft_reset(struct phy_device *phydev)
 					  YT8521_CCR_SW_RST, 0);
 }
 
+/**
+ * yt8831_config_init() - phy initializatioin
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8831_config_init(struct phy_device *phydev)
+{
+
+	u8 mode = YT8831_CHIP_MODE_AUTO_BX2500_SGMII;
+	int ret;
+
+
+	__set_bit(PHY_INTERFACE_MODE_2500BASEX,
+			phydev->possible_interfaces);
+
+
+	__set_bit(PHY_INTERFACE_MODE_SGMII,
+			phydev->possible_interfaces);
+
+	switch (phydev->interface) {
+	case PHY_INTERFACE_MODE_2500BASEX:
+		mode = YT8831_CHIP_MODE_FORCE_2500BASEX;
+		phydev->rate_matching = RATE_MATCH_PAUSE;
+		break;
+	case PHY_INTERFACE_MODE_SGMII:
+		mode = YT8831_CHIP_MODE_AUTO_BX2500_SGMII;
+		phydev->rate_matching = RATE_MATCH_NONE;
+		break;
+	default:
+		mode = YT8831_CHIP_MODE_AUTO_BX2500_SGMII;
+		phydev->rate_matching = RATE_MATCH_NONE;
+		break;
+	}
+
+	ret = ytphy_modify_ext_with_lock(phydev,
+					 YT8831_CHIP_MODE_SEL,
+					 YT8831_CHIP_MODE_SEL_MASK,
+					 mode);
+	if (ret < 0)
+		return ret;
+
+	/* Disable broadcast*/
+	ret = ytphy_modify_ext_with_lock(phydev,
+					 YT8831_MDIO_ADDRESS_CTL,
+					 YT8831_MDIO_ADDRESS_CTL_EN_BROADCAST_MASK,
+					 0);
+	if (ret < 0)
+		return ret;
+
+	ret = yt8831_serdes_init(phydev);
+	if (ret < 0)
+		return ret;
+
+	ret = yt8831_utp_init(phydev);
+	if (ret < 0)
+		return ret;
+
+	return genphy_soft_reset(phydev);
+}
+
 /**
  * yt8821_config_init() - phy initializatioin
  * @phydev: a pointer to a &struct phy_device
@@ -3102,6 +3539,50 @@ static void yt8821_update_interface(struct phy_device *phydev)
 	}
 }
 
+/**
+ * yt8831_read_status() -  determines the negotiated speed and duplex
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8831_read_status(struct phy_device *phydev)
+{
+	int ret;
+	int val;
+
+	phydev->speed = SPEED_UNKNOWN;
+	phydev->duplex = DUPLEX_UNKNOWN;
+
+	ret = genphy_read_status(phydev);
+	if (ret < 0)
+		return ret;
+
+	ret = phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
+	if (ret < 0)
+		return ret;
+
+	val = ret;
+	phydev->link = val & YTPHY_SSR_LINK;
+	if (phydev->link)
+		yt8821_adjust_status(phydev, val);
+
+	phydev_dbg(phydev,
+		"%s, phy addr: %d, link %s\n",
+		__func__, phydev->mdio.addr, phydev->link ? "up" : "down");
+
+
+	val = ytphy_read_ext_with_lock(phydev, YT8831_CHIP_MODE_SEL);
+	if (val < 0)
+		return val;
+
+	if (FIELD_GET(YT8831_CHIP_MODE_SEL_MASK, val) ==
+	    YT8831_CHIP_MODE_AUTO_BX2500_SGMII)
+		yt8821_update_interface(phydev);
+
+
+	return 0;
+}
+
 /**
  * yt8821_read_status() -  determines the negotiated speed and duplex
  * @phydev: a pointer to a &struct phy_device
@@ -3190,6 +3671,29 @@ static int yt8821_modify_utp_fiber_bmcr(struct phy_device *phydev,
 					mask, set);
 }
 
+/**
+ * yt8831_suspend() - suspend the hardware
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+
+static int yt8831_suspend(struct phy_device *phydev)
+{
+	struct ethtool_wolinfo wol = {0};
+	int ret;
+
+	yt8831_get_wol(phydev, &wol);
+
+	/* if wol enable, do nothing */
+	if (wol.wolopts & WAKE_MAGIC)
+		ret = 0;
+	else
+		ret = genphy_suspend(phydev);
+
+	return ret;
+}
+
 /**
  * yt8821_suspend() - suspend the hardware
  * @phydev: a pointer to a &struct phy_device
@@ -3212,6 +3716,29 @@ static int yt8821_suspend(struct phy_device *phydev)
 	return yt8821_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN);
 }
 
+/**
+ * yt8831_resume() - resume the hardware
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8831_resume(struct phy_device *phydev)
+{
+	struct ethtool_wolinfo wol = {0};
+	int ret;
+
+	yt8831_get_wol(phydev, &wol);
+
+	/* if wol enable, do nothing */
+	if (wol.wolopts & WAKE_MAGIC)
+		ret = 0;
+	else
+		ret = genphy_resume(phydev);
+
+	return ret;
+}
+
+
 /**
  * yt8821_resume() - resume the hardware
  * @phydev: a pointer to a &struct phy_device
@@ -3337,11 +3864,28 @@ static struct phy_driver motorcomm_phy_drvs[] = {
 		.suspend		= yt8821_suspend,
 		.resume			= yt8821_resume,
 	},
+	{
+		PHY_ID_MATCH_EXACT(PHY_ID_YT8831),
+		.name			= "YT8831/YT8821-VD 2.5Gbps PHY",
+		.get_features		= yt8821_get_features,
+		.read_page		= yt8831_read_page,
+		.write_page		= yt8831_write_page,
+		.get_wol		= yt8831_get_wol,
+		.set_wol		= yt8831_set_wol,
+		.config_aneg		= yt8831_config_aneg,
+		.aneg_done		= genphy_aneg_done,
+		.config_init		= yt8831_config_init,
+		.get_rate_matching	= yt8831_get_rate_matching,
+		.read_status		= yt8831_read_status,
+		.soft_reset		= genphy_soft_reset,
+		.suspend		= yt8831_suspend,
+		.resume			= yt8831_resume,
+	},
 };
 
 module_phy_driver(motorcomm_phy_drvs);
 
-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/861x/8821 PHY driver");
+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/861x/8821/8831 PHY driver");
 MODULE_AUTHOR("Peter Geis");
 MODULE_AUTHOR("Frank");
 MODULE_LICENSE("GPL");
-- 
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help