* [PATCH net-next v20 0/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
@ 2026-09-17 13:14 Kyle Switch
2026-09-17 13:14 ` [PATCH net-next v20 1/3] dt-bindings: net: Document Motorcomm YT8824 PHY package Kyle Switch
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Kyle Switch @ 2026-09-17 13:14 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, robh, krzk+dt,
conor+dt, andrew, hkallweit1, linux, Frank.Sae, netdev,
devicetree, linux-kernel
Cc: ming.xu, xiaolin.xu, jianmin.wang, jie.han
changes in v20:
patch 1: Add phy-mode description in properties in
motorcomm,yt8824.yaml
changes in v19:
patch 2: Replace the int type with the u16 type for test_mode.
patch 3: 1) Merge yt8824_utp_normal_test_mode_paged() and
yt8824_utp_invalid_test_mode_paged() into
yt8824_utp_set_template_test_mode().
2) Add a fatal error return when phy-mode is missing in
the DTS.
changes in v18:
1. Split the template_testmode() into a small patch
and optimize the implementation.
2. Optimize the definition and initialization of
interface_mode by adopting the existing approach.
changes in v17:
patch 1: Remove redundant description.
Use phy-mode instead of motorcomm,interface to describe the
interface.
Add the required checks.
patch 2: Use phy-mode to get interface mode.
Need return after init configuration is done to avoid falling
into the error path.
changes in v16:
1. Add DTS documentation.
2. Fix redundant initialization.
changes in v14:
1. Refactor the error return path to preserve and return the initial error,
rather than letting later errors mask it.
changes in v13:
1. Fix the lock release issue on the error path to prevent potential
deadlock or resource leak.
2. Add a func yt8824_restore_defaults() to restore configuration on
the error path,ensuring the hardware/device is left in a known
good state upon failure.
changes in v12:
1. Refactor the yt8824_read_status() function to ensure proper state
synchronization between hardware registers and the phy_device structure.
changes in v11:
1. Clean build_clang warning.
changes in v10:
1. Within the interface that swaps to the USXGMII reg space,the lock
include shared_lock and mdio mutex must remain held throughout
the entire operation and should only be released after all steps
have completed.
2. Improve the template interface in phy-c45.c
3. Fix the handling of some failure paths.
changes in v9:
1. Add shared_lock mutex prevents UTPs from interfering with each other
due to swapping reg space.
change in v8:
1. Clean up format warning.
2. Fix exception handling code logic based on Sashiko/Gemini.
3. Remove interrupt/handle function
changes in v7:
1. Refactor the using of mdio lock
In all cases of swapping to the USXGMII side interface, lock the MDIO bus
at the beginning, switch to the UTP address space after the operation is
completed, and then release the lock.The purpose of doing this is to
ensure that it will not affect other UTPs operating in the UTP address
space.
2. Rename YT8824_RSSR_FIBER_SPACE to YT8824_RSSR_USXGMII_SPACE.
changes in v6:
1. Add test mode helper in phy-c45.c
2. Refactor the using of swapping paged for utp and fiber
changes in v5:
1. Fix diffs issue which caused by unexpected whitespace.
2. Fix "exceeding 80 columns" warning.
changes in v4:
1. Remove motorcomm,yt8xxx.yaml, will update in other patch thread
2. Fix locking issue. Since every interface requires switching of space,
the bus is already locked during the space switching process in
phy_select_page(), other operations within the interface cannot
be locked again before unlock in phy_restore_page().
3. Fix warning log identified during the inspection process using
checkpatch.pl.
4. Fix the way of using common api in phy_package.c
changes in v3:
1. Using common apis defined in phy_package.c to handle shared top
extend register space.
2. Add dts demo in motorcomm,yt8xxx.yaml.
3. Fix unnecessary redundant judgments.
4. Fix BMCR registers operation using magic number.
5. Rename function based on its approximate functionality.
changes in v2:
1. Remove duplicate code and replace it with existing api.
Kyle Switch (3):
dt-bindings: net: Document Motorcomm YT8824 PHY package
net: phy: Add support for Template Control register for PMA
net: phy: Add driver for Motorcomm Quad 2.5GbE phy
.../bindings/net/motorcomm,yt8824.yaml | 62 +
drivers/net/phy/Kconfig | 3 +-
drivers/net/phy/motorcomm.c | 1575 ++++++++++++++++-
drivers/net/phy/phy-c45.c | 23 +
include/linux/phy.h | 1 +
include/uapi/linux/mdio.h | 12 +
6 files changed, 1673 insertions(+), 3 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v20 1/3] dt-bindings: net: Document Motorcomm YT8824 PHY package
2026-09-17 13:14 [PATCH net-next v20 0/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy Kyle Switch
@ 2026-09-17 13:14 ` Kyle Switch
2026-09-21 13:17 ` netdev-bot+sashiko
2026-09-17 13:14 ` [PATCH net-next v20 2/3] net: phy: Add support for Template Control register for PMA Kyle Switch
2026-09-17 13:14 ` [PATCH net-next v20 3/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy Kyle Switch
2 siblings, 1 reply; 7+ messages in thread
From: Kyle Switch @ 2026-09-17 13:14 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, robh, krzk+dt,
conor+dt, andrew, hkallweit1, linux, Frank.Sae, netdev,
devicetree, linux-kernel
Cc: ming.xu, xiaolin.xu, jianmin.wang, jie.han
Motorcomm YT8824 Ethernet PHY is PHY package of 4 PHY-s.
Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
---
.../bindings/net/motorcomm,yt8824.yaml | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml
diff --git a/Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml b/Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml
new file mode 100644
index 000000000000..9737364744a0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/motorcomm,yt8824.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MotorComm YT8824 Ethernet PHY
+
+maintainers:
+ - Kyle Switch <kyle.switch@motor-comm.com>
+
+description:
+ Motorcomm YT8824 Ethernet PHY is a PHY package of 4 PHYs.
+
+$ref: ethernet-phy-package.yaml#
+
+properties:
+ compatible:
+ enum:
+ - motorcomm,yt8824-package
+ phy-mode:
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [ internal, usxgmii ]
+
+required:
+ - compatible
+ - phy-mode
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-phy-package@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "motorcomm,yt8824-package";
+ reg = <9>;
+
+ phy-mode = "internal";
+
+ ethernet-phy@4 {
+ reg = <4>;
+ };
+
+ ethernet-phy@5 {
+ reg = <5>;
+ };
+
+ ethernet-phy@6 {
+ reg = <6>;
+ };
+
+ ethernet-phy@7 {
+ reg = <7>;
+ };
+ };
+ };
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v20 2/3] net: phy: Add support for Template Control register for PMA
2026-09-17 13:14 [PATCH net-next v20 0/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy Kyle Switch
2026-09-17 13:14 ` [PATCH net-next v20 1/3] dt-bindings: net: Document Motorcomm YT8824 PHY package Kyle Switch
@ 2026-09-17 13:14 ` Kyle Switch
2026-09-21 13:17 ` netdev-bot+sashiko
2026-09-17 13:14 ` [PATCH net-next v20 3/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy Kyle Switch
2 siblings, 1 reply; 7+ messages in thread
From: Kyle Switch @ 2026-09-17 13:14 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, robh, krzk+dt,
conor+dt, andrew, hkallweit1, linux, Frank.Sae, netdev,
devicetree, linux-kernel
Cc: ming.xu, xiaolin.xu, jianmin.wang, jie.han
Add support for the 10GBASE-T PMA Template Test Mode register
field, which allows selecting one of eight test modes (Normal and
TestMode1..TestMode7) used for PHY validation.
Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
---
drivers/net/phy/phy-c45.c | 23 +++++++++++++++++++++++
include/linux/phy.h | 1 +
include/uapi/linux/mdio.h | 12 ++++++++++++
3 files changed, 36 insertions(+)
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 870920311f9a..c5f5753f7194 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -1408,6 +1408,29 @@ int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable)
}
EXPORT_SYMBOL_GPL(genphy_c45_fast_retrain);
+/**
+ * genphy_c45_template_testmode - configure template testmode registers
+ * @phydev: target phy_device struct
+ * @test_mode: testmode includes Normal to Test mode 7
+ *
+ * Description: Set template testmode include Normal to Test mode 7
+ *
+ * Return: 0 on success, or a negative error code on failure (e.g. register
+ * read/write error).
+ */
+int genphy_c45_template_testmode(struct phy_device *phydev, u16 test_mode)
+{
+ u16 ctrl;
+
+ if (test_mode > MDIO_PMA_10GBT_TESTMODE_7)
+ return -EOPNOTSUPP;
+
+ ctrl = FIELD_PREP(MDIO_PMA_10GBT_TESTMODE_MASK, test_mode);
+ return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TESTMODE,
+ MDIO_PMA_10GBT_TESTMODE_MASK, ctrl);
+}
+EXPORT_SYMBOL_GPL(genphy_c45_template_testmode);
+
/**
* genphy_c45_plca_get_cfg - get PLCA configuration from standard registers
* @phydev: target phy_device struct
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 3d8afe6b7f1c..2db804b8fd4d 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -2357,6 +2357,7 @@ int genphy_c45_loopback(struct phy_device *phydev, bool enable, int speed);
int genphy_c45_pma_resume(struct phy_device *phydev);
int genphy_c45_pma_suspend(struct phy_device *phydev);
int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable);
+int genphy_c45_template_testmode(struct phy_device *phydev, u16 test_mode);
int genphy_c45_plca_get_cfg(struct phy_device *phydev,
struct phy_plca_cfg *plca_cfg);
int genphy_c45_plca_set_cfg(struct phy_device *phydev,
diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h
index 06f4bc3c20c7..353ca13c64fc 100644
--- a/include/uapi/linux/mdio.h
+++ b/include/uapi/linux/mdio.h
@@ -63,6 +63,7 @@
/* Media-dependent registers. */
#define MDIO_PMA_10GBT_SWAPPOL 130 /* 10GBASE-T pair swap & polarity */
#define MDIO_PMA_10GBT_TXPWR 131 /* 10GBASE-T TX power control */
+#define MDIO_PMA_10GBT_TESTMODE 132 /* Test mode control */
#define MDIO_PMA_10GBT_SNR 133 /* 10GBASE-T SNR margin, lane A.
* Lanes B-D are numbered 134-136. */
#define MDIO_PMA_10GBR_FSRT_CSR 147 /* 10GBASE-R fast retrain status and control */
@@ -320,6 +321,17 @@
/* PMA 10GBASE-R Fast Retrain status and control register. */
#define MDIO_PMA_10GBR_FSRT_ENABLE 0x0001 /* Fast retrain enable */
+/* PMA 10GBASE-T Template Test Mode Register*/
+#define MDIO_PMA_10GBT_TESTMODE_MASK GENMASK(15, 13) /* Template test mode */
+#define MDIO_PMA_10GBT_TESTMODE_NORMAL 0x0 /* Template Normal */
+#define MDIO_PMA_10GBT_TESTMODE_1 0x1 /* Template TestMode1 */
+#define MDIO_PMA_10GBT_TESTMODE_2 0x2 /* Template TestMode2 */
+#define MDIO_PMA_10GBT_TESTMODE_3 0x3 /* Template TestMode3 */
+#define MDIO_PMA_10GBT_TESTMODE_4 0x4 /* Template TestMode4 */
+#define MDIO_PMA_10GBT_TESTMODE_5 0x5 /* Template TestMode5 */
+#define MDIO_PMA_10GBT_TESTMODE_6 0x6 /* Template TestMode6 */
+#define MDIO_PMA_10GBT_TESTMODE_7 0x7 /* Template TestMode7 */
+
/* PCS 10GBASE-R/-T status register 1. */
#define MDIO_PCS_10GBRT_STAT1_BLKLK 0x0001 /* Block lock attained */
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net-next v20 3/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
2026-09-17 13:14 [PATCH net-next v20 0/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy Kyle Switch
2026-09-17 13:14 ` [PATCH net-next v20 1/3] dt-bindings: net: Document Motorcomm YT8824 PHY package Kyle Switch
2026-09-17 13:14 ` [PATCH net-next v20 2/3] net: phy: Add support for Template Control register for PMA Kyle Switch
@ 2026-09-17 13:14 ` Kyle Switch
2026-09-21 13:17 ` netdev-bot+sashiko
2 siblings, 1 reply; 7+ messages in thread
From: Kyle Switch @ 2026-09-17 13:14 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni, robh, krzk+dt,
conor+dt, andrew, hkallweit1, linux, Frank.Sae, netdev,
devicetree, linux-kernel
Cc: ming.xu, xiaolin.xu, jianmin.wang, jie.han
Add support for Motorcomm YT8824 quad-port 2.5G PHY to the existing
motorcomm driver, using the phy_package helpers for the shared top
extended register space.
Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
---
drivers/net/phy/Kconfig | 3 +-
drivers/net/phy/motorcomm.c | 1575 ++++++++++++++++++++++++++++++++++-
2 files changed, 1575 insertions(+), 3 deletions(-)
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index d3835597e379..996d75afed44 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -361,9 +361,10 @@ config MICROSEMI_PHY
config MOTORCOMM_PHY
tristate "Motorcomm PHYs"
+ select PHY_PACKAGE
help
Enables support for Motorcomm network PHYs.
- Currently supports YT85xx Gigabit Ethernet PHYs.
+ Currently supports YT85xx Gigabit Ethernet PHYs and YT8824 4 * 2.5G PHY.
config NATIONAL_PHY
tristate "National Semiconductor PHYs"
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index 90a4f86f2758..7ab09666ab3f 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -1,24 +1,31 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Motorcomm 8511/8521/8522/8531/8531S/8821 PHY driver.
+ * Motorcomm 8511/8521/8522/8531/8531S/8821/8824 PHY driver.
*
* Author: Peter Geis <pgwipeout@gmail.com>
* Author: Frank <Frank.Sae@motor-comm.com>
+ * Author: Kyle <kyle.switch@motor-comm.com>
*/
#include <linux/clk.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
+#include <linux/mdio.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/property.h>
+#include "phylib.h"
+
#define PHY_ID_YT8511 0x0000010a
#define PHY_ID_YT8521 0x0000011a
#define PHY_ID_YT8522 0x4f51e928
#define PHY_ID_YT8531 0x4f51e91b
#define PHY_ID_YT8531S 0x4f51e91a
#define PHY_ID_YT8821 0x4f51ea19
+#define PHY_ID_YT8824 0x4f51e8b8
/* YT8521/YT8531S/YT8821 Register Overview
* UTP Register space | FIBER Register space
* ------------------------------------------------------------
@@ -30,6 +37,18 @@
* ------------------------------------------------------------
*/
+/* YT8824 Register Overview
+ * UTP Register space | USXGMII Register space
+ * ------------------------------------------------------------
+ * | UTP MII | USXGMII MII |
+ * | UTP MMD | |
+ * | UTP Extended | USXGMII Extended |
+ * | UTP Top Extended | USXGMII Top Extended |
+ * ------------------------------------------------------------
+ * | Common Top Extended |
+ * ------------------------------------------------------------
+ */
+
/* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */
/* Specific Function Control Register */
@@ -381,6 +400,13 @@
#define YT8821_CHIP_MODE_AUTO_BX2500_SGMII 0
#define YT8821_CHIP_MODE_FORCE_BX2500 1
+#define YT8824_RSSR_SPACE_MASK BIT(0)
+#define YT8824_RSSR_USXGMII_SPACE (0x1)
+#define YT8824_RSSR_UTP_SPACE (0x0)
+#define YT8824_SDS_CFG_MIN_PRE_MASK GENMASK(3, 0)
+#define YT8824_SDS_EN_FILL_PRE BIT(13)
+#define YT8824_SDS_TX_PRE_PADDING (0x7)
+
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
@@ -399,6 +425,12 @@ struct yt8521_priv {
u8 reg_page;
};
+struct yt8824_shared_priv {
+ phy_interface_t interface_mode;
+ /* shared_lock used to UTPs operation isolation during swap reg space */
+ struct mutex shared_lock;
+};
+
/**
* ytphy_read_ext() - read a PHY's extended register
* @phydev: a pointer to a &struct phy_device
@@ -437,6 +469,78 @@ static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum)
return ret;
}
+/**
+ * ytphy_read_top_ext() - read a PHY's top extended register for YT8824
+ * @phydev: a pointer to a &struct phy_device
+ * @regnum: register number to read
+ *
+ * Returns: the value of regnum reg or negative error code
+ */
+static int ytphy_read_top_ext(struct phy_device *phydev, u16 regnum)
+{
+ int ret;
+
+ /*
+ * The package base address is used here to read the top ext registers.
+ * For example, in internal mode, the top ext address is fixed at 9;
+ * for usxgmii, the offset is 4. Instead of using the offset approach
+ * a fixed address is used here to avoid situations such as when phy0
+ * is not in use, where the top ext register address cannot be obtained
+ * correctly.
+ */
+ lockdep_assert_held(&phydev->mdio.bus->mdio_lock);
+ ret = __phy_package_write(phydev, 0, YTPHY_PAGE_SELECT, regnum);
+ if (ret < 0)
+ return ret;
+
+ return __phy_package_read(phydev, 0, YTPHY_PAGE_DATA);
+}
+
+/**
+ * ytphy_write_top_ext() - write a PHY's top extended register for YT8824
+ * @phydev: a pointer to a &struct phy_device
+ * @regnum: register number to write
+ * @val: register val to write
+ *
+ * Returns: 0 or negative error code
+ */
+static int ytphy_write_top_ext(struct phy_device *phydev, u16 regnum,
+ u16 val)
+{
+ int ret;
+
+ lockdep_assert_held(&phydev->mdio.bus->mdio_lock);
+ ret = __phy_package_write(phydev, 0, YTPHY_PAGE_SELECT, regnum);
+ if (ret < 0)
+ return ret;
+
+ return __phy_package_write(phydev, 0, YTPHY_PAGE_DATA, val);
+}
+
+/**
+ * phy8824_page_write_with_lock() - write page for YT8824
+ * @phydev: a pointer to a &struct phy_device
+ * @page: reg page(YT8824_RSSR_USXGMII_SPACE/YT8824_RSSR_UTP_SPACE).
+ *
+ * Returns: 0 or negative error code
+ */
+static int phy8824_page_write_with_lock(struct phy_device *phydev, int page)
+{
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+ ret = ytphy_read_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
+ if (ret < 0)
+ goto err;
+ ret &= ~YT8824_RSSR_SPACE_MASK;
+ ret |= (page & YT8824_RSSR_SPACE_MASK);
+ ret = ytphy_write_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG, ret);
+
+err:
+ phy_unlock_mdio_bus(phydev);
+ return ret;
+}
+
/**
* ytphy_write_ext() - write a PHY's extended register
* @phydev: a pointer to a &struct phy_device
@@ -633,6 +737,1036 @@ static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
return phy_restore_page(phydev, old_page, ret);
}
+/**
+ * yt8824_read_page() - read PHY8824 reg page
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: current reg space of yt8824 (YT8824_RSSR_USXGMII_SPACE/
+ * YT8824_RSSR_UTP_SPACE) or negative errno code
+ */
+static int yt8824_read_page(struct phy_device *phydev)
+{
+ int old_page;
+
+ old_page = ytphy_read_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
+ if (old_page < 0)
+ return old_page;
+
+ return old_page & YT8824_RSSR_SPACE_MASK;
+};
+
+/**
+ * yt8824_write_page() - write reg page
+ * @phydev: a pointer to a &struct phy_device
+ * @page: Reg page(YT8824_RSSR_USXGMII_SPACE/YT8824_RSSR_UTP_SPACE) to write.
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_write_page(struct phy_device *phydev, int page)
+{
+ int old_page;
+ u16 data;
+
+ old_page = ytphy_read_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
+ if (old_page < 0)
+ return old_page;
+ data = old_page & (~YT8824_RSSR_SPACE_MASK);
+ data |= page;
+
+ return ytphy_write_top_ext(phydev, YT8521_REG_SPACE_SELECT_REG, data);
+};
+
+/**
+ * yt8824_utp_set_template_test_mode() - config YT8824 UTP test mode.
+ * @phydev: a pointer to a &struct phy_device
+ * @test_mode: template test mode from normal, testmode1 to testmode7
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_set_template_test_mode(struct phy_device *phydev,
+ u16 test_mode)
+{
+ int ret;
+
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ return genphy_c45_template_testmode(phydev, test_mode);
+}
+
+/**
+ * yt8824_sds_isolate_paged() - enable YT8824 serdes isolate.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_sds_isolate_paged(struct phy_device *phydev)
+{
+ int old_page = YT8824_RSSR_UTP_SPACE;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_USXGMII_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ /* enable sds isolate */
+ ret = __phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, BMCR_ISOLATE);
+
+err_restore_page:
+ /* restore page, release the lock */
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_utp_softreset_paged() - config YT8824 UTP softreset.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_softreset_paged(struct phy_device *phydev)
+{
+ int ret = 0;
+ int val;
+
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ ret = phy_modify(phydev, MII_BMCR, BMCR_RESET, BMCR_RESET);
+ if (ret < 0)
+ return ret;
+ /* wait until softreset done. */
+ return phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+ 50000, 600000, true);
+}
+
+/**
+ * yt8824_sds_isolate_and_softreset_paged() - disable YT8824 serdes isolate
+ * and sds softreset.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_sds_isolate_and_softreset_paged(struct phy_device *phydev)
+{
+ int old_page = YT8824_RSSR_UTP_SPACE;
+ int val = 0;
+ int ret = -1;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_USXGMII_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ /* sds softreset and disable isolate */
+ ret = __phy_modify(phydev, MII_BMCR, BMCR_RESET | BMCR_ISOLATE,
+ BMCR_RESET & ~BMCR_ISOLATE);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* poll while still holding the lock */
+ ret = read_poll_timeout(__phy_read, val,
+ (val < 0) || !(val & BMCR_RESET), 50000, 600000,
+ true, phydev, MII_BMCR);
+ if (val < 0)
+ ret = val;
+
+err_restore_page:
+ /* restore page, release the lock */
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_restore_working_status() - called to do store working status
+ * @phydev: a pointer to a &struct phy_device
+ * @ret: operation's return code
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_restore_working_status(struct phy_device *phydev, int ret)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int r;
+
+ /* configure normal test mode */
+ r = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ if (priv->interface_mode != PHY_INTERFACE_MODE_INTERNAL) {
+ /* sds soft reset and disable isolation */
+ r = yt8824_sds_isolate_and_softreset_paged(phydev);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ }
+
+ return ret;
+}
+
+/**
+ * yt8824_soft_reset() - called to do PHY software reset
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_soft_reset(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int ret;
+
+ mutex_lock(&priv->shared_lock);
+ if (priv->interface_mode == PHY_INTERFACE_MODE_INTERNAL) {
+ /* test mode 1 */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_1);
+ if (ret < 0)
+ goto retry;
+ ret = yt8824_utp_softreset_paged(phydev);
+ if (ret < 0)
+ goto retry;
+ /* normal mode */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret < 0)
+ goto retry;
+ } else {
+ /* test mode 1 */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_1);
+ if (ret < 0)
+ goto retry;
+
+ /* sds isolation */
+ ret = yt8824_sds_isolate_paged(phydev);
+ if (ret < 0)
+ goto retry;
+
+ /* utp soft reset */
+ ret = yt8824_utp_softreset_paged(phydev);
+ if (ret < 0)
+ goto retry;
+
+ /* normal mode */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret < 0)
+ goto retry;
+
+ /* sds soft reset and disable isolation */
+ ret = yt8824_sds_isolate_and_softreset_paged(phydev);
+ if (ret < 0)
+ goto retry;
+ }
+ mutex_unlock(&priv->shared_lock);
+ return ret;
+retry:
+ ret = yt8824_restore_working_status(phydev, ret);
+ mutex_unlock(&priv->shared_lock);
+
+ return ret;
+}
+
+/**
+ * yt8824_extern_config_utp_init_paged() - config external phy8824 utp init
+ * @phydev: target phy_device struct
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_extern_config_utp_init_paged(struct phy_device *phydev)
+{
+ int ret = 0;
+ int val = 0;
+ int r;
+
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* power down */
+ ret = phy_modify(phydev, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN);
+ if (ret < 0)
+ goto err_restore;
+
+ /* pll calibration */
+ ret = ytphy_write_ext_with_lock(phydev, 0x0001, 0x0003);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa20e, 0x0cba);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa20a, 0xc3f1);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa20c, 0x1620);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2b6, 0x0a00);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2b6, 0x0e00);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimization utp */
+ ret = ytphy_write_ext_with_lock(phydev, 0x0001, 0x0003);
+ if (ret < 0)
+ goto err_restore;
+
+ /* enable nibble */
+ ret = ytphy_write_ext_with_lock(phydev, 0xa003, 0x0003);
+ if (ret < 0)
+ goto err_restore;
+
+ /* idle err detect enable */
+ ret = ytphy_write_ext_with_lock(phydev, 0x03d0, 0x5210);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimized 2.5G long cable performance */
+ ret = ytphy_write_ext_with_lock(phydev, 0x0372, 0x5038);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x037c, 0x6068);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0388, 0x00a0);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimized fast retrain */
+ ret = ytphy_write_ext_with_lock(phydev, 0x0359, 0x2140);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x000c, 0xc1a0);
+ if (ret < 0)
+ goto err_restore;
+
+ /* 2.5G template tone */
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2fa, 0x0083);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x04e2, 0x0149);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimized 2.5G template */
+ ret = ytphy_write_ext_with_lock(phydev, 0x047e, 0x3939);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x047f, 0x3939);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0480, 0x3939);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0481, 0x3939);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimized 1000M cable length threshold */
+ ret = ytphy_write_ext_with_lock(phydev, 0x0336, 0xab0a);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0340, 0x301d);
+ if (ret < 0)
+ goto err_restore;
+
+ /* 100M template amplitude */
+ ret = ytphy_write_ext_with_lock(phydev, 0x046e, 0x4545);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x046f, 0x4545);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0470, 0x4545);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0471, 0x4545);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimized 100M cable length threshold */
+ ret = ytphy_write_ext_with_lock(phydev, 0x030b, 0xaa1d);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x071f, 0x0036);
+ if (ret < 0)
+ goto err_restore;
+
+ /* 10M template amplitude */
+ ret = ytphy_write_ext_with_lock(phydev, 0x046b, 0x1818);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x046c, 0x1818);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimized 10M cable length threshold */
+ ret = ytphy_write_ext_with_lock(phydev, 0x0466, 0x6c6c);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0467, 0x6c6c);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0468, 0x6c6c);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0469, 0x6c6c);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimize utp 1000M performance */
+ ret = ytphy_write_ext_with_lock(phydev, 0x034a, 0xff03);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x00f8, 0xb3ff);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0059, 0x4040);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x032c, 0x5094);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x032d, 0xd094);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x032e, 0x5308);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x0322, 0x6440);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x04d3, 0x5220);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x04d2, 0x5220);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimized EMC CS */
+ ret = ytphy_write_ext_with_lock(phydev, 0x00c8, 0xffff);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x00be, 0x6406);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x037a, 0x40ff);
+ if (ret < 0)
+ goto err_restore;
+
+ /* optimized EMC RE */
+ ret = ytphy_write_ext_with_lock(phydev, 0x0482, 0xffff);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2d5, 0x1f1f);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2d6, 0x1f1f);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2d7, 0x1f1f);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2d8, 0x1f1f);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa218, 0x006e);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa01d, 0xfff0);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa01e, 0xfff0);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa01d, 0xffff);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0xa01e, 0xffff);
+ if (ret < 0)
+ goto err_restore;
+
+ ret = genphy_c45_template_testmode(phydev, MDIO_PMA_10GBT_TESTMODE_1);
+ if (ret < 0)
+ goto err_restore_normal;
+ /* reset */
+ ret = phy_modify(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE,
+ BMCR_RESET | BMCR_ANENABLE);
+ if (ret < 0)
+ goto err_restore_normal;
+ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+ 50000, 600000, true);
+ if (ret < 0)
+ goto err_restore_normal;
+
+ ret = genphy_c45_template_testmode(phydev,
+ MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret < 0)
+ goto err_restore_normal;
+ return 0;
+
+err_restore:
+ r = phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ return ret;
+
+err_restore_normal:
+ r = genphy_c45_template_testmode(phydev,
+ MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ r = phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ return ret;
+}
+
+/**
+ * yt8824_extern_config_sds_init_paged() - config external phy8824 sds init
+ * @phydev: target phy_device struct
+ *
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_extern_config_sds_init_paged(struct phy_device *phydev)
+{
+ int old_page = YT8824_RSSR_UTP_SPACE;
+ int val_1, val_2, val_3, tmp;
+ int ret = -1;
+ int val;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_USXGMII_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ /* read efuse */
+ ret = ytphy_read_top_ext(phydev, 0xa13e);
+ if (ret < 0)
+ goto err_restore_page;
+ else
+ val_1 = ret;
+
+ ret = ytphy_read_top_ext(phydev, 0xa13f);
+ if (ret < 0)
+ goto err_restore_page;
+ else
+ val_2 = ret;
+
+ ret = ytphy_read_top_ext(phydev, 0xa140);
+ if (ret < 0)
+ goto err_restore_page;
+ else
+ val_3 = ret;
+
+ /* Serdes optimization */
+ ret = ytphy_write_ext(phydev, 0x04be, 0x000d);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x049f, 0x7ded);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04a9, 0x009f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* analog CDR */
+ ret = ytphy_write_ext(phydev, 0x0406, 0x0800);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized VCO */
+ ret = ytphy_write_ext(phydev, 0x0438, 0x9024);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0439, 0x00c0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized PLL lock */
+ ret = ytphy_read_ext(phydev, 0x0429);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(13) | BIT(12));
+ tmp = (val_1 & (BIT(7) | BIT(6))) >> 6;
+ ret |= (tmp << 12);
+ ret = ytphy_write_ext(phydev, 0x0429, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_ext(phydev, 0x0441);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(1) | BIT(0));
+ tmp = (val_1 & (BIT(5) | BIT(4))) >> 4;
+ ret |= tmp;
+ ret = ytphy_write_ext(phydev, 0x0441, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_ext(phydev, 0x042b);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(13) | BIT(12));
+ tmp = (val_3 & (BIT(1) | BIT(0)));
+ ret |= (tmp << 12);
+ ret = ytphy_write_ext(phydev, 0x042b, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x043a, 0x1006);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x042a, 0xf070);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x0491, 0x007f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0492, 0x7f7f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Serdes training threshold */
+ ret = ytphy_write_ext(phydev, 0x0454, 0x0f14);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0497, 0x0a44);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* digital eye diagram of SerDes */
+ ret = ytphy_write_ext(phydev, 0x04cd, 0x0000);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Serdes LDO */
+ ret = ytphy_read_ext(phydev, 0x04b5);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(6) | BIT(5) | BIT(4));
+ tmp = (val_2 & (BIT(4) | BIT(3) | BIT(2))) >> 2;
+ ret |= (tmp << 4);
+ ret = ytphy_write_ext(phydev, 0x04b5, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_ext(phydev, 0x04b4);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(10) | BIT(9) | BIT(8));
+ tmp = (val_2 & (BIT(7) | BIT(6) | BIT(5))) >> 5;
+ ret |= (tmp << 8);
+ ret = ytphy_write_ext(phydev, 0x04b4, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized Serdes RX */
+ ret = ytphy_write_ext(phydev, 0x04af, 0x45e3);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x048a, 0x0fff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0408, 0x7c00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04d6, 0x007f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x044f, 0xff08);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized Serdes TX */
+ ret = ytphy_write_ext(phydev, 0x048e, 0x7d00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x000d, 0x0606);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Serdes manual config */
+ ret = ytphy_write_ext(phydev, 0x04b0, 0x0804);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04b1, 0x7074);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04af, 0x45e7);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* restart calibration */
+ ret = ytphy_write_ext(phydev, 0x0003, 0x5603);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0492, 0x7fff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0492, 0x7f7f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x2000, 0x0040);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x2000, 0x0000);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* TX preamble padded to 8; RX IPG always > 8 */
+ ret = __phy_read(phydev, MII_RESV1);
+ if (ret < 0)
+ goto err_restore_page;
+ ret &= ~YT8824_SDS_CFG_MIN_PRE_MASK;
+ ret |= YT8824_SDS_TX_PRE_PADDING;
+ ret |= YT8824_SDS_EN_FILL_PRE;
+ ret = __phy_write(phydev, MII_RESV1, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ /* reset serdes */
+ ret = __phy_modify(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE,
+ BMCR_RESET | BMCR_ANENABLE);
+ if (ret < 0)
+ goto err_restore_page;
+ /* poll while still holding the lock; __phy_read takes no lock */
+ ret = read_poll_timeout(__phy_read, val,
+ (val < 0) || !(val & BMCR_RESET), 50000, 600000,
+ true, phydev, MII_BMCR);
+ if (val < 0)
+ ret = val;
+err_restore_page:
+ /* restore page, release the lock */
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_internal_config_init_paged() - config internal phy8824 init
+ * @phydev: target phy_device struct
+ *
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_internal_config_init_paged(struct phy_device *phydev)
+{
+ int ret = 0;
+ int val = 0;
+ int r = 0;
+
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ ret = ytphy_write_ext_with_lock(phydev, 0x1, 0x3);
+ if (ret < 0)
+ return ret;
+ /* power down */
+ ret = phy_modify(phydev, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa20e, 0xcba);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa20a, 0xc3f1);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa20c, 0x1620);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2b6, 0xa00);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2b6, 0xe00);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa003, 0x3);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x3d0, 0x5210);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x372, 0x5038);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x37c, 0x6068);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x388, 0xa0);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x359, 0x2140);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2fa, 0x83);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x4e2, 0x149);
+ if (ret < 0)
+ goto err_restore;
+ /* 2.5G tempate */
+ ret = ytphy_write_ext_with_lock(phydev, 0x47e, 0x3939);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x47f, 0x3939);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x480, 0x3939);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x481, 0x3939);
+ if (ret < 0)
+ goto err_restore;
+ /* 1000 cable length threshold */
+ ret = ytphy_write_ext_with_lock(phydev, 0x336, 0xab0a);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x340, 0x301d);
+ if (ret < 0)
+ goto err_restore;
+ /* 1000 performance */
+ ret = ytphy_write_ext_with_lock(phydev, 0x34a, 0xff03);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xf8, 0xb3ff);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x32c, 0x5094);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x32d, 0xd094);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x32e, 0x5308);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x322, 0x6440);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x4d3, 0x5220);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x4d2, 0x5220);
+ if (ret < 0)
+ goto err_restore;
+ /* 100 tempate */
+ ret = ytphy_write_ext_with_lock(phydev, 0x46e, 0x4545);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x46f, 0x4545);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x470, 0x4545);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x471, 0x4545);
+ if (ret < 0)
+ goto err_restore;
+ /* 100 cable length threshold */
+ ret = ytphy_write_ext_with_lock(phydev, 0x30b, 0xaa1d);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x71f, 0x36);
+ if (ret < 0)
+ goto err_restore;
+ /* 10 tempate */
+ ret = ytphy_write_ext_with_lock(phydev, 0x46b, 0x1818);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x46c, 0x1818);
+ if (ret < 0)
+ goto err_restore;
+ /* 10 tempate MAU*/
+ ret = ytphy_write_ext_with_lock(phydev, 0x466, 0x6c6c);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x467, 0x6c6c);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x468, 0x6c6c);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x469, 0x6c6c);
+ if (ret < 0)
+ goto err_restore;
+ /* EMC CS, Inconsistent with external phy */
+ ret = ytphy_write_ext_with_lock(phydev, 0xc8, 0xfff);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xbe, 0x6406);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0x37a, 0x40ff);
+ if (ret < 0)
+ goto err_restore;
+ /* EMC RE*/
+ ret = ytphy_write_ext_with_lock(phydev, 0x482, 0xffff);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2d5, 0x1f1f);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2d6, 0x1f1f);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2d7, 0x1f1f);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa2d8, 0x1f1f);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa218, 0x6e);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa01d, 0xfff0);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa01e, 0xfff0);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa01d, 0xffff);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xa01e, 0xffff);
+ if (ret < 0)
+ goto err_restore;
+ ret = ytphy_write_ext_with_lock(phydev, 0xc, 0x41a1);
+ if (ret < 0)
+ goto err_restore;
+ ret = genphy_c45_template_testmode(phydev, MDIO_PMA_10GBT_TESTMODE_1);
+ if (ret)
+ goto err_restore_normal;
+ /* reset */
+ ret = phy_modify(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE,
+ BMCR_RESET | BMCR_ANENABLE);
+ if (ret < 0)
+ goto err_restore_normal;
+ ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
+ 50000, 600000, true);
+ if (ret < 0)
+ goto err_restore_normal;
+
+ ret = genphy_c45_template_testmode(phydev,
+ MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret < 0)
+ goto err_restore_normal;
+
+ return 0;
+
+err_restore:
+ r = phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ return ret;
+
+err_restore_normal:
+ r = genphy_c45_template_testmode(phydev,
+ MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ r = phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ return ret;
+}
+
+/**
+ * yt8824_config_init() - phy initializatioin
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_init(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int ret;
+
+ mutex_lock(&priv->shared_lock);
+ if (priv->interface_mode == PHY_INTERFACE_MODE_INTERNAL) {
+ ret = yt8824_internal_config_init_paged(phydev);
+ if (ret < 0)
+ goto err;
+ } else {
+ ret = yt8824_extern_config_sds_init_paged(phydev);
+ if (ret < 0)
+ goto err;
+ ret = yt8824_extern_config_utp_init_paged(phydev);
+ if (ret < 0)
+ goto err;
+ }
+ mutex_unlock(&priv->shared_lock);
+ ret = yt8824_soft_reset(phydev);
+
+ phydev_dbg(phydev, "%s done, phy addr: %d\n", __func__,
+ phydev->mdio.addr);
+ return ret;
+err:
+ mutex_unlock(&priv->shared_lock);
+ return ret;
+}
+
static int yt8531_set_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
@@ -3104,6 +4238,426 @@ static int yt8821_resume(struct phy_device *phydev)
return yt8821_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
}
+/**
+ * yt8824_get_features - read mmd register to get 2.5G capability
+ * @phydev: target phy_device struct
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_get_features(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int ret;
+
+ mutex_lock(&priv->shared_lock);
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ goto err;
+ ret = yt8821_get_features(phydev);
+
+err:
+ mutex_unlock(&priv->shared_lock);
+ return ret;
+}
+
+/**
+ * yt8824_aneg_done() - check negotiation state.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: auto-negotiation complete status or negative errno code
+ */
+static int yt8824_aneg_done(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int auto_neg;
+ int ret;
+
+ mutex_lock(&priv->shared_lock);
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ goto err;
+
+ ret = phy_read(phydev, MII_BMSR);
+ if (ret < 0)
+ goto err;
+ mutex_unlock(&priv->shared_lock);
+ auto_neg = !!(ret & BMSR_ANEGCOMPLETE);
+
+ phydev_dbg(phydev, "%s, phy addr: %d, auto negotiation done: %d\n",
+ __func__, phydev->mdio.addr, auto_neg);
+ return auto_neg;
+err:
+ mutex_unlock(&priv->shared_lock);
+ return ret;
+}
+
+/**
+ * yt8824_read_status_paged() - determines the speed and duplex of one page
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_read_status_paged(struct phy_device *phydev)
+{
+ int link = 0;
+ int ret = 0;
+ int val = 0;
+
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ ret = genphy_read_status(phydev);
+ if (ret < 0)
+ return ret;
+
+ if (phydev->autoneg_complete) {
+ ret = genphy_c45_read_lpa(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
+ if (ret < 0)
+ return ret;
+
+ val = ret;
+
+ link = val & YTPHY_SSR_LINK;
+ if (link)
+ yt8821_adjust_status(phydev, val);
+
+ if (link) {
+ if (phydev->link == 0)
+ phydev_dbg(phydev, "%s, phy addr: %d, link up\n",
+ __func__, phydev->mdio.addr);
+ phydev->link = 1;
+ } else {
+ if (phydev->link == 1)
+ phydev_dbg(phydev, "%s, phy addr: %d, link down\n",
+ __func__, phydev->mdio.addr);
+ phydev->link = 0;
+ }
+ phy_resolve_aneg_pause(phydev);
+ return 0;
+}
+
+/**
+ * yt8824_read_status() - determines the negotiated speed and duplex
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_read_status(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int ret;
+
+ mutex_lock(&priv->shared_lock);
+ ret = yt8824_read_status_paged(phydev);
+ mutex_unlock(&priv->shared_lock);
+
+ return ret;
+}
+
+/**
+ * yt8824_utp_power_on(): utp power on.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_power_on(struct phy_device *phydev)
+{
+ int ret = 0;
+
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0x0);
+}
+
+/**
+ * yt8824_utp_power_down(): utp power down.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_utp_power_down(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ return phy_modify(phydev, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN);
+}
+
+/**
+ * yt8824_power_on() - set utp power on.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * NOTE: need WA like softreset
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_power_on(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int ret;
+ int r;
+
+ if (priv->interface_mode == PHY_INTERFACE_MODE_INTERNAL) {
+ /* test mode 1 */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_1);
+ if (ret < 0)
+ goto retry;
+ /* utp power on */
+ ret = yt8824_utp_power_on(phydev);
+ if (ret < 0)
+ goto retry;
+ /* normal mode */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret < 0)
+ goto retry;
+ } else {
+ /* test mode 1 */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_1);
+ if (ret < 0)
+ goto retry;
+
+ /* sds isolation */
+ ret = yt8824_sds_isolate_paged(phydev);
+ if (ret < 0)
+ goto retry;
+
+ /* utp power on */
+ ret = yt8824_utp_power_on(phydev);
+ if (ret < 0)
+ goto retry;
+
+ /* normal mode */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret < 0)
+ goto retry;
+
+ /* sds soft reset and disable isolation */
+ ret = yt8824_sds_isolate_and_softreset_paged(phydev);
+ if (ret < 0)
+ goto retry;
+ }
+ return 0;
+
+retry:
+ /*
+ * If the PHY up operation succeeds but the subsequent operation
+ * fails, revert to the default state.
+ */
+ r = yt8824_utp_power_down(phydev);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ ret = yt8824_restore_working_status(phydev, ret);
+ return ret;
+}
+
+/**
+ * yt8824_resume() - resume the hardware
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_resume(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int ret;
+
+ mutex_lock(&priv->shared_lock);
+ ret = yt8824_power_on(phydev);
+ mutex_unlock(&priv->shared_lock);
+
+ return ret;
+}
+
+/**
+ * yt8824_power_down() - set utp power down.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * NOTE: need WA like softreset
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_power_down(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int ret;
+ int r;
+
+ if (priv->interface_mode == PHY_INTERFACE_MODE_INTERNAL) {
+ /* test mode 1 */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_1);
+ if (ret < 0)
+ goto retry;
+ /* utp power down */
+ ret = yt8824_utp_power_down(phydev);
+ if (ret < 0)
+ goto retry;
+ /* normal mode */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret < 0)
+ goto retry;
+ } else {
+ /* test mode 1 */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_1);
+ if (ret < 0)
+ goto retry;
+
+ /* sds isolation */
+ ret = yt8824_sds_isolate_paged(phydev);
+ if (ret < 0)
+ goto retry;
+
+ /* utp power down */
+ ret = yt8824_utp_power_down(phydev);
+ if (ret < 0)
+ goto retry;
+
+ /* normal mode */
+ ret = yt8824_utp_set_template_test_mode
+ (phydev, MDIO_PMA_10GBT_TESTMODE_NORMAL);
+ if (ret < 0)
+ goto retry;
+
+ /* sds soft reset and disable isolation */
+ ret = yt8824_sds_isolate_and_softreset_paged(phydev);
+ if (ret < 0)
+ goto retry;
+ }
+ return 0;
+
+retry:
+ /*
+ * If the PHY down operation succeeds but the subsequent operation
+ * fails, revert to the default state.
+ */
+ r = yt8824_utp_power_on(phydev);
+ if (ret >= 0 && r < 0)
+ ret = r;
+ ret = yt8824_restore_working_status(phydev, ret);
+ return ret;
+}
+
+/**
+ * yt8824_suspend() - suspend the hardware
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_suspend(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int ret;
+
+ mutex_lock(&priv->shared_lock);
+ ret = yt8824_power_down(phydev);
+ mutex_unlock(&priv->shared_lock);
+
+ return ret;
+}
+
+/**
+ * yt8824_config_aneg() - config negotiation
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_aneg(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ int phy_ctrl = 0;
+ int ret;
+
+ mutex_lock(&priv->shared_lock);
+ ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ goto err;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ phydev->advertising))
+ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
+
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+ MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
+ if (ret < 0)
+ goto err;
+
+ ret = __genphy_config_aneg(phydev, ret);
+
+err:
+ mutex_unlock(&priv->shared_lock);
+ return ret;
+}
+
+/**
+ * yt8824_phy_package_probe_once() - init phy package for phy8824.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_phy_package_probe_once(struct phy_device *phydev)
+{
+ struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
+ struct device_node *np = phy_package_get_node(phydev);
+ int res;
+
+ if (!priv || !np) {
+ phydev_err(phydev, "%s: missing shared priv or DT node\n",
+ __func__);
+ return -EINVAL;
+ }
+ /* Initialise shared lock for YT8824 */
+ mutex_init(&priv->shared_lock);
+ res = of_get_phy_mode(np, &priv->interface_mode);
+ if (res < 0) {
+ phydev_warn(phydev, "%s, phy-mode missing in DTS.\n", __func__);
+ return res;
+ }
+
+ return 0;
+}
+
+/**
+ * yt8824_probe() - phy8824 probe.
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct yt8824_shared_priv *shared_priv;
+ int ret;
+
+ ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
+ if (ret)
+ return ret;
+
+ if (phy_package_probe_once(phydev)) {
+ ret = yt8824_phy_package_probe_once(phydev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static struct phy_driver motorcomm_phy_drvs[] = {
{
PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
@@ -3190,13 +4744,29 @@ static struct phy_driver motorcomm_phy_drvs[] = {
.suspend = yt8821_suspend,
.resume = yt8821_resume,
},
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8824),
+ .name = "YT8824 Quad Ports 2.5Gbps Ethernet",
+ .get_features = yt8824_get_features,
+ .read_page = yt8824_read_page,
+ .write_page = yt8824_write_page,
+ .probe = yt8824_probe,
+ .config_aneg = yt8824_config_aneg,
+ .aneg_done = yt8824_aneg_done,
+ .config_init = yt8824_config_init,
+ .read_status = yt8824_read_status,
+ .soft_reset = yt8824_soft_reset,
+ .suspend = yt8824_suspend,
+ .resume = yt8824_resume,
+ },
};
module_phy_driver(motorcomm_phy_drvs);
-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821 PHY driver");
+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821/8824 PHY driver");
MODULE_AUTHOR("Peter Geis");
MODULE_AUTHOR("Frank");
+MODULE_AUTHOR("Kyle");
MODULE_LICENSE("GPL");
static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
@@ -3206,6 +4776,7 @@ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8821) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8824) },
{ /* sentinel */ }
};
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v20 1/3] dt-bindings: net: Document Motorcomm YT8824 PHY package
2026-09-17 13:14 ` [PATCH net-next v20 1/3] dt-bindings: net: Document Motorcomm YT8824 PHY package Kyle Switch
@ 2026-09-21 13:17 ` netdev-bot+sashiko
0 siblings, 0 replies; 7+ messages in thread
From: netdev-bot+sashiko @ 2026-09-21 13:17 UTC (permalink / raw)
To: kyle.switch
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, robh, krzk+dt,
conor+dt, andrew, hkallweit1, linux, Frank.Sae, netdev,
devicetree, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang,
jie.han
Thank you for your contribution! Sashiko AI review found 5 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 3 · Low: 2
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] The single example in the new binding is self-inconsistent and
contradicts the semantics it inherits: the package node is named…
- [Medium] The binding redefines the standard, MAC-side property `phy-
mode` on a PHY *package* node with its own type reference (`$ref:…
- [Medium] The package-level `phy-mode` enum is limited to `[internal,
usxgmii]`.
- [Low] The new binding file
Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml is not…
- [Low] Concern (judged a false positive at end of series): the binding
requires `phy-mode` on the package node and defines compatible…
--
dt-bindings: net: Document Motorcomm YT8824 PHY package
This adds a new binding for the Motorcomm YT8824, described as a PHY
package containing four PHYs. The binding references
ethernet-phy-package.yaml and adds a package-level phy-mode property with
the values internal and usxgmii.
A few questions about the new schema below.
> diff --git a/Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml b/Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml
> new file mode 100644
> index 0000000000000..9737364744a07
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml
> @@ -0,0 +1,62 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/motorcomm,yt8824.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MotorComm YT8824 Ethernet PHY
> +
> +maintainers:
> + - Kyle Switch <kyle.switch@motor-comm.com>
[Severity: Low]
Should MAINTAINERS gain an entry for this file? The MOTORCOMM PHY DRIVER
section currently lists the binding literally:
MOTORCOMM PHY DRIVER
M: Frank <Frank.Sae@motor-comm.com>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
F: drivers/net/phy/motorcomm.c
That pattern does not match motorcomm,yt8824.yaml, and no other section
matches it either, so get_maintainer.pl will not report the Motorcomm PHY
maintainer for future changes to this file. Widening the existing entry to
Documentation/devicetree/bindings/net/motorcomm,yt8*.yaml, or adding a new
F: line, would cover it.
> +
> +description:
> + Motorcomm YT8824 Ethernet PHY is a PHY package of 4 PHYs.
> +
> +$ref: ethernet-phy-package.yaml#
> +
> +properties:
> + compatible:
> + enum:
> + - motorcomm,yt8824-package
> + phy-mode:
> + $ref: /schemas/types.yaml#/definitions/string
> + enum: [ internal, usxgmii ]
[Severity: Medium]
Is phy-mode the right property name here, and is internal a valid value for
this device?
phy-mode is owned by ethernet-controller.yaml, as an alias of
phy-connection-type, and internal is defined there as:
# There is not a standard bus between the MAC and the PHY,
# something proprietary is being used to embed the PHY in the
# MAC.
- internal
The YT8824 is a discrete package addressed over MDIO, so that definition
does not appear to describe it. The driver added later in the series keys
hardware bring-up off this value:
drivers/net/phy/motorcomm.c:yt8824_config_init() {
if (priv->interface_mode == PHY_INTERFACE_MODE_INTERNAL) {
ret = yt8824_internal_config_init_paged(phydev);
...
}
so the standard property name is being given a vendor-specific meaning that
the schema does not state. The property is also added with no description
and placed in required:, which freezes it as ABI.
The closest peer binding, qcom,qca807x.yaml (which also references
ethernet-phy-package.yaml), uses a vendor-prefixed property with an explicit
mode table instead:
qcom,package-mode:
description: |
PHY package can be configured in 3 mode following this table:
Would a described, vendor-prefixed property such as motorcomm,package-mode
be a better fit than overloading phy-mode?
[Severity: Medium]
Separately, is usxgmii the correct wire format for this package?
If the four 2.5G ports are multiplexed onto a single 10G SerDes uplink, the
kernel's interface mode for that format is 10g-qxgmii
(PHY_INTERFACE_MODE_10G_QXGMII), and ethernet-controller.yaml lists the two
as distinct values:
- usxgmii
- 10gbase-r
- 25gbase-r
- 10g-qxgmii
A MAC configured from phy-mode = "usxgmii" would program a single-channel
USXGMII PCS, which would leave the remaining three ports without a channel.
If the chip instead exposes four independent USXGMII lanes, a single
package-wide string cannot express which PHY sits on which lane, and the
binding defines no property for that mapping. I could not determine the
host topology from the series: the only SerDes setup for the non-internal
mode is preamble padding in yt8824_extern_config_sds_init_paged(), which is
consistent with either arrangement.
Could the description: state the host-side topology explicitly?
> +
> +required:
> + - compatible
> + - phy-mode
> + - reg
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + mdio {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + ethernet-phy-package@0 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + compatible = "motorcomm,yt8824-package";
> + reg = <9>;
[Severity: Medium]
Two things about this node.
First, the unit address is @0 while reg = <9>. Should the node be named
ethernet-phy-package@9 so the unit address matches the first reg entry? As
written, two YT8824 packages on the same bus would both be named
ethernet-phy-package@0, which collides under the
-Wunique_unit_address_if_enabled used by
Documentation/devicetree/bindings/Makefile.
Second, is reg = <9> consistent with the meaning inherited from
ethernet-phy-package.yaml?
reg:
minimum: 0
maximum: 31
description:
The base ID number for the PHY package.
Commonly the ID of the first PHY in the PHY package.
Here all four members are at 4, 5, 6 and 7, i.e. below the declared base, and
phylib only ever forms member addresses by adding an unsigned offset to the
base:
drivers/net/phy/phy_package.c:phy_package_address() {
u8 base_addr = shared->base_addr;
if (addr_offset >= PHY_MAX_ADDR - base_addr)
return -EIO;
return base_addr + addr_offset;
}
so no member of this example is reachable as base + offset.
The driver added later in the series treats the package reg as the absolute
MDIO address of the shared top-extended register block, at offset 0:
drivers/net/phy/motorcomm.c:ytphy_write_top_ext() {
/*
* The package base address is used here to read the top ext registers.
* For example, in internal mode, the top ext address is fixed at 9;
*/
ret = __phy_package_write(phydev, 0, YTPHY_PAGE_SELECT, regnum);
...
}
If that is the intended meaning, should this binding add its own reg:
description overriding the base-ID wording? A DT author following the
inherited text and writing reg = <4> would have of_phy_package_join() store
4 as base_addr, and the page-select and page-data writes above would then
land on the member PHY at address 4 instead of the package register block.
> +
> + phy-mode = "internal";
> +
> + ethernet-phy@4 {
> + reg = <4>;
> + };
> +
> + ethernet-phy@5 {
> + reg = <5>;
> + };
[ ... ]
For reference, the binding file is unchanged by the remaining two patches in
the series, so nothing later adjusts the example or the phy-mode wording.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260917131441.3428554-1-kyle.switch%40motor-comm.com
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v20 2/3] net: phy: Add support for Template Control register for PMA
2026-09-17 13:14 ` [PATCH net-next v20 2/3] net: phy: Add support for Template Control register for PMA Kyle Switch
@ 2026-09-21 13:17 ` netdev-bot+sashiko
0 siblings, 0 replies; 7+ messages in thread
From: netdev-bot+sashiko @ 2026-09-21 13:17 UTC (permalink / raw)
To: kyle.switch
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, robh, krzk+dt,
conor+dt, andrew, hkallweit1, linux, Frank.Sae, netdev,
devicetree, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang,
jie.han
Thank you for your contribution! Sashiko AI review found more severe
issue(s) in other patches of this series. This mail is only an FYI, in case
you want to address the nit picks below when you respin.
Low priority / nit picks for your consideration:
- [Low] DISCARDED (resolved later in the series):
genphy_c45_template_testmode() has no in-tree caller at this commit,…
- [Low] include/uapi/linux/mdio.h:325 defines the newly exported macro
MDIO_PMA_10GBT_TESTMODE_MASK as GENMASK(15, 13).
--
net: phy: Add support for Template Control register for PMA
This adds a new phylib helper, genphy_c45_template_testmode(), which writes
the 10GBASE-T PMA template test mode field, along with the register and
field definitions in the exported mdio.h header. Eight values are defined,
Normal plus TestMode1 through TestMode7.
Two small observations below.
> diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
> index 870920311f9a0..c5f5753f7194b 100644
> --- a/drivers/net/phy/phy-c45.c
> +++ b/drivers/net/phy/phy-c45.c
> @@ -1408,6 +1408,29 @@ int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable)
> }
> EXPORT_SYMBOL_GPL(genphy_c45_fast_retrain);
>
> +/**
> + * genphy_c45_template_testmode - configure template testmode registers
> + * @phydev: target phy_device struct
> + * @test_mode: testmode includes Normal to Test mode 7
> + *
> + * Description: Set template testmode include Normal to Test mode 7
> + *
> + * Return: 0 on success, or a negative error code on failure (e.g. register
> + * read/write error).
> + */
> +int genphy_c45_template_testmode(struct phy_device *phydev, u16 test_mode)
> +{
> + u16 ctrl;
> +
> + if (test_mode > MDIO_PMA_10GBT_TESTMODE_7)
> + return -EOPNOTSUPP;
> +
> + ctrl = FIELD_PREP(MDIO_PMA_10GBT_TESTMODE_MASK, test_mode);
> + return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TESTMODE,
> + MDIO_PMA_10GBT_TESTMODE_MASK, ctrl);
> +}
> +EXPORT_SYMBOL_GPL(genphy_c45_template_testmode);
[Severity: Low]
This isn't a bug, just a note for the record. At this commit
genphy_c45_template_testmode() has no in-tree caller.
The following patch in the series, "net: phy: Add driver for Motorcomm Quad
2.5GbE phy", adds the consumers, for example in
yt8824_utp_set_template_test_mode():
return genphy_c45_template_testmode(phydev, test_mode);
along with uses of MDIO_PMA_10GBT_TESTMODE_1 and
MDIO_PMA_10GBT_TESTMODE_NORMAL in yt8824_soft_reset() and
yt8824_restore_working_status(), so the transient state resolves within the
series.
> diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h
> index 06f4bc3c20c71..353ca13c64fca 100644
> --- a/include/uapi/linux/mdio.h
> +++ b/include/uapi/linux/mdio.h
[ ... ]
> @@ -320,6 +321,17 @@
> /* PMA 10GBASE-R Fast Retrain status and control register. */
> #define MDIO_PMA_10GBR_FSRT_ENABLE 0x0001 /* Fast retrain enable */
>
> +/* PMA 10GBASE-T Template Test Mode Register*/
> +#define MDIO_PMA_10GBT_TESTMODE_MASK GENMASK(15, 13) /* Template test mode */
[Severity: Low]
Can userspace expand this new mask? GENMASK() comes from the kernel-only
include/linux/bits.h, and the exported header only pulls in:
#include <linux/types.h>
#include <linux/mii.h>
Neither uapi mii.h nor uapi ethtool.h defines GENMASK or BIT, so a
userspace translation unit that includes <linux/mdio.h> and actually uses
MDIO_PMA_10GBT_TESTMODE_MASK, for instance in a constant initializer, would
fail to compile with GENMASK undeclared.
Would __GENMASK() from include/uapi/linux/bits.h, or a plain 0xe000, be
preferable here?
The in-kernel user in phy-c45.c is unaffected, since it reaches GENMASK and
FIELD_PREP through the in-kernel linux/mdio.h chain. Note the header
already carries a few bare GENMASK() uses that predate this patch, so this
is an existing wart in the file rather than newly broken userspace, but new
uapi additions probably should not repeat it.
> +#define MDIO_PMA_10GBT_TESTMODE_NORMAL 0x0 /* Template Normal */
> +#define MDIO_PMA_10GBT_TESTMODE_1 0x1 /* Template TestMode1 */
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260917131441.3428554-1-kyle.switch%40motor-comm.com
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net-next v20 3/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
2026-09-17 13:14 ` [PATCH net-next v20 3/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy Kyle Switch
@ 2026-09-21 13:17 ` netdev-bot+sashiko
0 siblings, 0 replies; 7+ messages in thread
From: netdev-bot+sashiko @ 2026-09-21 13:17 UTC (permalink / raw)
To: kyle.switch
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, robh, krzk+dt,
conor+dt, andrew, hkallweit1, linux, Frank.Sae, netdev,
devicetree, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang,
jie.han
Thank you for your contribution! Sashiko AI review found 9 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 6 · Low: 3
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] The YT8824 driver silently redefines the meaning of the PHY-
package 'reg' property: ytphy_read_top_ext()/ytphy_write_top_ext()…
- [Medium] yt8824_phy_package_probe_once() takes the PHY-to-MAC interface
mode exclusively from a new 'phy-mode' property on the PHY *package*…
- [Medium] yt8824_config_aneg() programs the 2.5G advertisement
(MDIO_AN_10GBT_CTRL_ADV2_5G) solely from phydev->advertising, ignoring…
- [Medium] yt8824_read_status_paged() unconditionally overwrites the link
state computed by genphy_read_status() with the current vendor…
- [Medium] yt8824_probe() consumes the package one-shot flag
(phy_package_probe_once() is a bare test_and_set_bit()) before running…
- [Medium] The package-shared mutex priv->shared_lock and
priv->interface_mode are initialized inside…
- [Low] Concern (assessed FALSE POSITIVE): 'YT8824 support is advertised
without disclosing that the driver can only bind from an ethernet-phy-…
- [Low] The YT8824 binding example names the package node 'ethernet-phy-
package@0' while declaring 'reg = <9>'.
- [Low] yt8824_read_status_paged() calls genphy_c45_read_lpa() whenever
phydev->autoneg_complete is set (without requiring phydev->autoneg ==…
--
net: phy: Add driver for Motorcomm Quad 2.5GbE phy
Adds support for the Motorcomm YT8824 quad-port 2.5G PHY to the existing
motorcomm driver. The shared "Common Top Extended" register block is
reached through the phy_package helpers, and a package-shared mutex plus a
package-level phy-mode property drive the internal/usxgmii init paths.
A few questions below about the register-space addressing, the interface
mode source, the read_status/config_aneg paths and the package probe
sequencing.
> diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
> index 90a4f86f2758b..7ab09666ab3f4 100644
> --- a/drivers/net/phy/motorcomm.c
> +++ b/drivers/net/phy/motorcomm.c
[ ... ]
> @@ -399,6 +425,12 @@ struct yt8521_priv {
> u8 reg_page;
> };
>
> +struct yt8824_shared_priv {
> + phy_interface_t interface_mode;
> + /* shared_lock used to UTPs operation isolation during swap reg space */
> + struct mutex shared_lock;
> +};
> +
[ ... ]
> @@ -437,6 +469,78 @@ static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum)
> +/**
> + * ytphy_read_top_ext() - read a PHY's top extended register for YT8824
> + * @phydev: a pointer to a &struct phy_device
> + * @regnum: register number to read
> + *
> + * Returns: the value of regnum reg or negative error code
> + */
> +static int ytphy_read_top_ext(struct phy_device *phydev, u16 regnum)
> +{
> + int ret;
> +
> + /*
> + * The package base address is used here to read the top ext registers.
> + * For example, in internal mode, the top ext address is fixed at 9;
> + * for usxgmii, the offset is 4. Instead of using the offset approach
> + * a fixed address is used here to avoid situations such as when phy0
> + * is not in use, where the top ext register address cannot be obtained
> + * correctly.
> + */
> + lockdep_assert_held(&phydev->mdio.bus->mdio_lock);
> + ret = __phy_package_write(phydev, 0, YTPHY_PAGE_SELECT, regnum);
> + if (ret < 0)
> + return ret;
> +
> + return __phy_package_read(phydev, 0, YTPHY_PAGE_DATA);
> +}
[Severity: Medium]
This uses package offset 0, so the address used for the top extended block
is whatever of_phy_package_join() took verbatim from the package node:
drivers/net/phy/phy_package.c:of_phy_package_join() {
...
if (of_property_read_u32(package_node, "reg", &base_addr))
...
}
Documentation/devicetree/bindings/net/ethernet-phy-package.yaml describes
that property as:
reg:
minimum: 0
maximum: 31
description:
The base ID number for the PHY package.
Commonly the ID of the first PHY in the PHY package.
The new motorcomm,yt8824.yaml lists reg as required but does not re-describe
or constrain it, while the comment above states the hardware requirement
(address 9 in internal mode, first PHY + 4 for usxgmii).
If a device tree follows the documented meaning and sets reg to the address
of the first PHY, do ytphy_read_top_ext()/ytphy_write_top_ext() then write
YTPHY_PAGE_SELECT/YTPHY_PAGE_DATA (0x1e/0x1f) into an unrelated sibling PHY?
That would make every page switch through yt8824_read_page(),
yt8824_write_page() and phy8824_page_write_with_lock() silently ineffective
while clobbering another port's extended registers, and the efuse reads in
yt8824_extern_config_sds_init_paged() would come from the wrong device.
Should the binding document and constrain reg for this device, or should the
driver derive the top extended address rather than assuming package offset 0?
[ ... ]
> @@ -622,15 +726,1045 @@ static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
[ ... ]
> +static int yt8824_config_init(struct phy_device *phydev)
> +{
> + struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
> + int ret;
> +
> + mutex_lock(&priv->shared_lock);
> + if (priv->interface_mode == PHY_INTERFACE_MODE_INTERNAL) {
> + ret = yt8824_internal_config_init_paged(phydev);
> if (ret < 0)
> - goto err_restore_page;
> + goto err;
> + } else {
> + ret = yt8824_extern_config_sds_init_paged(phydev);
> + if (ret < 0)
> + goto err;
> + ret = yt8824_extern_config_utp_init_paged(phydev);
> + if (ret < 0)
> + goto err;
> }
> + mutex_unlock(&priv->shared_lock);
> + ret = yt8824_soft_reset(phydev);
[Severity: Medium]
This branch, and the same test in yt8824_soft_reset(), yt8824_power_on(),
yt8824_power_down() and yt8824_restore_working_status(), uses only
priv->interface_mode, which comes from a new phy-mode property on the PHY
package node. phydev->interface is never consulted, even though phylib has
already resolved it per port from the MAC's phy-mode/phy-connection-type by
the time config_init runs.
Can these two descriptions of the same link disagree? A device tree whose
MAC node says usxgmii while the package node says internal would run
yt8824_internal_config_init_paged() and skip the SerDes isolate/soft-reset
sequences in soft_reset/resume/suspend, with config_init still returning 0
and no diagnostic.
Also, the new property is per-package while phydev->interface is per-port, so
a per-port difference cannot be expressed at all. Since this adds a new DT
ABI that duplicates information phylib already has (qca807x used a
vendor-specific qcom,package-mode property instead), would using
phydev->interface here work?
[ ... ]
> @@ -3104,6 +4238,426 @@ static int yt8821_resume(struct phy_device *phydev)
[ ... ]
> +static int yt8824_read_status_paged(struct phy_device *phydev)
> +{
> + int link = 0;
> + int ret = 0;
> + int val = 0;
> +
> + ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
> + if (ret < 0)
> + return ret;
> +
> + ret = genphy_read_status(phydev);
> + if (ret < 0)
> + return ret;
> +
> + if (phydev->autoneg_complete) {
> + ret = genphy_c45_read_lpa(phydev);
> + if (ret < 0)
> + return ret;
> + }
[Severity: Low]
This tests only phydev->autoneg_complete, not phydev->autoneg. With
"ethtool -s ethX autoneg off speed 1000 duplex full", clause 22
autonegotiation is still running (genphy_check_and_restart_aneg() leaves
ANENABLE set), so BMSR_ANEGCOMPLETE and therefore autoneg_complete become 1
while phydev->autoneg is AUTONEG_DISABLE.
genphy_read_status() deliberately clears the pause state for that case:
drivers/net/phy/phy_device.c:genphy_read_status() {
...
phydev->pause = false;
phydev->asym_pause = false;
...
if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
phy_resolve_aneg_linkmode(phydev);
else if (phydev->autoneg == AUTONEG_DISABLE)
err = genphy_read_status_fixed(phydev);
...
}
genphy_c45_read_lpa() then sets phydev->pause from LPA_PAUSE_CAP again.
Should the call be gated on phydev->autoneg == AUTONEG_ENABLE too?
> +
> + ret = phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
> + if (ret < 0)
> + return ret;
> +
> + val = ret;
> +
> + link = val & YTPHY_SSR_LINK;
> + if (link)
> + yt8821_adjust_status(phydev, val);
> +
> + if (link) {
> + if (phydev->link == 0)
> + phydev_dbg(phydev, "%s, phy addr: %d, link up\n",
> + __func__, phydev->mdio.addr);
> + phydev->link = 1;
> + } else {
[Severity: Medium]
Here the link state computed by genphy_read_status() is replaced by the
current vendor status bit. genphy_update_link() keeps the latched-low BMSR
indication in polling mode (and forces link = 0 while BMCR_ANRESTART is set)
so that a drop between two polls is observable.
If a cable is unplugged and replugged between two 1 second polls, does this
sequence lose the transition?
genphy_update_link() -> phydev->link = 0 (latched BMSR)
yt8824_read_status_paged() -> YTPHY_SSR_LINK set, phydev->link = 1
phy_check_link_status() -> link && state == PHY_RUNNING, no branch taken
drivers/net/phy/phy.c:phy_check_link_status() {
if (phydev->link && phydev->state != PHY_RUNNING) {
...
phy_link_up(phydev);
} else if (!phydev->link && phydev->state != PHY_NOLINK) {
...
}
yt8821_adjust_status() has already updated phydev->speed/duplex, so if the
new speed differs the MAC keeps its old configuration and phy_link_up() is
never called. The same overwrite exists in the older yt8521/yt8821 helpers,
but this is new code; could the generic link-down result be preserved instead
of being promoted back to link up?
> + if (phydev->link == 1)
> + phydev_dbg(phydev, "%s, phy addr: %d, link down\n",
> + __func__, phydev->mdio.addr);
> + phydev->link = 0;
> + }
> + phy_resolve_aneg_pause(phydev);
> + return 0;
> +}
[ ... ]
> +static int yt8824_config_aneg(struct phy_device *phydev)
> +{
> + struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
> + int phy_ctrl = 0;
> + int ret;
> +
> + mutex_lock(&priv->shared_lock);
> + ret = phy8824_page_write_with_lock(phydev, YT8824_RSSR_UTP_SPACE);
> + if (ret < 0)
> + goto err;
> +
> + if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
> + phydev->advertising))
> + phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
> +
> + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
> + MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
> + if (ret < 0)
> + goto err;
> +
> + ret = __genphy_config_aneg(phydev, ret);
[Severity: Medium]
The 2.5G advertisement is programmed from phydev->advertising alone, without
looking at phydev->autoneg or phydev->speed. Is the forced speed still
honoured in this case?
"ethtool -s ethX autoneg off speed 1000 duplex full" keeps the
2500baseT_Full bit in phydev->advertising:
drivers/net/phy/phy.c:phy_ethtool_ksettings_set() {
...
linkmode_copy(phydev->advertising, advertising);
...
}
so MDIO_AN_10GBT_CTRL_ADV2_5G is set here, and __genphy_config_aneg() then
takes the forced-speed path which only rewrites the clause 22 registers:
drivers/net/phy/phy_device.c:__genphy_config_aneg() {
...
} else {
linkmode_zero(fixed_advert);
c = phy_caps_lookup(phydev->speed, phydev->duplex,
phydev->supported, true);
if (c)
linkmode_and(fixed_advert, phydev->supported,
c->linkmodes);
advert = fixed_advert;
}
...
}
Nothing clears the 2.5G advertisement, so can the port still negotiate
2.5G with a capable partner after 1000/full was forced? Should ADV2_5G be
cleared when autoneg is disabled and the forced speed is not 2500?
> +
> +err:
> + mutex_unlock(&priv->shared_lock);
> + return ret;
> +}
> +
> +/**
> + * yt8824_phy_package_probe_once() - init phy package for phy8824.
> + * @phydev: a pointer to a &struct phy_device
> + *
> + * Returns: 0 or negative errno code
> + */
> +static int yt8824_phy_package_probe_once(struct phy_device *phydev)
> +{
> + struct yt8824_shared_priv *priv = phy_package_get_priv(phydev);
> + struct device_node *np = phy_package_get_node(phydev);
> + int res;
> +
> + if (!priv || !np) {
> + phydev_err(phydev, "%s: missing shared priv or DT node\n",
> + __func__);
> + return -EINVAL;
> + }
> + /* Initialise shared lock for YT8824 */
> + mutex_init(&priv->shared_lock);
> + res = of_get_phy_mode(np, &priv->interface_mode);
> + if (res < 0) {
> + phydev_warn(phydev, "%s, phy-mode missing in DTS.\n", __func__);
> + return res;
> + }
> +
> + return 0;
> +}
[Severity: Medium]
mutex_init(&priv->shared_lock) and the write of priv->interface_mode happen
without the package lock held. phy_package_probe_once() is only a
test_and_set_bit(), so it guarantees single execution but provides no mutual
exclusion and no ordering for the other package members:
drivers/net/phy/phy_package.c:phy_package_probe_once() {
return __phy_package_set_once(phydev, 1);
}
A sibling whose probe sees the bit already set returns immediately, and
phy_probe() then calls yt8824_get_features(), which does
mutex_lock(&priv->shared_lock). Can that run on a mutex whose wait_lock and
wait_list are still the zeroed memory from the kzalloc'ed shared priv, and
can interface_mode be read before it is written?
dp83640 wraps the same sequence:
drivers/net/phy/dp83640.c:dp83640_probe() {
...
/* Ensure other PHY probes wait for shared clock initialization. */
phy_package_lock(phydev);
if (phy_package_probe_once(phydev))
dp83640_clock_init(clock);
phy_package_unlock(phydev);
...
}
Would the same phy_package_lock()/phy_package_unlock() pairing be
appropriate here?
> +
> +/**
> + * yt8824_probe() - phy8824 probe.
> + * @phydev: a pointer to a &struct phy_device
> + *
> + * Returns: 0 or negative errno code
> + */
> +static int yt8824_probe(struct phy_device *phydev)
> +{
> + struct device *dev = &phydev->mdio.dev;
> + struct yt8824_shared_priv *shared_priv;
> + int ret;
> +
> + ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
> + if (ret)
> + return ret;
[Severity: Low]
This isn't a bug, but the join is unconditional, so a YT8824 that is not
described under an ethernet-phy-package node cannot bind and fails with
-EINVAL from of_phy_package_join(). That matches qca807x_probe(), which
also calls devm_of_phy_package_join() unconditionally, and the requirement is
documented by motorcomm,yt8824.yaml added earlier in the series. Would it be
worth saying in the commit message that the YT8824 binds only from a PHY
package topology?
> +
> + if (phy_package_probe_once(phydev)) {
> + ret = yt8824_phy_package_probe_once(phydev);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
[Severity: Medium]
The one-shot bit is consumed before the fallible initialization runs, and
nothing puts it back if yt8824_phy_package_probe_once() fails.
of_get_phy_mode() leaves *interface untouched on error and the shared priv is
kzalloc'ed, so priv->interface_mode stays PHY_INTERFACE_MODE_NA (0) when the
phy-mode lookup fails. If a sibling of the same package already holds a
reference, that sibling skips initialization entirely and runs with
interface_mode == NA.
Every later test is of the form:
if (priv->interface_mode == PHY_INTERFACE_MODE_INTERNAL)
...internal path...
else
...SerDes path...
so does NA silently select the external USXGMII path? Those ports would run
yt8824_extern_config_sds_init_paged() (efuse-derived PLL/LDO trims, VCO/CDR
writes, MII_RESV1 preamble config, SerDes BMCR reset) and isolate/reset the
SerDes on every resume/suspend/soft_reset even on a part wired for the
internal host interface, with success returned.
Should the one-shot state be rolled back on failure (or the elect plus init
be done under phy_package_lock() as dp83640_probe() does), and should
PHY_INTERFACE_MODE_NA be rejected rather than defaulting to the SerDes path?
One more question about the binding added earlier in this series,
Documentation/devicetree/bindings/net/motorcomm,yt8824.yaml:
[Severity: Low]
The example names the package node ethernet-phy-package@0 while declaring
reg = <9>:
ethernet-phy-package@0 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "motorcomm,yt8824-package";
reg = <9>;
The unit address should match the first entry of reg, and reg is the value
of_phy_package_join() turns into the package base address that
ytphy_read_top_ext()/ytphy_write_top_ext() rely on. Should the node be named
ethernet-phy-package@9? dtc's unit_address_vs_reg check only tests for
presence, so dt_binding_check will not flag this.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260917131441.3428554-1-kyle.switch%40motor-comm.com
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-21 13:18 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-17 13:14 [PATCH net-next v20 0/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy Kyle Switch
2026-09-17 13:14 ` [PATCH net-next v20 1/3] dt-bindings: net: Document Motorcomm YT8824 PHY package Kyle Switch
2026-09-21 13:17 ` netdev-bot+sashiko
2026-09-17 13:14 ` [PATCH net-next v20 2/3] net: phy: Add support for Template Control register for PMA Kyle Switch
2026-09-21 13:17 ` netdev-bot+sashiko
2026-09-17 13:14 ` [PATCH net-next v20 3/3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy Kyle Switch
2026-09-21 13:17 ` netdev-bot+sashiko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®