[openwrt/openwrt] kernel: backport Motorcomm YT8521/YT8531 support

LEDE Commits lede-commits at lists.infradead.org
Thu May 18 08:51:11 PDT 2023


hauke pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/64afcbad3d69147b07dff2462e5645f2c193dee5

commit 64afcbad3d69147b07dff2462e5645f2c193dee5
Author: Tianling Shen <cnsztl at immortalwrt.org>
AuthorDate: Sat May 13 13:54:38 2023 +0800

    kernel: backport Motorcomm YT8521/YT8531 support
    
    It will be used on NanoPi R2C and OrangePi R1 Plus LTS board.
    
    Signed-off-by: Tianling Shen <cnsztl at immortalwrt.org>
---
 ...dia-i2c-Add-driver-for-Sony-IMX477-sensor.patch |    2 +-
 ...on-devicetree-Add-documentation-for-imx37.patch |    2 +-
 ...-media-i2c-Add-IMX519-CMOS-sensor-binding.patch |    2 +-
 ...90-v6.0-net-mii-add-mii_bmcr_encode_fixed.patch |   55 +
 ...ver-for-Motorcomm-yt8521-gigabit-ethernet.patch | 1724 ++++++++++++++++++++
 ...-phy-fix-yt8521-duplicated-argument-to-or.patch |   49 +
 ...2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch |  140 ++
 ...-phy-fix-the-spelling-problem-of-Sentinel.patch |   26 +
 ...m-change-the-phy-id-of-yt8521-and-yt8531s.patch |   29 +
 ...macro-for-Motorcomm-yt8521-yt8531-gigabit.patch |  107 ++
 ...-dts-support-for-Motorcomm-yt8521-gigabit.patch |  343 ++++
 ...dts-support-for-Motorcomm-yt8531s-gigabit.patch |  100 ++
 ...ver-for-Motorcomm-yt8531-gigabit-ethernet.patch |  302 ++++
 ...-phy-motorcomm-uninitialized-variables-in.patch |   34 +
 ...em-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch |    2 +-
 ...-watchdog-add-realtek-otto-watchdog-timer.patch |    2 +-
 target/linux/rockchip/armv8/config-5.15            |    1 +
 17 files changed, 2915 insertions(+), 5 deletions(-)

diff --git a/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch b/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch
index bbf071cb01..e756fa2fff 100644
--- a/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch
+++ b/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch
@@ -25,7 +25,7 @@ Signed-off-by: Naushir Patuck <naush at raspberrypi.com>
 
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
-@@ -17537,6 +17537,14 @@ T:	git git://linuxtv.org/media_tree.git
+@@ -17538,6 +17538,14 @@ T:	git git://linuxtv.org/media_tree.git
  F:	Documentation/devicetree/bindings/media/i2c/sony,imx412.yaml
  F:	drivers/media/i2c/imx412.c
  
diff --git a/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch b/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch
index f006899069..27fd0e5793 100644
--- a/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch
+++ b/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch
@@ -132,7 +132,7 @@ Signed-off-by: David Plowman <david.plowman at raspberrypi.com>
 +...
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
-@@ -17551,6 +17551,7 @@ M:	Raspberry Pi Kernel Maintenance <kern
+@@ -17552,6 +17552,7 @@ M:	Raspberry Pi Kernel Maintenance <kern
  L:	linux-media at vger.kernel.org
  S:	Maintained
  T:	git git://linuxtv.org/media_tree.git
diff --git a/target/linux/bcm27xx/patches-5.15/950-0520-dt-bindings-media-i2c-Add-IMX519-CMOS-sensor-binding.patch b/target/linux/bcm27xx/patches-5.15/950-0520-dt-bindings-media-i2c-Add-IMX519-CMOS-sensor-binding.patch
index dbeb5cb288..25e6555fdb 100644
--- a/target/linux/bcm27xx/patches-5.15/950-0520-dt-bindings-media-i2c-Add-IMX519-CMOS-sensor-binding.patch
+++ b/target/linux/bcm27xx/patches-5.15/950-0520-dt-bindings-media-i2c-Add-IMX519-CMOS-sensor-binding.patch
@@ -132,7 +132,7 @@ Signed-off-by: Lee Jackson <info at arducam.com>
 +...
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
-@@ -17555,6 +17555,14 @@ F:	Documentation/devicetree/bindings/med
+@@ -17556,6 +17556,14 @@ F:	Documentation/devicetree/bindings/med
  F:	Documentation/devicetree/bindings/media/i2c/imx477.yaml
  F:	drivers/media/i2c/imx477.c
  
diff --git a/target/linux/generic/backport-5.15/790-v6.0-net-mii-add-mii_bmcr_encode_fixed.patch b/target/linux/generic/backport-5.15/790-v6.0-net-mii-add-mii_bmcr_encode_fixed.patch
new file mode 100644
index 0000000000..87d7ff6154
--- /dev/null
+++ b/target/linux/generic/backport-5.15/790-v6.0-net-mii-add-mii_bmcr_encode_fixed.patch
@@ -0,0 +1,55 @@
+From bdb6cfe7512f7a214815a3092f0be50963dcacbc Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel at armlinux.org.uk>
+Date: Sat, 18 Jun 2022 11:28:32 +0100
+Subject: [PATCH] net: mii: add mii_bmcr_encode_fixed()
+
+Add a function to encode a fixed speed/duplex to a BMCR value.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel at armlinux.org.uk>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ include/linux/mii.h | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+--- a/include/linux/mii.h
++++ b/include/linux/mii.h
+@@ -595,4 +595,39 @@ static inline u8 mii_resolve_flowctrl_fd
+ 	return cap;
+ }
+ 
++/**
++ * mii_bmcr_encode_fixed - encode fixed speed/duplex settings to a BMCR value
++ * @speed: a SPEED_* value
++ * @duplex: a DUPLEX_* value
++ *
++ * Encode the speed and duplex to a BMCR value. 2500, 1000, 100 and 10 Mbps are
++ * supported. 2500Mbps is encoded to 1000Mbps. Other speeds are encoded as 10
++ * Mbps. Unknown duplex values are encoded to half-duplex.
++ */
++static inline u16 mii_bmcr_encode_fixed(int speed, int duplex)
++{
++	u16 bmcr;
++
++	switch (speed) {
++	case SPEED_2500:
++	case SPEED_1000:
++		bmcr = BMCR_SPEED1000;
++		break;
++
++	case SPEED_100:
++		bmcr = BMCR_SPEED100;
++		break;
++
++	case SPEED_10:
++	default:
++		bmcr = BMCR_SPEED10;
++		break;
++	}
++
++	if (duplex == DUPLEX_FULL)
++		bmcr |= BMCR_FULLDPLX;
++
++	return bmcr;
++}
++
+ #endif /* __LINUX_MII_H__ */
diff --git a/target/linux/generic/backport-5.15/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch b/target/linux/generic/backport-5.15/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch
new file mode 100644
index 0000000000..57137c34fc
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch
@@ -0,0 +1,1724 @@
+From 70479a40954cf353e87a486997a3477108c75aa9 Mon Sep 17 00:00:00 2001
+From: Frank <Frank.Sae at motor-comm.com>
+Date: Fri, 28 Oct 2022 17:26:21 +0800
+Subject: [PATCH] net: phy: Add driver for Motorcomm yt8521 gigabit ethernet
+ phy
+
+Add a driver for the motorcomm yt8521 gigabit ethernet phy. We have verified
+ the driver on StarFive VisionFive development board, which is developed by
+ Shanghai StarFive Technology Co., Ltd.. On the board, yt8521 gigabit ethernet
+ phy works in utp mode, RGMII interface, supports 1000M/100M/10M speeds, and
+ wol(magic package).
+
+Signed-off-by: Frank <Frank.Sae at motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ MAINTAINERS                 |    1 +
+ drivers/net/phy/Kconfig     |    2 +-
+ drivers/net/phy/motorcomm.c | 1635 ++++++++++++++++++++++++++++++++++-
+ 3 files changed, 1635 insertions(+), 3 deletions(-)
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -12697,6 +12697,7 @@ F:	include/uapi/linux/meye.h
+ 
+ MOTORCOMM PHY DRIVER
+ M:	Peter Geis <pgwipeout at gmail.com>
++M:	Frank <Frank.Sae at motor-comm.com>
+ L:	netdev at vger.kernel.org
+ S:	Maintained
+ F:	drivers/net/phy/motorcomm.c
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -245,7 +245,7 @@ config MOTORCOMM_PHY
+ 	tristate "Motorcomm PHYs"
+ 	help
+ 	  Enables support for Motorcomm network PHYs.
+-	  Currently supports the YT8511 gigabit PHY.
++	  Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs.
+ 
+ config NATIONAL_PHY
+ 	tristate "National Semiconductor PHYs"
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1,15 +1,106 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+- * Driver for Motorcomm PHYs
++ * Motorcomm 8511/8521 PHY driver.
+  *
+  * Author: Peter Geis <pgwipeout at gmail.com>
++ * Author: Frank <Frank.Sae at motor-comm.com>
+  */
+ 
++#include <linux/etherdevice.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/phy.h>
+ 
+ #define PHY_ID_YT8511		0x0000010a
++#define PHY_ID_YT8521				0x0000011A
++
++/* YT8521 Register Overview
++ *	UTP Register space	|	FIBER Register space
++ *  ------------------------------------------------------------
++ * |	UTP MII			|	FIBER MII		|
++ * |	UTP MMD			|				|
++ * |	UTP Extended		|	FIBER Extended		|
++ *  ------------------------------------------------------------
++ * |			Common Extended				|
++ *  ------------------------------------------------------------
++ */
++
++/* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */
++
++/* Specific Function Control Register */
++#define YTPHY_SPECIFIC_FUNCTION_CONTROL_REG	0x10
++
++/* 2b00 Manual MDI configuration
++ * 2b01 Manual MDIX configuration
++ * 2b10 Reserved
++ * 2b11 Enable automatic crossover for all modes  *default*
++ */
++#define YTPHY_SFCR_MDI_CROSSOVER_MODE_MASK	(BIT(6) | BIT(5))
++#define YTPHY_SFCR_CROSSOVER_EN			BIT(3)
++#define YTPHY_SFCR_SQE_TEST_EN			BIT(2)
++#define YTPHY_SFCR_POLARITY_REVERSAL_EN		BIT(1)
++#define YTPHY_SFCR_JABBER_DIS			BIT(0)
++
++/* Specific Status Register */
++#define YTPHY_SPECIFIC_STATUS_REG		0x11
++#define YTPHY_SSR_SPEED_MODE_OFFSET		14
++
++#define YTPHY_SSR_SPEED_MODE_MASK		(BIT(15) | BIT(14))
++#define YTPHY_SSR_SPEED_10M			0x0
++#define YTPHY_SSR_SPEED_100M			0x1
++#define YTPHY_SSR_SPEED_1000M			0x2
++#define YTPHY_SSR_DUPLEX_OFFSET			13
++#define YTPHY_SSR_DUPLEX			BIT(13)
++#define YTPHY_SSR_PAGE_RECEIVED			BIT(12)
++#define YTPHY_SSR_SPEED_DUPLEX_RESOLVED		BIT(11)
++#define YTPHY_SSR_LINK				BIT(10)
++#define YTPHY_SSR_MDIX_CROSSOVER		BIT(6)
++#define YTPHY_SSR_DOWNGRADE			BIT(5)
++#define YTPHY_SSR_TRANSMIT_PAUSE		BIT(3)
++#define YTPHY_SSR_RECEIVE_PAUSE			BIT(2)
++#define YTPHY_SSR_POLARITY			BIT(1)
++#define YTPHY_SSR_JABBER			BIT(0)
++
++/* Interrupt enable Register */
++#define YTPHY_INTERRUPT_ENABLE_REG		0x12
++#define YTPHY_IER_WOL				BIT(6)
++
++/* Interrupt Status Register */
++#define YTPHY_INTERRUPT_STATUS_REG		0x13
++#define YTPHY_ISR_AUTONEG_ERR			BIT(15)
++#define YTPHY_ISR_SPEED_CHANGED			BIT(14)
++#define YTPHY_ISR_DUPLEX_CHANGED		BIT(13)
++#define YTPHY_ISR_PAGE_RECEIVED			BIT(12)
++#define YTPHY_ISR_LINK_FAILED			BIT(11)
++#define YTPHY_ISR_LINK_SUCCESSED		BIT(10)
++#define YTPHY_ISR_WOL				BIT(6)
++#define YTPHY_ISR_WIRESPEED_DOWNGRADE		BIT(5)
++#define YTPHY_ISR_SERDES_LINK_FAILED		BIT(3)
++#define YTPHY_ISR_SERDES_LINK_SUCCESSED		BIT(2)
++#define YTPHY_ISR_POLARITY_CHANGED		BIT(1)
++#define YTPHY_ISR_JABBER_HAPPENED		BIT(0)
++
++/* Speed Auto Downgrade Control Register */
++#define YTPHY_SPEED_AUTO_DOWNGRADE_CONTROL_REG	0x14
++#define YTPHY_SADCR_SPEED_DOWNGRADE_EN		BIT(5)
++
++/* If these bits are set to 3, the PHY attempts five times ( 3(set value) +
++ * additional 2) before downgrading, default 0x3
++ */
++#define YTPHY_SADCR_SPEED_RETRY_LIMIT		(0x3 << 2)
++
++/* Rx Error Counter Register */
++#define YTPHY_RX_ERROR_COUNTER_REG		0x15
++
++/* Extended Register's Address Offset Register */
++#define YTPHY_PAGE_SELECT			0x1E
++
++/* Extended Register's Data Register */
++#define YTPHY_PAGE_DATA				0x1F
++
++/* FIBER Auto-Negotiation link partner ability */
++#define YTPHY_FLPA_PAUSE			(0x3 << 7)
++#define YTPHY_FLPA_ASYM_PAUSE			(0x2 << 7)
+ 
+ #define YT8511_PAGE_SELECT	0x1e
+ #define YT8511_PAGE		0x1f
+@@ -38,6 +129,352 @@
+ #define YT8511_DELAY_FE_TX_EN	(0xf << 12)
+ #define YT8511_DELAY_FE_TX_DIS	(0x2 << 12)
+ 
++/* Extended register is different from MMD Register and MII Register.
++ * We can use ytphy_read_ext/ytphy_write_ext/ytphy_modify_ext function to
++ * operate extended register.
++ * Extended Register  start
++ */
++
++/* Phy gmii clock gating Register */
++#define YT8521_CLOCK_GATING_REG			0xC
++#define YT8521_CGR_RX_CLK_EN			BIT(12)
++
++#define YT8521_EXTREG_SLEEP_CONTROL1_REG	0x27
++#define YT8521_ESC1R_SLEEP_SW			BIT(15)
++#define YT8521_ESC1R_PLLON_SLP			BIT(14)
++
++/* Phy fiber Link timer cfg2 Register */
++#define YT8521_LINK_TIMER_CFG2_REG		0xA5
++#define YT8521_LTCR_EN_AUTOSEN			BIT(15)
++
++/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A  are common ext registers
++ * of yt8521 phy. There is no need to switch reg space when operating these
++ * registers.
++ */
++
++#define YT8521_REG_SPACE_SELECT_REG		0xA000
++#define YT8521_RSSR_SPACE_MASK			BIT(1)
++#define YT8521_RSSR_FIBER_SPACE			(0x1 << 1)
++#define YT8521_RSSR_UTP_SPACE			(0x0 << 1)
++#define YT8521_RSSR_TO_BE_ARBITRATED		(0xFF)
++
++#define YT8521_CHIP_CONFIG_REG			0xA001
++#define YT8521_CCR_SW_RST			BIT(15)
++
++#define YT8521_CCR_MODE_SEL_MASK		(BIT(2) | BIT(1) | BIT(0))
++#define YT8521_CCR_MODE_UTP_TO_RGMII		0
++#define YT8521_CCR_MODE_FIBER_TO_RGMII		1
++#define YT8521_CCR_MODE_UTP_FIBER_TO_RGMII	2
++#define YT8521_CCR_MODE_UTP_TO_SGMII		3
++#define YT8521_CCR_MODE_SGPHY_TO_RGMAC		4
++#define YT8521_CCR_MODE_SGMAC_TO_RGPHY		5
++#define YT8521_CCR_MODE_UTP_TO_FIBER_AUTO	6
++#define YT8521_CCR_MODE_UTP_TO_FIBER_FORCE	7
++
++/* 3 phy polling modes,poll mode combines utp and fiber mode*/
++#define YT8521_MODE_FIBER			0x1
++#define YT8521_MODE_UTP				0x2
++#define YT8521_MODE_POLL			0x3
++
++#define YT8521_RGMII_CONFIG1_REG		0xA003
++
++/* TX Gig-E Delay is bits 3:0, default 0x1
++ * TX Fast-E Delay is bits 7:4, default 0xf
++ * RX Delay is bits 13:10, default 0x0
++ * Delay = 150ps * N
++ * On = 2250ps, off = 0ps
++ */
++#define YT8521_RC1R_RX_DELAY_MASK		(0xF << 10)
++#define YT8521_RC1R_RX_DELAY_EN			(0xF << 10)
++#define YT8521_RC1R_RX_DELAY_DIS		(0x0 << 10)
++#define YT8521_RC1R_FE_TX_DELAY_MASK		(0xF << 4)
++#define YT8521_RC1R_FE_TX_DELAY_EN		(0xF << 4)
++#define YT8521_RC1R_FE_TX_DELAY_DIS		(0x0 << 4)
++#define YT8521_RC1R_GE_TX_DELAY_MASK		(0xF << 0)
++#define YT8521_RC1R_GE_TX_DELAY_EN		(0xF << 0)
++#define YT8521_RC1R_GE_TX_DELAY_DIS		(0x0 << 0)
++
++#define YTPHY_MISC_CONFIG_REG			0xA006
++#define YTPHY_MCR_FIBER_SPEED_MASK		BIT(0)
++#define YTPHY_MCR_FIBER_1000BX			(0x1 << 0)
++#define YTPHY_MCR_FIBER_100FX			(0x0 << 0)
++
++/* WOL MAC ADDR: MACADDR2(highest), MACADDR1(middle), MACADDR0(lowest) */
++#define YTPHY_WOL_MACADDR2_REG			0xA007
++#define YTPHY_WOL_MACADDR1_REG			0xA008
++#define YTPHY_WOL_MACADDR0_REG			0xA009
++
++#define YTPHY_WOL_CONFIG_REG			0xA00A
++#define YTPHY_WCR_INTR_SEL			BIT(6)
++#define YTPHY_WCR_ENABLE			BIT(3)
++
++/* 2b00 84ms
++ * 2b01 168ms  *default*
++ * 2b10 336ms
++ * 2b11 672ms
++ */
++#define YTPHY_WCR_PULSE_WIDTH_MASK		(BIT(2) | BIT(1))
++#define YTPHY_WCR_PULSE_WIDTH_672MS		(BIT(2) | BIT(1))
++
++/* 1b0 Interrupt and WOL events is level triggered and active LOW  *default*
++ * 1b1 Interrupt and WOL events is pulse triggered and active LOW
++ */
++#define YTPHY_WCR_TYPE_PULSE			BIT(0)
++
++/* Extended Register  end */
++
++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
++	 * on which media is connected (YT8521_RSSR_TO_BE_ARBITRATED).
++	 */
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(combo_advertising);
++
++	/* YT8521_MODE_FIBER / YT8521_MODE_UTP / YT8521_MODE_POLL*/
++	u8 polling_mode;
++	u8 strap_mode; /* 8 working modes  */
++	/* current reg page of yt8521 phy:
++	 * YT8521_RSSR_UTP_SPACE
++	 * YT8521_RSSR_FIBER_SPACE
++	 * YT8521_RSSR_TO_BE_ARBITRATED
++	 */
++	u8 reg_page;
++};
++
++/**
++ * ytphy_read_ext() - read a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to read
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns the value of regnum reg or negative error code
++ */
++static int ytphy_read_ext(struct phy_device *phydev, u16 regnum)
++{
++	int ret;
++
++	ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
++	if (ret < 0)
++		return ret;
++
++	return __phy_read(phydev, YTPHY_PAGE_DATA);
++}
++
++/**
++ * ytphy_read_ext_with_lock() - read a PHY's extended register
++ * @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_ext_with_lock(struct phy_device *phydev, u16 regnum)
++{
++	int ret;
++
++	phy_lock_mdio_bus(phydev);
++	ret = ytphy_read_ext(phydev, regnum);
++	phy_unlock_mdio_bus(phydev);
++
++	return ret;
++}
++
++/**
++ * ytphy_write_ext() - write a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to write
++ * @val: value to write to @regnum
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative error code
++ */
++static int ytphy_write_ext(struct phy_device *phydev, u16 regnum, u16 val)
++{
++	int ret;
++
++	ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
++	if (ret < 0)
++		return ret;
++
++	return __phy_write(phydev, YTPHY_PAGE_DATA, val);
++}
++
++/**
++ * ytphy_write_ext_with_lock() - write a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to write
++ * @val: value to write to @regnum
++ *
++ * returns 0 or negative error code
++ */
++static int ytphy_write_ext_with_lock(struct phy_device *phydev, u16 regnum,
++				     u16 val)
++{
++	int ret;
++
++	phy_lock_mdio_bus(phydev);
++	ret = ytphy_write_ext(phydev, regnum, val);
++	phy_unlock_mdio_bus(phydev);
++
++	return ret;
++}
++
++/**
++ * ytphy_modify_ext() - bits modify a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to write
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * NOTE: Convenience function which allows a PHY's extended register to be
++ * modified as new register value = (old register value & ~mask) | set.
++ * The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative error code
++ */
++static int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask,
++			    u16 set)
++{
++	int ret;
++
++	ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum);
++	if (ret < 0)
++		return ret;
++
++	return __phy_modify(phydev, YTPHY_PAGE_DATA, mask, set);
++}
++
++/**
++ * ytphy_modify_ext_with_lock() - bits modify a PHY's extended register
++ * @phydev: a pointer to a &struct phy_device
++ * @regnum: register number to write
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * NOTE: Convenience function which allows a PHY's extended register to be
++ * modified as new register value = (old register value & ~mask) | set.
++ *
++ * returns 0 or negative error code
++ */
++static int ytphy_modify_ext_with_lock(struct phy_device *phydev, u16 regnum,
++				      u16 mask, u16 set)
++{
++	int ret;
++
++	phy_lock_mdio_bus(phydev);
++	ret = ytphy_modify_ext(phydev, regnum, mask, set);
++	phy_unlock_mdio_bus(phydev);
++
++	return ret;
++}
++
++/**
++ * ytphy_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: YTPHY_WOL_CONFIG_REG is common ext reg.
++ */
++static void ytphy_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, YTPHY_WOL_CONFIG_REG);
++	if (wol_config < 0)
++		return;
++
++	if (wol_config & YTPHY_WCR_ENABLE)
++		wol->wolopts |= WAKE_MAGIC;
++}
++
++/**
++ * ytphy_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: YTPHY_WOL_CONFIG_REG, YTPHY_WOL_MACADDR2_REG, YTPHY_WOL_MACADDR1_REG
++ * and YTPHY_WOL_MACADDR0_REG are common ext reg. The
++ * YTPHY_INTERRUPT_ENABLE_REG of UTP is special, fiber also use this register.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
++{
++	struct net_device *p_attached_dev;
++	const u16 mac_addr_reg[] = {
++		YTPHY_WOL_MACADDR2_REG,
++		YTPHY_WOL_MACADDR1_REG,
++		YTPHY_WOL_MACADDR0_REG,
++	};
++	const u8 *mac_addr;
++	int old_page;
++	int ret = 0;
++	u16 mask;
++	u16 val;
++	u8 i;
++
++	if (wol->wolopts & WAKE_MAGIC) {
++		p_attached_dev = phydev->attached_dev;
++		if (!p_attached_dev)
++			return -ENODEV;
++
++		mac_addr = (const u8 *)p_attached_dev->dev_addr;
++		if (!is_valid_ether_addr(mac_addr))
++			return -EINVAL;
++
++		/* lock mdio bus then switch to utp reg space */
++		old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
++		if (old_page < 0)
++			goto err_restore_page;
++
++		/* Store the device address for the magic packet */
++		for (i = 0; i < 3; i++) {
++			ret = ytphy_write_ext(phydev, mac_addr_reg[i],
++					      ((mac_addr[i * 2] << 8)) |
++						      (mac_addr[i * 2 + 1]));
++			if (ret < 0)
++				goto err_restore_page;
++		}
++
++		/* Enable WOL feature */
++		mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
++		val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
++		val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
++		ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val);
++		if (ret < 0)
++			goto err_restore_page;
++
++		/* Enable WOL interrupt */
++		ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
++				   YTPHY_IER_WOL);
++		if (ret < 0)
++			goto err_restore_page;
++
++	} else {
++		old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
++		if (old_page < 0)
++			goto err_restore_page;
++
++		/* Disable WOL feature */
++		mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
++		ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, 0);
++
++		/* Disable WOL interrupt */
++		ret = __phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
++				   YTPHY_IER_WOL, 0);
++		if (ret < 0)
++			goto err_restore_page;
++	}
++
++err_restore_page:
++	return phy_restore_page(phydev, old_page, ret);
++}
++
+ static int yt8511_read_page(struct phy_device *phydev)
+ {
+ 	return __phy_read(phydev, YT8511_PAGE_SELECT);
+@@ -111,6 +548,1181 @@ err_restore_page:
+ 	return phy_restore_page(phydev, oldpage, ret);
+ }
+ 
++/**
++ * yt8521_read_page() - read reg page
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns current reg space of yt8521 (YT8521_RSSR_FIBER_SPACE/
++ * YT8521_RSSR_UTP_SPACE) or negative errno code
++ */
++static int yt8521_read_page(struct phy_device *phydev)
++{
++	int old_page;
++
++	old_page = ytphy_read_ext(phydev, YT8521_REG_SPACE_SELECT_REG);
++	if (old_page < 0)
++		return old_page;
++
++	if ((old_page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE)
++		return YT8521_RSSR_FIBER_SPACE;
++
++	return YT8521_RSSR_UTP_SPACE;
++};
++
++/**
++ * yt8521_write_page() - write reg page
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to write.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_write_page(struct phy_device *phydev, int page)
++{
++	int mask = YT8521_RSSR_SPACE_MASK;
++	int set;
++
++	if ((page & YT8521_RSSR_SPACE_MASK) == YT8521_RSSR_FIBER_SPACE)
++		set = YT8521_RSSR_FIBER_SPACE;
++	else
++		set = YT8521_RSSR_UTP_SPACE;
++
++	return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set);
++};
++
++/**
++ * yt8521_probe() - read chip config then set suitable polling_mode
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_probe(struct phy_device *phydev)
++{
++	struct device *dev = &phydev->mdio.dev;
++	struct yt8521_priv *priv;
++	int chip_config;
++	int ret;
++
++	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	phydev->priv = priv;
++
++	chip_config = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG);
++	if (chip_config < 0)
++		return chip_config;
++
++	priv->strap_mode = chip_config & YT8521_CCR_MODE_SEL_MASK;
++	switch (priv->strap_mode) {
++	case YT8521_CCR_MODE_FIBER_TO_RGMII:
++	case YT8521_CCR_MODE_SGPHY_TO_RGMAC:
++	case YT8521_CCR_MODE_SGMAC_TO_RGPHY:
++		priv->polling_mode = YT8521_MODE_FIBER;
++		priv->reg_page = YT8521_RSSR_FIBER_SPACE;
++		phydev->port = PORT_FIBRE;
++		break;
++	case YT8521_CCR_MODE_UTP_FIBER_TO_RGMII:
++	case YT8521_CCR_MODE_UTP_TO_FIBER_AUTO:
++	case YT8521_CCR_MODE_UTP_TO_FIBER_FORCE:
++		priv->polling_mode = YT8521_MODE_POLL;
++		priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED;
++		phydev->port = PORT_NONE;
++		break;
++	case YT8521_CCR_MODE_UTP_TO_SGMII:
++	case YT8521_CCR_MODE_UTP_TO_RGMII:
++		priv->polling_mode = YT8521_MODE_UTP;
++		priv->reg_page = YT8521_RSSR_UTP_SPACE;
++		phydev->port = PORT_TP;
++		break;
++	}
++	/* set default reg space */
++	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++		ret = ytphy_write_ext_with_lock(phydev,
++						YT8521_REG_SPACE_SELECT_REG,
++						priv->reg_page);
++		if (ret < 0)
++			return ret;
++	}
++
++	return 0;
++}
++
++/**
++ * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_utp_read_lpa(struct phy_device *phydev)
++{
++	int lpa, lpagb;
++
++	if (phydev->autoneg == AUTONEG_ENABLE) {
++		if (!phydev->autoneg_complete) {
++			mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
++							0);
++			mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
++			return 0;
++		}
++
++		if (phydev->is_gigabit_capable) {
++			lpagb = __phy_read(phydev, MII_STAT1000);
++			if (lpagb < 0)
++				return lpagb;
++
++			if (lpagb & LPA_1000MSFAIL) {
++				int adv = __phy_read(phydev, MII_CTRL1000);
++
++				if (adv < 0)
++					return adv;
++
++				if (adv & CTL1000_ENABLE_MASTER)
++					phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
++				else
++					phydev_err(phydev, "Master/Slave resolution failed\n");
++				return -ENOLINK;
++			}
++
++			mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
++							lpagb);
++		}
++
++		lpa = __phy_read(phydev, MII_LPA);
++		if (lpa < 0)
++			return lpa;
++
++		mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
++	} else {
++		linkmode_zero(phydev->lp_advertising);
++	}
++
++	return 0;
++}
++
++/**
++ * yt8521_adjust_status() - update speed and duplex to phydev. when in fiber
++ * mode, adjust speed and duplex.
++ * @phydev: a pointer to a &struct phy_device
++ * @status: yt8521 status read from YTPHY_SPECIFIC_STATUS_REG
++ * @is_utp: false(yt8521 work in fiber mode) or true(yt8521 work in utp mode)
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0
++ */
++static int yt8521_adjust_status(struct phy_device *phydev, int status,
++				bool is_utp)
++{
++	int speed_mode, duplex;
++	int speed;
++	int err;
++	int lpa;
++
++	if (is_utp)
++		duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_OFFSET;
++	else
++		duplex = DUPLEX_FULL;	/* for fiber, it always DUPLEX_FULL */
++
++	speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >>
++		     YTPHY_SSR_SPEED_MODE_OFFSET;
++
++	switch (speed_mode) {
++	case YTPHY_SSR_SPEED_10M:
++		if (is_utp)
++			speed = SPEED_10;
++		else
++			/* for fiber, it will never run here, default to
++			 * SPEED_UNKNOWN
++			 */
++			speed = SPEED_UNKNOWN;
++		break;
++	case YTPHY_SSR_SPEED_100M:
++		speed = SPEED_100;
++		break;
++	case YTPHY_SSR_SPEED_1000M:
++		speed = SPEED_1000;
++		break;
++	default:
++		speed = SPEED_UNKNOWN;
++		break;
++	}
++
++	phydev->speed = speed;
++	phydev->duplex = duplex;
++
++	if (is_utp) {
++		err = ytphy_utp_read_lpa(phydev);
++		if (err < 0)
++			return err;
++
++		phy_resolve_aneg_pause(phydev);
++	} else {
++		lpa = __phy_read(phydev, MII_LPA);
++		if (lpa < 0)
++			return lpa;
++
++		/* only support 1000baseX Full */
++		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
++				 phydev->lp_advertising, lpa & LPA_1000XFULL);
++
++		if (!(lpa & YTPHY_FLPA_PAUSE)) {
++			phydev->pause = 0;
++			phydev->asym_pause = 0;
++		} else if ((lpa & YTPHY_FLPA_ASYM_PAUSE)) {
++			phydev->pause = 1;
++			phydev->asym_pause = 1;
++		} else {
++			phydev->pause = 1;
++			phydev->asym_pause = 0;
++		}
++	}
++
++	return 0;
++}
++
++/**
++ * yt8521_read_status_paged() -  determines the speed and duplex of one page
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
++ * operate.
++ *
++ * returns 1 (utp or fiber link),0 (no link) or negative errno code
++ */
++static int yt8521_read_status_paged(struct phy_device *phydev, int page)
++{
++	int fiber_latch_val;
++	int fiber_curr_val;
++	int old_page;
++	int ret = 0;
++	int status;
++	int link;
++
++	linkmode_zero(phydev->lp_advertising);
++	phydev->duplex = DUPLEX_UNKNOWN;
++	phydev->speed = SPEED_UNKNOWN;
++	phydev->asym_pause = 0;
++	phydev->pause = 0;
++
++	/* YT8521 has two reg space (utp/fiber) for linkup with utp/fiber
++	 * respectively. but for utp/fiber combo mode, reg space should be
++	 * arbitrated based on media priority. by default, utp takes
++	 * priority. reg space should be properly set before read
++	 * YTPHY_SPECIFIC_STATUS_REG.
++	 */
++
++	page &= YT8521_RSSR_SPACE_MASK;
++	old_page = phy_select_page(phydev, page);
++	if (old_page < 0)
++		goto err_restore_page;
++
++	/* Read YTPHY_SPECIFIC_STATUS_REG, which indicates the speed and duplex
++	 * of the PHY is actually using.
++	 */
++	ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
++	if (ret < 0)
++		goto err_restore_page;
++
++	status = ret;
++	link = !!(status & YTPHY_SSR_LINK);
++
++	/* When PHY is in fiber mode, speed transferred from 1000Mbps to
++	 * 100Mbps,there is not link down from YTPHY_SPECIFIC_STATUS_REG, so
++	 * we need check MII_BMSR to identify such case.
++	 */
++	if (page == YT8521_RSSR_FIBER_SPACE) {
++		ret = __phy_read(phydev, MII_BMSR);
++		if (ret < 0)
++			goto err_restore_page;
++
++		fiber_latch_val = ret;
++		ret = __phy_read(phydev, MII_BMSR);
++		if (ret < 0)
++			goto err_restore_page;
++
++		fiber_curr_val = ret;
++		if (link && fiber_latch_val != fiber_curr_val) {
++			link = 0;
++			phydev_info(phydev,
++				    "%s, fiber link down detect, latch = %04x, curr = %04x\n",
++				    __func__, fiber_latch_val, fiber_curr_val);
++		}
++	} else {
++		/* Read autonegotiation status */
++		ret = __phy_read(phydev, MII_BMSR);
++		if (ret < 0)
++			goto err_restore_page;
++
++		phydev->autoneg_complete = ret & BMSR_ANEGCOMPLETE ? 1 : 0;
++	}
++
++	if (link) {
++		if (page == YT8521_RSSR_UTP_SPACE)
++			yt8521_adjust_status(phydev, status, true);
++		else
++			yt8521_adjust_status(phydev, status, false);
++	}
++	return phy_restore_page(phydev, old_page, link);
++
++err_restore_page:
++	return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_read_status() -  determines the negotiated speed and duplex
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_read_status(struct phy_device *phydev)
++{
++	struct yt8521_priv *priv = phydev->priv;
++	int link_fiber = 0;
++	int link_utp;
++	int link;
++	int ret;
++
++	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++		link = yt8521_read_status_paged(phydev, priv->reg_page);
++		if (link < 0)
++			return link;
++	} else {
++		/* when page is YT8521_RSSR_TO_BE_ARBITRATED, arbitration is
++		 * needed. by default, utp is higher priority.
++		 */
++
++		link_utp = yt8521_read_status_paged(phydev,
++						    YT8521_RSSR_UTP_SPACE);
++		if (link_utp < 0)
++			return link_utp;
++
++		if (!link_utp) {
++			link_fiber = yt8521_read_status_paged(phydev,
++							      YT8521_RSSR_FIBER_SPACE);
++			if (link_fiber < 0)
++				return link_fiber;
++		}
++
++		link = link_utp || link_fiber;
++	}
++
++	if (link) {
++		if (phydev->link == 0) {
++			/* arbitrate reg space based on linkup media type. */
++			if (priv->polling_mode == YT8521_MODE_POLL &&
++			    priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) {
++				if (link_fiber)
++					priv->reg_page =
++						YT8521_RSSR_FIBER_SPACE;
++				else
++					priv->reg_page = YT8521_RSSR_UTP_SPACE;
++
++				ret = ytphy_write_ext_with_lock(phydev,
++								YT8521_REG_SPACE_SELECT_REG,
++								priv->reg_page);
++				if (ret < 0)
++					return ret;
++
++				phydev->port = link_fiber ? PORT_FIBRE : PORT_TP;
++
++				phydev_info(phydev, "%s, link up, media: %s\n",
++					    __func__,
++					    (phydev->port == PORT_TP) ?
++					    "UTP" : "Fiber");
++			}
++		}
++		phydev->link = 1;
++	} else {
++		if (phydev->link == 1) {
++			phydev_info(phydev, "%s, link down, media: %s\n",
++				    __func__, (phydev->port == PORT_TP) ?
++				    "UTP" : "Fiber");
++
++			/* When in YT8521_MODE_POLL mode, need prepare for next
++			 * arbitration.
++			 */
++			if (priv->polling_mode == YT8521_MODE_POLL) {
++				priv->reg_page = YT8521_RSSR_TO_BE_ARBITRATED;
++				phydev->port = PORT_NONE;
++			}
++		}
++
++		phydev->link = 0;
++	}
++
++	return 0;
++}
++
++/**
++ * yt8521_modify_bmcr_paged - bits modify a PHY's BMCR register of one page
++ * @phydev: the phy_device struct
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to operate
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * NOTE: Convenience function which allows a PHY's BMCR register to be
++ * modified as new register value = (old register value & ~mask) | set.
++ * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space
++ * has MII_BMCR. poll mode combines utp and faber,so need do both.
++ * If it is reset, it will wait for completion.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_modify_bmcr_paged(struct phy_device *phydev, int page,
++				    u16 mask, u16 set)
++{
++	int max_cnt = 500; /* the max wait time of reset ~ 500 ms */
++	int old_page;
++	int ret = 0;
++
++	old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK);
++	if (old_page < 0)
++		goto err_restore_page;
++
++	ret = __phy_modify(phydev, MII_BMCR, mask, set);
++	if (ret < 0)
++		goto err_restore_page;
++
++	/* If it is reset, need to wait for the reset to complete */
++	if (set == BMCR_RESET) {
++		while (max_cnt--) {
++			usleep_range(1000, 1100);
++			ret = __phy_read(phydev, MII_BMCR);
++			if (ret < 0)
++				goto err_restore_page;
++
++			if (!(ret & BMCR_RESET))
++				return phy_restore_page(phydev, old_page, 0);
++		}
++	}
++
++err_restore_page:
++	return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_modify_utp_fiber_bmcr - bits modify a PHY's BMCR register
++ * @phydev: the phy_device struct
++ * @mask: bit mask of bits to clear
++ * @set: bit mask of bits to set
++ *
++ * NOTE: Convenience function which allows a PHY's BMCR register to be
++ * modified as new register value = (old register value & ~mask) | set.
++ * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space
++ * has MII_BMCR. poll mode combines utp and faber,so need do both.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_modify_utp_fiber_bmcr(struct phy_device *phydev, u16 mask,
++					u16 set)
++{
++	struct yt8521_priv *priv = phydev->priv;
++	int ret;
++
++	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++		ret = yt8521_modify_bmcr_paged(phydev, priv->reg_page, mask,
++					       set);
++		if (ret < 0)
++			return ret;
++	} else {
++		ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_UTP_SPACE,
++					       mask, set);
++		if (ret < 0)
++			return ret;
++
++		ret = yt8521_modify_bmcr_paged(phydev, YT8521_RSSR_FIBER_SPACE,
++					       mask, set);
++		if (ret < 0)
++			return ret;
++	}
++	return 0;
++}
++
++/**
++ * yt8521_soft_reset() - called to issue a PHY software reset
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_soft_reset(struct phy_device *phydev)
++{
++	return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_RESET);
++}
++
++/**
++ * yt8521_suspend() - suspend the hardware
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_suspend(struct phy_device *phydev)
++{
++	int wol_config;
++
++	/* YTPHY_WOL_CONFIG_REG is common ext reg */
++	wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
++	if (wol_config < 0)
++		return wol_config;
++
++	/* if wol enable, do nothing */
++	if (wol_config & YTPHY_WCR_ENABLE)
++		return 0;
++
++	return yt8521_modify_utp_fiber_bmcr(phydev, 0, BMCR_PDOWN);
++}
++
++/**
++ * yt8521_resume() - resume the hardware
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_resume(struct phy_device *phydev)
++{
++	int ret;
++	int wol_config;
++
++	/* disable auto sleep */
++	ret = ytphy_modify_ext_with_lock(phydev,
++					 YT8521_EXTREG_SLEEP_CONTROL1_REG,
++					 YT8521_ESC1R_SLEEP_SW, 0);
++	if (ret < 0)
++		return ret;
++
++	wol_config = ytphy_read_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG);
++	if (wol_config < 0)
++		return wol_config;
++
++	/* if wol enable, do nothing */
++	if (wol_config & YTPHY_WCR_ENABLE)
++		return 0;
++
++	return yt8521_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
++}
++
++/**
++ * yt8521_config_init() - called to initialize the PHY
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_config_init(struct phy_device *phydev)
++{
++	int old_page;
++	int ret = 0;
++	u16 val;
++
++	old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
++	if (old_page < 0)
++		goto err_restore_page;
++
++	switch (phydev->interface) {
++	case PHY_INTERFACE_MODE_RGMII:
++		val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
++		val |= YT8521_RC1R_RX_DELAY_DIS;
++		break;
++	case PHY_INTERFACE_MODE_RGMII_RXID:
++		val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
++		val |= YT8521_RC1R_RX_DELAY_EN;
++		break;
++	case PHY_INTERFACE_MODE_RGMII_TXID:
++		val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
++		val |= YT8521_RC1R_RX_DELAY_DIS;
++		break;
++	case PHY_INTERFACE_MODE_RGMII_ID:
++		val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
++		val |= YT8521_RC1R_RX_DELAY_EN;
++		break;
++	case PHY_INTERFACE_MODE_SGMII:
++		break;
++	default: /* do not support other modes */
++		ret = -EOPNOTSUPP;
++		goto err_restore_page;
++	}
++
++	/* set rgmii delay mode */
++	if (phydev->interface != PHY_INTERFACE_MODE_SGMII) {
++		ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG,
++				       (YT8521_RC1R_RX_DELAY_MASK |
++				       YT8521_RC1R_FE_TX_DELAY_MASK |
++				       YT8521_RC1R_GE_TX_DELAY_MASK),
++				       val);
++		if (ret < 0)
++			goto err_restore_page;
++	}
++
++	/* disable auto sleep */
++	ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
++			       YT8521_ESC1R_SLEEP_SW, 0);
++	if (ret < 0)
++		goto err_restore_page;
++
++	/* enable RXC clock when no wire plug */
++	ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
++			       YT8521_CGR_RX_CLK_EN, 0);
++	if (ret < 0)
++		goto err_restore_page;
++
++err_restore_page:
++	return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_prepare_fiber_features() -  A small helper function that setup
++ * fiber's features.
++ * @phydev: a pointer to a &struct phy_device
++ * @dst: a pointer to store fiber's features
++ */
++static void yt8521_prepare_fiber_features(struct phy_device *phydev,
++					  unsigned long *dst)
++{
++	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, dst);
++	linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, dst);
++	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, dst);
++	linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, dst);
++}
++
++/**
++ * yt8521_fiber_setup_forced - configures/forces speed from @phydev
++ * @phydev: target phy_device struct
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_fiber_setup_forced(struct phy_device *phydev)
++{
++	u16 val;
++	int ret;
++
++	if (phydev->speed == SPEED_1000)
++		val = YTPHY_MCR_FIBER_1000BX;
++	else if (phydev->speed == SPEED_100)
++		val = YTPHY_MCR_FIBER_100FX;
++	else
++		return -EINVAL;
++
++	ret =  __phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
++	if (ret < 0)
++		return ret;
++
++	/* disable Fiber auto sensing */
++	ret =  ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG,
++				YT8521_LTCR_EN_AUTOSEN, 0);
++	if (ret < 0)
++		return ret;
++
++	ret =  ytphy_modify_ext(phydev, YTPHY_MISC_CONFIG_REG,
++				YTPHY_MCR_FIBER_SPEED_MASK, val);
++	if (ret < 0)
++		return ret;
++
++	return ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
++				YT8521_CCR_SW_RST, 0);
++}
++
++/**
++ * ytphy_check_and_restart_aneg - Enable and restart auto-negotiation
++ * @phydev: target phy_device struct
++ * @restart: whether aneg restart is requested
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_check_and_restart_aneg(struct phy_device *phydev, bool restart)
++{
++	int ret;
++
++	if (!restart) {
++		/* Advertisement hasn't changed, but maybe aneg was never on to
++		 * begin with?  Or maybe phy was isolated?
++		 */
++		ret = __phy_read(phydev, MII_BMCR);
++		if (ret < 0)
++			return ret;
++
++		if (!(ret & BMCR_ANENABLE) || (ret & BMCR_ISOLATE))
++			restart = true;
++	}
++	/* Enable and Restart Autonegotiation
++	 * Don't isolate the PHY if we're negotiating
++	 */
++	if (restart)
++		return __phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,
++				    BMCR_ANENABLE | BMCR_ANRESTART);
++
++	return 0;
++}
++
++/**
++ * yt8521_fiber_config_aneg - restart auto-negotiation or write
++ * YTPHY_MISC_CONFIG_REG.
++ * @phydev: target phy_device struct
++ *
++ * NOTE:The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_fiber_config_aneg(struct phy_device *phydev)
++{
++	int err, changed = 0;
++	int bmcr;
++	u16 adv;
++
++	if (phydev->autoneg != AUTONEG_ENABLE)
++		return yt8521_fiber_setup_forced(phydev);
++
++	/* enable Fiber auto sensing */
++	err =  ytphy_modify_ext(phydev, YT8521_LINK_TIMER_CFG2_REG,
++				0, YT8521_LTCR_EN_AUTOSEN);
++	if (err < 0)
++		return err;
++
++	err =  ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
++				YT8521_CCR_SW_RST, 0);
++	if (err < 0)
++		return err;
++
++	bmcr = __phy_read(phydev, MII_BMCR);
++	if (bmcr < 0)
++		return bmcr;
++
++	/* When it is coming from fiber forced mode, add bmcr power down
++	 * and power up to let aneg work fine.
++	 */
++	if (!(bmcr & BMCR_ANENABLE)) {
++		__phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
++		usleep_range(1000, 1100);
++		__phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
++	}
++
++	adv = linkmode_adv_to_mii_adv_x(phydev->advertising,
++					ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
++
++	/* Setup fiber advertisement */
++	err = __phy_modify_changed(phydev, MII_ADVERTISE,
++				   ADVERTISE_1000XHALF | ADVERTISE_1000XFULL |
++				   ADVERTISE_1000XPAUSE |
++				   ADVERTISE_1000XPSE_ASYM,
++				   adv);
++	if (err < 0)
++		return err;
++
++	if (err > 0)
++		changed = 1;
++
++	return ytphy_check_and_restart_aneg(phydev, changed);
++}
++
++/**
++ * ytphy_setup_master_slave
++ * @phydev: target phy_device struct
++ *
++ * NOTE: The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_setup_master_slave(struct phy_device *phydev)
++{
++	u16 ctl = 0;
++
++	if (!phydev->is_gigabit_capable)
++		return 0;
++
++	switch (phydev->master_slave_set) {
++	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
++		ctl |= CTL1000_PREFER_MASTER;
++		break;
++	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
++		break;
++	case MASTER_SLAVE_CFG_MASTER_FORCE:
++		ctl |= CTL1000_AS_MASTER;
++		fallthrough;
++	case MASTER_SLAVE_CFG_SLAVE_FORCE:
++		ctl |= CTL1000_ENABLE_MASTER;
++		break;
++	case MASTER_SLAVE_CFG_UNKNOWN:
++	case MASTER_SLAVE_CFG_UNSUPPORTED:
++		return 0;
++	default:
++		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
++		return -EOPNOTSUPP;
++	}
++
++	return __phy_modify_changed(phydev, MII_CTRL1000,
++				    (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
++				    CTL1000_PREFER_MASTER), ctl);
++}
++
++/**
++ * ytphy_utp_config_advert - sanitize and advertise auto-negotiation parameters
++ * @phydev: target phy_device struct
++ *
++ * NOTE: Writes MII_ADVERTISE with the appropriate values,
++ * after sanitizing the values to make sure we only advertise
++ * what is supported.  Returns < 0 on error, 0 if the PHY's advertisement
++ * hasn't changed, and > 0 if it has changed.
++ * The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_utp_config_advert(struct phy_device *phydev)
++{
++	int err, bmsr, changed = 0;
++	u32 adv;
++
++	/* Only allow advertising what this PHY supports */
++	linkmode_and(phydev->advertising, phydev->advertising,
++		     phydev->supported);
++
++	adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
++
++	/* Setup standard advertisement */
++	err = __phy_modify_changed(phydev, MII_ADVERTISE,
++				   ADVERTISE_ALL | ADVERTISE_100BASE4 |
++				   ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
++				   adv);
++	if (err < 0)
++		return err;
++	if (err > 0)
++		changed = 1;
++
++	bmsr = __phy_read(phydev, MII_BMSR);
++	if (bmsr < 0)
++		return bmsr;
++
++	/* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
++	 * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
++	 * logical 1.
++	 */
++	if (!(bmsr & BMSR_ESTATEN))
++		return changed;
++
++	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
++
++	err = __phy_modify_changed(phydev, MII_CTRL1000,
++				   ADVERTISE_1000FULL | ADVERTISE_1000HALF,
++				   adv);
++	if (err < 0)
++		return err;
++	if (err > 0)
++		changed = 1;
++
++	return changed;
++}
++
++/**
++ * ytphy_utp_config_aneg - restart auto-negotiation or write BMCR
++ * @phydev: target phy_device struct
++ * @changed: whether autoneg is requested
++ *
++ * NOTE: If auto-negotiation is enabled, we configure the
++ * advertising, and then restart auto-negotiation.  If it is not
++ * enabled, then we write the BMCR.
++ * The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_utp_config_aneg(struct phy_device *phydev, bool changed)
++{
++	int err;
++	u16 ctl;
++
++	err = ytphy_setup_master_slave(phydev);
++	if (err < 0)
++		return err;
++	else if (err)
++		changed = true;
++
++	if (phydev->autoneg != AUTONEG_ENABLE) {
++		/* configures/forces speed/duplex from @phydev */
++
++		ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
++
++		return __phy_modify(phydev, MII_BMCR, ~(BMCR_LOOPBACK |
++				    BMCR_ISOLATE | BMCR_PDOWN), ctl);
++	}
++
++	err = ytphy_utp_config_advert(phydev);
++	if (err < 0) /* error */
++		return err;
++	else if (err)
++		changed = true;
++
++	return ytphy_check_and_restart_aneg(phydev, changed);
++}
++
++/**
++ * yt8521_config_aneg_paged() - switch reg space then call genphy_config_aneg
++ * of one page
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
++ * operate.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_config_aneg_paged(struct phy_device *phydev, int page)
++{
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(fiber_supported);
++	struct yt8521_priv *priv = phydev->priv;
++	int old_page;
++	int ret = 0;
++
++	page &= YT8521_RSSR_SPACE_MASK;
++
++	old_page = phy_select_page(phydev, page);
++	if (old_page < 0)
++		goto err_restore_page;
++
++	/* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED,
++	 * phydev->advertising should be updated.
++	 */
++	if (priv->reg_page == YT8521_RSSR_TO_BE_ARBITRATED) {
++		linkmode_zero(fiber_supported);
++		yt8521_prepare_fiber_features(phydev, fiber_supported);
++
++		/* prepare fiber_supported, then setup advertising. */
++		if (page == YT8521_RSSR_FIBER_SPACE) {
++			linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
++					 fiber_supported);
++			linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
++					 fiber_supported);
++			linkmode_and(phydev->advertising,
++				     priv->combo_advertising, fiber_supported);
++		} else {
++			/* ETHTOOL_LINK_MODE_Autoneg_BIT is also used in utp */
++			linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
++					   fiber_supported);
++			linkmode_andnot(phydev->advertising,
++					priv->combo_advertising,
++					fiber_supported);
++		}
++	}
++
++	if (page == YT8521_RSSR_FIBER_SPACE)
++		ret = yt8521_fiber_config_aneg(phydev);
++	else
++		ret = ytphy_utp_config_aneg(phydev, false);
++
++err_restore_page:
++	return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_config_aneg() - change reg space then call yt8521_config_aneg_paged
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_config_aneg(struct phy_device *phydev)
++{
++	struct yt8521_priv *priv = phydev->priv;
++	int ret;
++
++	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++		ret = yt8521_config_aneg_paged(phydev, priv->reg_page);
++		if (ret < 0)
++			return ret;
++	} else {
++		/* If reg_page is YT8521_RSSR_TO_BE_ARBITRATED,
++		 * phydev->advertising need to be saved at first run.
++		 * Because it contains the advertising which supported by both
++		 * mac and yt8521(utp and fiber).
++		 */
++		if (linkmode_empty(priv->combo_advertising)) {
++			linkmode_copy(priv->combo_advertising,
++				      phydev->advertising);
++		}
++
++		ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_UTP_SPACE);
++		if (ret < 0)
++			return ret;
++
++		ret = yt8521_config_aneg_paged(phydev, YT8521_RSSR_FIBER_SPACE);
++		if (ret < 0)
++			return ret;
++
++		/* we don't known which will be link, so restore
++		 * phydev->advertising as default value.
++		 */
++		linkmode_copy(phydev->advertising, priv->combo_advertising);
++	}
++	return 0;
++}
++
++/**
++ * yt8521_aneg_done_paged() - determines the auto negotiation result of one
++ * page.
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
++ * operate.
++ *
++ * returns 0(no link)or 1(fiber or utp link) or negative errno code
++ */
++static int yt8521_aneg_done_paged(struct phy_device *phydev, int page)
++{
++	int old_page;
++	int ret = 0;
++	int link;
++
++	old_page = phy_select_page(phydev, page & YT8521_RSSR_SPACE_MASK);
++	if (old_page < 0)
++		goto err_restore_page;
++
++	ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
++	if (ret < 0)
++		goto err_restore_page;
++
++	link = !!(ret & YTPHY_SSR_LINK);
++	ret = link;
++
++err_restore_page:
++	return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_aneg_done() - determines the auto negotiation result
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0(no link)or 1(fiber or utp link) or negative errno code
++ */
++static int yt8521_aneg_done(struct phy_device *phydev)
++{
++	struct yt8521_priv *priv = phydev->priv;
++	int link_fiber = 0;
++	int link_utp;
++	int link;
++
++	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++		link = yt8521_aneg_done_paged(phydev, priv->reg_page);
++	} else {
++		link_utp = yt8521_aneg_done_paged(phydev,
++						  YT8521_RSSR_UTP_SPACE);
++		if (link_utp < 0)
++			return link_utp;
++
++		if (!link_utp) {
++			link_fiber = yt8521_aneg_done_paged(phydev,
++							    YT8521_RSSR_FIBER_SPACE);
++			if (link_fiber < 0)
++				return link_fiber;
++		}
++		link = link_fiber || link_utp;
++		phydev_info(phydev, "%s, link_fiber: %d, link_utp: %d\n",
++			    __func__, link_fiber, link_utp);
++	}
++
++	return link;
++}
++
++/**
++ * ytphy_utp_read_abilities - read PHY abilities from Clause 22 registers
++ * @phydev: target phy_device struct
++ *
++ * NOTE: Reads the PHY's abilities and populates
++ * phydev->supported accordingly.
++ * The caller must have taken the MDIO bus lock.
++ *
++ * returns 0 or negative errno code
++ */
++static int ytphy_utp_read_abilities(struct phy_device *phydev)
++{
++	int val;
++
++	linkmode_set_bit_array(phy_basic_ports_array,
++			       ARRAY_SIZE(phy_basic_ports_array),
++			       phydev->supported);
++
++	val = __phy_read(phydev, MII_BMSR);
++	if (val < 0)
++		return val;
++
++	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported,
++			 val & BMSR_ANEGCAPABLE);
++
++	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported,
++			 val & BMSR_100FULL);
++	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported,
++			 val & BMSR_100HALF);
++	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported,
++			 val & BMSR_10FULL);
++	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported,
++			 val & BMSR_10HALF);
++
++	if (val & BMSR_ESTATEN) {
++		val = __phy_read(phydev, MII_ESTATUS);
++		if (val < 0)
++			return val;
++
++		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
++				 phydev->supported, val & ESTATUS_1000_TFULL);
++		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
++				 phydev->supported, val & ESTATUS_1000_THALF);
++		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
++				 phydev->supported, val & ESTATUS_1000_XFULL);
++	}
++
++	return 0;
++}
++
++/**
++ * yt8521_get_features_paged() -  read supported link modes for one page
++ * @phydev: a pointer to a &struct phy_device
++ * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to
++ * operate.
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_get_features_paged(struct phy_device *phydev, int page)
++{
++	int old_page;
++	int ret = 0;
++
++	page &= YT8521_RSSR_SPACE_MASK;
++	old_page = phy_select_page(phydev, page);
++	if (old_page < 0)
++		goto err_restore_page;
++
++	if (page == YT8521_RSSR_FIBER_SPACE) {
++		linkmode_zero(phydev->supported);
++		yt8521_prepare_fiber_features(phydev, phydev->supported);
++	} else {
++		ret = ytphy_utp_read_abilities(phydev);
++		if (ret < 0)
++			goto err_restore_page;
++	}
++
++err_restore_page:
++	return phy_restore_page(phydev, old_page, ret);
++}
++
++/**
++ * yt8521_get_features - switch reg space then call yt8521_get_features_paged
++ * @phydev: target phy_device struct
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8521_get_features(struct phy_device *phydev)
++{
++	struct yt8521_priv *priv = phydev->priv;
++	int ret;
++
++	if (priv->reg_page != YT8521_RSSR_TO_BE_ARBITRATED) {
++		ret = yt8521_get_features_paged(phydev, priv->reg_page);
++	} else {
++		ret = yt8521_get_features_paged(phydev,
++						YT8521_RSSR_UTP_SPACE);
++		if (ret < 0)
++			return ret;
++
++		/* add fiber's features to phydev->supported */
++		yt8521_prepare_fiber_features(phydev, phydev->supported);
++	}
++	return ret;
++}
++
+ static struct phy_driver motorcomm_phy_drvs[] = {
+ 	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
+@@ -121,16 +1733,35 @@ static struct phy_driver motorcomm_phy_d
+ 		.read_page	= yt8511_read_page,
+ 		.write_page	= yt8511_write_page,
+ 	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_YT8521),
++		.name		= "YT8521 Gigabit Ethernet",
++		.get_features	= yt8521_get_features,
++		.probe		= yt8521_probe,
++		.read_page	= yt8521_read_page,
++		.write_page	= yt8521_write_page,
++		.get_wol	= ytphy_get_wol,
++		.set_wol	= ytphy_set_wol,
++		.config_aneg	= yt8521_config_aneg,
++		.aneg_done	= yt8521_aneg_done,
++		.config_init	= yt8521_config_init,
++		.read_status	= yt8521_read_status,
++		.soft_reset	= yt8521_soft_reset,
++		.suspend	= yt8521_suspend,
++		.resume		= yt8521_resume,
++	},
+ };
+ 
+ module_phy_driver(motorcomm_phy_drvs);
+ 
+-MODULE_DESCRIPTION("Motorcomm PHY driver");
++MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver");
+ MODULE_AUTHOR("Peter Geis");
++MODULE_AUTHOR("Frank");
+ MODULE_LICENSE("GPL");
+ 
+ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
+ 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
++	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
+ 	{ /* sentinal */ }
+ };
+ 
diff --git a/target/linux/generic/backport-5.15/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch b/target/linux/generic/backport-5.15/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch
new file mode 100644
index 0000000000..cce71c8d84
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch
@@ -0,0 +1,49 @@
+From 4e0243e7128c9b25ea2739136076a95d6adaba5e Mon Sep 17 00:00:00 2001
+From: Frank <Frank.Sae at motor-comm.com>
+Date: Fri, 4 Nov 2022 16:44:41 +0800
+Subject: [PATCH] net: phy: fix yt8521 duplicated argument to & or |
+
+cocci warnings: (new ones prefixed by >>)
+>> drivers/net/phy/motorcomm.c:1122:8-35: duplicated argument to & or |
+  drivers/net/phy/motorcomm.c:1126:8-35: duplicated argument to & or |
+  drivers/net/phy/motorcomm.c:1130:8-34: duplicated argument to & or |
+  drivers/net/phy/motorcomm.c:1134:8-34: duplicated argument to & or |
+
+ The second YT8521_RC1R_GE_TX_DELAY_xx should be YT8521_RC1R_FE_TX_DELAY_xx.
+
+Fixes: 70479a40954c ("net: phy: Add driver for Motorcomm yt8521 gigabit ethernet phy")
+Reported-by: kernel test robot <lkp at intel.com>
+Reported-by: Julia Lawall <julia.lawall at lip6.fr>
+Signed-off-by: Frank <Frank.Sae at motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/motorcomm.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1119,19 +1119,19 @@ static int yt8521_config_init(struct phy
+ 
+ 	switch (phydev->interface) {
+ 	case PHY_INTERFACE_MODE_RGMII:
+-		val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
++		val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+ 		val |= YT8521_RC1R_RX_DELAY_DIS;
+ 		break;
+ 	case PHY_INTERFACE_MODE_RGMII_RXID:
+-		val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_GE_TX_DELAY_DIS;
++		val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+ 		val |= YT8521_RC1R_RX_DELAY_EN;
+ 		break;
+ 	case PHY_INTERFACE_MODE_RGMII_TXID:
+-		val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
++		val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+ 		val |= YT8521_RC1R_RX_DELAY_DIS;
+ 		break;
+ 	case PHY_INTERFACE_MODE_RGMII_ID:
+-		val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_GE_TX_DELAY_EN;
++		val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+ 		val |= YT8521_RC1R_RX_DELAY_EN;
+ 		break;
+ 	case PHY_INTERFACE_MODE_SGMII:
diff --git a/target/linux/generic/backport-5.15/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch b/target/linux/generic/backport-5.15/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch
new file mode 100644
index 0000000000..da60c63905
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch
@@ -0,0 +1,140 @@
+From 813abcd98fb1b2cccf850cdfa092a4bfc50b2363 Mon Sep 17 00:00:00 2001
+From: Frank <Frank.Sae at motor-comm.com>
+Date: Tue, 22 Nov 2022 16:42:32 +0800
+Subject: [PATCH] net: phy: add Motorcomm YT8531S phy id.
+
+We added patch for motorcomm.c to support YT8531S. This patch has
+been tested on AM335x platform which has one YT8531S interface
+card and passed all test cases.
+The tested cases indluding: YT8531S UTP function with support of
+10M/100M/1000M; YT8531S Fiber function with support of 100M/1000M;
+and YT8531S Combo function that supports auto detection of media type.
+
+Since most functions of YT8531S are similar to YT8521 and we reuse some
+codes for YT8521 in the patch file.
+
+Signed-off-by: Frank <Frank.Sae at motor-comm.com>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/Kconfig     |  2 +-
+ drivers/net/phy/motorcomm.c | 52 +++++++++++++++++++++++++++++++++----
+ 2 files changed, 48 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -245,7 +245,7 @@ config MOTORCOMM_PHY
+ 	tristate "Motorcomm PHYs"
+ 	help
+ 	  Enables support for Motorcomm network PHYs.
+-	  Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs.
++	  Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
+ 
+ config NATIONAL_PHY
+ 	tristate "National Semiconductor PHYs"
+--- 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 PHY driver.
++ * Motorcomm 8511/8521/8531S PHY driver.
+  *
+  * Author: Peter Geis <pgwipeout at gmail.com>
+  * Author: Frank <Frank.Sae at motor-comm.com>
+@@ -12,9 +12,10 @@
+ #include <linux/phy.h>
+ 
+ #define PHY_ID_YT8511		0x0000010a
+-#define PHY_ID_YT8521				0x0000011A
++#define PHY_ID_YT8521		0x0000011A
++#define PHY_ID_YT8531S		0x4F51E91A
+ 
+-/* YT8521 Register Overview
++/* YT8521/YT8531S Register Overview
+  *	UTP Register space	|	FIBER Register space
+  *  ------------------------------------------------------------
+  * |	UTP MII			|	FIBER MII		|
+@@ -147,7 +148,7 @@
+ #define YT8521_LINK_TIMER_CFG2_REG		0xA5
+ #define YT8521_LTCR_EN_AUTOSEN			BIT(15)
+ 
+-/* 0xA000, 0xA001, 0xA003 ,and 0xA006 ~ 0xA00A  are common ext registers
++/* 0xA000, 0xA001, 0xA003, 0xA006 ~ 0xA00A and 0xA012 are common ext registers
+  * of yt8521 phy. There is no need to switch reg space when operating these
+  * registers.
+  */
+@@ -221,6 +222,9 @@
+  */
+ #define YTPHY_WCR_TYPE_PULSE			BIT(0)
+ 
++#define YT8531S_SYNCE_CFG_REG			0xA012
++#define YT8531S_SCR_SYNCE_ENABLE		BIT(6)
++
+ /* Extended Register  end */
+ 
+ struct yt8521_priv {
+@@ -648,6 +652,26 @@ static int yt8521_probe(struct phy_devic
+ }
+ 
+ /**
++ * yt8531s_probe() - read chip config then set suitable polling_mode
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * returns 0 or negative errno code
++ */
++static int yt8531s_probe(struct phy_device *phydev)
++{
++	int ret;
++
++	/* Disable SyncE clock output by default */
++	ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG,
++					 YT8531S_SCR_SYNCE_ENABLE, 0);
++	if (ret < 0)
++		return ret;
++
++	/* same as yt8521_probe */
++	return yt8521_probe(phydev);
++}
++
++/**
+  * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
+  * @phydev: a pointer to a &struct phy_device
+  *
+@@ -1750,11 +1774,28 @@ static struct phy_driver motorcomm_phy_d
+ 		.suspend	= yt8521_suspend,
+ 		.resume		= yt8521_resume,
+ 	},
++	{
++		PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
++		.name		= "YT8531S Gigabit Ethernet",
++		.get_features	= yt8521_get_features,
++		.probe		= yt8531s_probe,
++		.read_page	= yt8521_read_page,
++		.write_page	= yt8521_write_page,
++		.get_wol	= ytphy_get_wol,
++		.set_wol	= ytphy_set_wol,
++		.config_aneg	= yt8521_config_aneg,
++		.aneg_done	= yt8521_aneg_done,
++		.config_init	= yt8521_config_init,
++		.read_status	= yt8521_read_status,
++		.soft_reset	= yt8521_soft_reset,
++		.suspend	= yt8521_suspend,
++		.resume		= yt8521_resume,
++	},
+ };
+ 
+ module_phy_driver(motorcomm_phy_drvs);
+ 
+-MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver");
++MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
+ MODULE_AUTHOR("Peter Geis");
+ MODULE_AUTHOR("Frank");
+ MODULE_LICENSE("GPL");
+@@ -1762,6 +1803,7 @@ MODULE_LICENSE("GPL");
+ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
+ 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
+ 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
++	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
+ 	{ /* sentinal */ }
+ };
+ 
diff --git a/target/linux/generic/backport-5.15/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch b/target/linux/generic/backport-5.15/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch
new file mode 100644
index 0000000000..94fc32aadb
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch
@@ -0,0 +1,26 @@
+From 4104a713204d62aca482eebb0c6226d82a0721eb Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae at motor-comm.com>
+Date: Sat, 28 Jan 2023 14:35:57 +0800
+Subject: [PATCH] net: phy: fix the spelling problem of Sentinel
+
+CHECK: 'sentinal' may be misspelled - perhaps 'sentinel'?
+
+Signed-off-by: Frank Sae <Frank.Sae at motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Link: https://lore.kernel.org/r/20230128063558.5850-1-Frank.Sae@motor-comm.com
+Signed-off-by: Jakub Kicinski <kuba at kernel.org>
+---
+ drivers/net/phy/motorcomm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1804,7 +1804,7 @@ static const struct mdio_device_id __may
+ 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
+ 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
+ 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
+-	{ /* sentinal */ }
++	{ /* sentinel */ }
+ };
+ 
+ MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);
diff --git a/target/linux/generic/backport-5.15/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch b/target/linux/generic/backport-5.15/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch
new file mode 100644
index 0000000000..076fa82d26
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch
@@ -0,0 +1,29 @@
+From 3c1dc22162d673d595855d24f95200ed2643f88f Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae at motor-comm.com>
+Date: Sat, 28 Jan 2023 14:35:58 +0800
+Subject: [PATCH] net: phy: motorcomm: change the phy id of yt8521 and yt8531s
+ to lowercase
+
+The phy id is usually defined in lower case.
+
+Signed-off-by: Frank Sae <Frank.Sae at motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Link: https://lore.kernel.org/r/20230128063558.5850-2-Frank.Sae@motor-comm.com
+Signed-off-by: Jakub Kicinski <kuba at kernel.org>
+---
+ drivers/net/phy/motorcomm.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -12,8 +12,8 @@
+ #include <linux/phy.h>
+ 
+ #define PHY_ID_YT8511		0x0000010a
+-#define PHY_ID_YT8521		0x0000011A
+-#define PHY_ID_YT8531S		0x4F51E91A
++#define PHY_ID_YT8521		0x0000011a
++#define PHY_ID_YT8531S		0x4f51e91a
+ 
+ /* YT8521/YT8531S Register Overview
+  *	UTP Register space	|	FIBER Register space
diff --git a/target/linux/generic/backport-5.15/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch b/target/linux/generic/backport-5.15/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch
new file mode 100644
index 0000000000..ba9a6ab4cc
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch
@@ -0,0 +1,107 @@
+From 4869a146cd60fc8115230f0a45e15e534c531922 Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae at motor-comm.com>
+Date: Thu, 2 Feb 2023 11:00:34 +0800
+Subject: [PATCH] net: phy: Add BIT macro for Motorcomm yt8521/yt8531 gigabit
+ ethernet phy
+
+Add BIT macro for Motorcomm yt8521/yt8531 gigabit ethernet phy.
+ This is a preparatory patch. Add BIT macro for 0xA012 reg, and
+ supplement for 0xA001 and 0xA003 reg. These will be used to support dts.
+
+Signed-off-by: Frank Sae <Frank.Sae at motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/motorcomm.c | 55 ++++++++++++++++++++++++++++++++++---
+ 1 file changed, 51 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -161,6 +161,11 @@
+ 
+ #define YT8521_CHIP_CONFIG_REG			0xA001
+ #define YT8521_CCR_SW_RST			BIT(15)
++/* 1b0 disable 1.9ns rxc clock delay  *default*
++ * 1b1 enable 1.9ns rxc clock delay
++ */
++#define YT8521_CCR_RXC_DLY_EN			BIT(8)
++#define YT8521_CCR_RXC_DLY_1_900_NS		1900
+ 
+ #define YT8521_CCR_MODE_SEL_MASK		(BIT(2) | BIT(1) | BIT(0))
+ #define YT8521_CCR_MODE_UTP_TO_RGMII		0
+@@ -178,22 +183,41 @@
+ #define YT8521_MODE_POLL			0x3
+ 
+ #define YT8521_RGMII_CONFIG1_REG		0xA003
+-
++/* 1b0 use original tx_clk_rgmii  *default*
++ * 1b1 use inverted tx_clk_rgmii.
++ */
++#define YT8521_RC1R_TX_CLK_SEL_INVERTED		BIT(14)
+ /* TX Gig-E Delay is bits 3:0, default 0x1
+  * TX Fast-E Delay is bits 7:4, default 0xf
+  * RX Delay is bits 13:10, default 0x0
+  * Delay = 150ps * N
+  * On = 2250ps, off = 0ps
+  */
+-#define YT8521_RC1R_RX_DELAY_MASK		(0xF << 10)
++#define YT8521_RC1R_RX_DELAY_MASK		GENMASK(13, 10)
+ #define YT8521_RC1R_RX_DELAY_EN			(0xF << 10)
+ #define YT8521_RC1R_RX_DELAY_DIS		(0x0 << 10)
+-#define YT8521_RC1R_FE_TX_DELAY_MASK		(0xF << 4)
++#define YT8521_RC1R_FE_TX_DELAY_MASK		GENMASK(7, 4)
+ #define YT8521_RC1R_FE_TX_DELAY_EN		(0xF << 4)
+ #define YT8521_RC1R_FE_TX_DELAY_DIS		(0x0 << 4)
+-#define YT8521_RC1R_GE_TX_DELAY_MASK		(0xF << 0)
++#define YT8521_RC1R_GE_TX_DELAY_MASK		GENMASK(3, 0)
+ #define YT8521_RC1R_GE_TX_DELAY_EN		(0xF << 0)
+ #define YT8521_RC1R_GE_TX_DELAY_DIS		(0x0 << 0)
++#define YT8521_RC1R_RGMII_0_000_NS		0
++#define YT8521_RC1R_RGMII_0_150_NS		1
++#define YT8521_RC1R_RGMII_0_300_NS		2
++#define YT8521_RC1R_RGMII_0_450_NS		3
++#define YT8521_RC1R_RGMII_0_600_NS		4
++#define YT8521_RC1R_RGMII_0_750_NS		5
++#define YT8521_RC1R_RGMII_0_900_NS		6
++#define YT8521_RC1R_RGMII_1_050_NS		7
++#define YT8521_RC1R_RGMII_1_200_NS		8
++#define YT8521_RC1R_RGMII_1_350_NS		9
++#define YT8521_RC1R_RGMII_1_500_NS		10
++#define YT8521_RC1R_RGMII_1_650_NS		11
++#define YT8521_RC1R_RGMII_1_800_NS		12
++#define YT8521_RC1R_RGMII_1_950_NS		13
++#define YT8521_RC1R_RGMII_2_100_NS		14
++#define YT8521_RC1R_RGMII_2_250_NS		15
+ 
+ #define YTPHY_MISC_CONFIG_REG			0xA006
+ #define YTPHY_MCR_FIBER_SPEED_MASK		BIT(0)
+@@ -222,6 +246,29 @@
+  */
+ #define YTPHY_WCR_TYPE_PULSE			BIT(0)
+ 
++#define YTPHY_SYNCE_CFG_REG			0xA012
++#define YT8521_SCR_SYNCE_ENABLE			BIT(5)
++/* 1b0 output 25m clock
++ * 1b1 output 125m clock  *default*
++ */
++#define YT8521_SCR_CLK_FRE_SEL_125M		BIT(3)
++#define YT8521_SCR_CLK_SRC_MASK			GENMASK(2, 1)
++#define YT8521_SCR_CLK_SRC_PLL_125M		0
++#define YT8521_SCR_CLK_SRC_UTP_RX		1
++#define YT8521_SCR_CLK_SRC_SDS_RX		2
++#define YT8521_SCR_CLK_SRC_REF_25M		3
++#define YT8531_SCR_SYNCE_ENABLE			BIT(6)
++/* 1b0 output 25m clock   *default*
++ * 1b1 output 125m clock
++ */
++#define YT8531_SCR_CLK_FRE_SEL_125M		BIT(4)
++#define YT8531_SCR_CLK_SRC_MASK			GENMASK(3, 1)
++#define YT8531_SCR_CLK_SRC_PLL_125M		0
++#define YT8531_SCR_CLK_SRC_UTP_RX		1
++#define YT8531_SCR_CLK_SRC_SDS_RX		2
++#define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL	3
++#define YT8531_SCR_CLK_SRC_REF_25M		4
++#define YT8531_SCR_CLK_SRC_SSC_25M		5
+ #define YT8531S_SYNCE_CFG_REG			0xA012
+ #define YT8531S_SCR_SYNCE_ENABLE		BIT(6)
+ 
diff --git a/target/linux/generic/backport-5.15/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch b/target/linux/generic/backport-5.15/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch
new file mode 100644
index 0000000000..6d89fae84c
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch
@@ -0,0 +1,343 @@
+From a6e68f0f8769f79c67cdcfb6302feecd36197dec Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae at motor-comm.com>
+Date: Thu, 2 Feb 2023 11:00:35 +0800
+Subject: [PATCH] net: phy: Add dts support for Motorcomm yt8521 gigabit
+ ethernet phy
+
+Add dts support for Motorcomm yt8521 gigabit ethernet phy.
+ Add ytphy_rgmii_clk_delay_config function to support dst config for
+ the delay of rgmii clk. This funciont is common for yt8521, yt8531s
+ and yt8531.
+ This patch has been verified on AM335x platform.
+
+Signed-off-by: Frank Sae <Frank.Sae at motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/motorcomm.c | 253 ++++++++++++++++++++++++++++--------
+ 1 file changed, 199 insertions(+), 54 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -10,6 +10,7 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/phy.h>
++#include <linux/of.h>
+ 
+ #define PHY_ID_YT8511		0x0000010a
+ #define PHY_ID_YT8521		0x0000011a
+@@ -187,21 +188,9 @@
+  * 1b1 use inverted tx_clk_rgmii.
+  */
+ #define YT8521_RC1R_TX_CLK_SEL_INVERTED		BIT(14)
+-/* TX Gig-E Delay is bits 3:0, default 0x1
+- * TX Fast-E Delay is bits 7:4, default 0xf
+- * RX Delay is bits 13:10, default 0x0
+- * Delay = 150ps * N
+- * On = 2250ps, off = 0ps
+- */
+ #define YT8521_RC1R_RX_DELAY_MASK		GENMASK(13, 10)
+-#define YT8521_RC1R_RX_DELAY_EN			(0xF << 10)
+-#define YT8521_RC1R_RX_DELAY_DIS		(0x0 << 10)
+ #define YT8521_RC1R_FE_TX_DELAY_MASK		GENMASK(7, 4)
+-#define YT8521_RC1R_FE_TX_DELAY_EN		(0xF << 4)
+-#define YT8521_RC1R_FE_TX_DELAY_DIS		(0x0 << 4)
+ #define YT8521_RC1R_GE_TX_DELAY_MASK		GENMASK(3, 0)
+-#define YT8521_RC1R_GE_TX_DELAY_EN		(0xF << 0)
+-#define YT8521_RC1R_GE_TX_DELAY_DIS		(0x0 << 0)
+ #define YT8521_RC1R_RGMII_0_000_NS		0
+ #define YT8521_RC1R_RGMII_0_150_NS		1
+ #define YT8521_RC1R_RGMII_0_300_NS		2
+@@ -274,6 +263,10 @@
+ 
+ /* Extended Register  end */
+ 
++#define YTPHY_DTS_OUTPUT_CLK_DIS		0
++#define YTPHY_DTS_OUTPUT_CLK_25M		25000000
++#define YTPHY_DTS_OUTPUT_CLK_125M		125000000
++
+ 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
+@@ -641,6 +634,142 @@ static int yt8521_write_page(struct phy_
+ };
+ 
+ /**
++ * struct ytphy_cfg_reg_map - map a config value to a register value
++ * @cfg: value in device configuration
++ * @reg: value in the register
++ */
++struct ytphy_cfg_reg_map {
++	u32 cfg;
++	u32 reg;
++};
++
++static const struct ytphy_cfg_reg_map ytphy_rgmii_delays[] = {
++	/* for tx delay / rx delay with YT8521_CCR_RXC_DLY_EN is not set. */
++	{ 0,	YT8521_RC1R_RGMII_0_000_NS },
++	{ 150,	YT8521_RC1R_RGMII_0_150_NS },
++	{ 300,	YT8521_RC1R_RGMII_0_300_NS },
++	{ 450,	YT8521_RC1R_RGMII_0_450_NS },
++	{ 600,	YT8521_RC1R_RGMII_0_600_NS },
++	{ 750,	YT8521_RC1R_RGMII_0_750_NS },
++	{ 900,	YT8521_RC1R_RGMII_0_900_NS },
++	{ 1050,	YT8521_RC1R_RGMII_1_050_NS },
++	{ 1200,	YT8521_RC1R_RGMII_1_200_NS },
++	{ 1350,	YT8521_RC1R_RGMII_1_350_NS },
++	{ 1500,	YT8521_RC1R_RGMII_1_500_NS },
++	{ 1650,	YT8521_RC1R_RGMII_1_650_NS },
++	{ 1800,	YT8521_RC1R_RGMII_1_800_NS },
++	{ 1950,	YT8521_RC1R_RGMII_1_950_NS },	/* default tx/rx delay */
++	{ 2100,	YT8521_RC1R_RGMII_2_100_NS },
++	{ 2250,	YT8521_RC1R_RGMII_2_250_NS },
++
++	/* only for rx delay with YT8521_CCR_RXC_DLY_EN is set. */
++	{ 0    + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_000_NS },
++	{ 150  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_150_NS },
++	{ 300  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_300_NS },
++	{ 450  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_450_NS },
++	{ 600  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_600_NS },
++	{ 750  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_750_NS },
++	{ 900  + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_0_900_NS },
++	{ 1050 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_050_NS },
++	{ 1200 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_200_NS },
++	{ 1350 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_350_NS },
++	{ 1500 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_500_NS },
++	{ 1650 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_650_NS },
++	{ 1800 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_800_NS },
++	{ 1950 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_1_950_NS },
++	{ 2100 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_2_100_NS },
++	{ 2250 + YT8521_CCR_RXC_DLY_1_900_NS,	YT8521_RC1R_RGMII_2_250_NS }
++};
++
++static u32 ytphy_get_delay_reg_value(struct phy_device *phydev,
++				     const char *prop_name,
++				     const struct ytphy_cfg_reg_map *tbl,
++				     int tb_size,
++				     u16 *rxc_dly_en,
++				     u32 dflt)
++{
++	struct device_node *node = phydev->mdio.dev.of_node;
++	int tb_size_half = tb_size / 2;
++	u32 val;
++	int i;
++
++	if (of_property_read_u32(node, prop_name, &val))
++		goto err_dts_val;
++
++	/* when rxc_dly_en is NULL, it is get the delay for tx, only half of
++	 * tb_size is valid.
++	 */
++	if (!rxc_dly_en)
++		tb_size = tb_size_half;
++
++	for (i = 0; i < tb_size; i++) {
++		if (tbl[i].cfg == val) {
++			if (rxc_dly_en && i < tb_size_half)
++				*rxc_dly_en = 0;
++			return tbl[i].reg;
++		}
++	}
++
++	phydev_warn(phydev, "Unsupported value %d for %s using default (%u)\n",
++		    val, prop_name, dflt);
++
++err_dts_val:
++	/* when rxc_dly_en is not NULL, it is get the delay for rx.
++	 * The rx default in dts and ytphy_rgmii_clk_delay_config is 1950 ps,
++	 * so YT8521_CCR_RXC_DLY_EN should not be set.
++	 */
++	if (rxc_dly_en)
++		*rxc_dly_en = 0;
++
++	return dflt;
++}
++
++static int ytphy_rgmii_clk_delay_config(struct phy_device *phydev)
++{
++	int tb_size = ARRAY_SIZE(ytphy_rgmii_delays);
++	u16 rxc_dly_en = YT8521_CCR_RXC_DLY_EN;
++	u32 rx_reg, tx_reg;
++	u16 mask, val = 0;
++	int ret;
++
++	rx_reg = ytphy_get_delay_reg_value(phydev, "rx-internal-delay-ps",
++					   ytphy_rgmii_delays, tb_size,
++					   &rxc_dly_en,
++					   YT8521_RC1R_RGMII_1_950_NS);
++	tx_reg = ytphy_get_delay_reg_value(phydev, "tx-internal-delay-ps",
++					   ytphy_rgmii_delays, tb_size, NULL,
++					   YT8521_RC1R_RGMII_1_950_NS);
++
++	switch (phydev->interface) {
++	case PHY_INTERFACE_MODE_RGMII:
++		rxc_dly_en = 0;
++		break;
++	case PHY_INTERFACE_MODE_RGMII_RXID:
++		val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg);
++		break;
++	case PHY_INTERFACE_MODE_RGMII_TXID:
++		rxc_dly_en = 0;
++		val |= FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
++		break;
++	case PHY_INTERFACE_MODE_RGMII_ID:
++		val |= FIELD_PREP(YT8521_RC1R_RX_DELAY_MASK, rx_reg) |
++		       FIELD_PREP(YT8521_RC1R_GE_TX_DELAY_MASK, tx_reg);
++		break;
++	default: /* do not support other modes */
++		return -EOPNOTSUPP;
++	}
++
++	ret = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG,
++			       YT8521_CCR_RXC_DLY_EN, rxc_dly_en);
++	if (ret < 0)
++		return ret;
++
++	/* Generally, it is not necessary to adjust YT8521_RC1R_FE_TX_DELAY */
++	mask = YT8521_RC1R_RX_DELAY_MASK | YT8521_RC1R_GE_TX_DELAY_MASK;
++	return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
++}
++
++/**
+  * yt8521_probe() - read chip config then set suitable polling_mode
+  * @phydev: a pointer to a &struct phy_device
+  *
+@@ -648,9 +777,12 @@ static int yt8521_write_page(struct phy_
+  */
+ static int yt8521_probe(struct phy_device *phydev)
+ {
++	struct device_node *node = phydev->mdio.dev.of_node;
+ 	struct device *dev = &phydev->mdio.dev;
+ 	struct yt8521_priv *priv;
+ 	int chip_config;
++	u16 mask, val;
++	u32 freq;
+ 	int ret;
+ 
+ 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+@@ -695,7 +827,45 @@ static int yt8521_probe(struct phy_devic
+ 			return ret;
+ 	}
+ 
+-	return 0;
++	if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
++		freq = YTPHY_DTS_OUTPUT_CLK_DIS;
++
++	if (phydev->drv->phy_id == PHY_ID_YT8521) {
++		switch (freq) {
++		case YTPHY_DTS_OUTPUT_CLK_DIS:
++			mask = YT8521_SCR_SYNCE_ENABLE;
++			val = 0;
++			break;
++		case YTPHY_DTS_OUTPUT_CLK_25M:
++			mask = YT8521_SCR_SYNCE_ENABLE |
++			       YT8521_SCR_CLK_SRC_MASK |
++			       YT8521_SCR_CLK_FRE_SEL_125M;
++			val = YT8521_SCR_SYNCE_ENABLE |
++			      FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
++					 YT8521_SCR_CLK_SRC_REF_25M);
++			break;
++		case YTPHY_DTS_OUTPUT_CLK_125M:
++			mask = YT8521_SCR_SYNCE_ENABLE |
++			       YT8521_SCR_CLK_SRC_MASK |
++			       YT8521_SCR_CLK_FRE_SEL_125M;
++			val = YT8521_SCR_SYNCE_ENABLE |
++			      YT8521_SCR_CLK_FRE_SEL_125M |
++			      FIELD_PREP(YT8521_SCR_CLK_SRC_MASK,
++					 YT8521_SCR_CLK_SRC_PLL_125M);
++			break;
++		default:
++			phydev_warn(phydev, "Freq err:%u\n", freq);
++			return -EINVAL;
++		}
++	} else if (phydev->drv->phy_id == PHY_ID_YT8531S) {
++		return 0;
++	} else {
++		phydev_warn(phydev, "PHY id err\n");
++		return -EINVAL;
++	}
++
++	return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
++					  val);
+ }
+ 
+ /**
+@@ -1180,61 +1350,36 @@ static int yt8521_resume(struct phy_devi
+  */
+ static int yt8521_config_init(struct phy_device *phydev)
+ {
++	struct device_node *node = phydev->mdio.dev.of_node;
+ 	int old_page;
+ 	int ret = 0;
+-	u16 val;
+ 
+ 	old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE);
+ 	if (old_page < 0)
+ 		goto err_restore_page;
+ 
+-	switch (phydev->interface) {
+-	case PHY_INTERFACE_MODE_RGMII:
+-		val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+-		val |= YT8521_RC1R_RX_DELAY_DIS;
+-		break;
+-	case PHY_INTERFACE_MODE_RGMII_RXID:
+-		val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS;
+-		val |= YT8521_RC1R_RX_DELAY_EN;
+-		break;
+-	case PHY_INTERFACE_MODE_RGMII_TXID:
+-		val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+-		val |= YT8521_RC1R_RX_DELAY_DIS;
+-		break;
+-	case PHY_INTERFACE_MODE_RGMII_ID:
+-		val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN;
+-		val |= YT8521_RC1R_RX_DELAY_EN;
+-		break;
+-	case PHY_INTERFACE_MODE_SGMII:
+-		break;
+-	default: /* do not support other modes */
+-		ret = -EOPNOTSUPP;
+-		goto err_restore_page;
+-	}
+-
+ 	/* set rgmii delay mode */
+ 	if (phydev->interface != PHY_INTERFACE_MODE_SGMII) {
+-		ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG,
+-				       (YT8521_RC1R_RX_DELAY_MASK |
+-				       YT8521_RC1R_FE_TX_DELAY_MASK |
+-				       YT8521_RC1R_GE_TX_DELAY_MASK),
+-				       val);
++		ret = ytphy_rgmii_clk_delay_config(phydev);
+ 		if (ret < 0)
+ 			goto err_restore_page;
+ 	}
+ 
+-	/* disable auto sleep */
+-	ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
+-			       YT8521_ESC1R_SLEEP_SW, 0);
+-	if (ret < 0)
+-		goto err_restore_page;
+-
+-	/* enable RXC clock when no wire plug */
+-	ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
+-			       YT8521_CGR_RX_CLK_EN, 0);
+-	if (ret < 0)
+-		goto err_restore_page;
++	if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
++		/* disable auto sleep */
++		ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG,
++				       YT8521_ESC1R_SLEEP_SW, 0);
++		if (ret < 0)
++			goto err_restore_page;
++	}
+ 
++	if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
++		/* enable RXC clock when no wire plug */
++		ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG,
++				       YT8521_CGR_RX_CLK_EN, 0);
++		if (ret < 0)
++			goto err_restore_page;
++	}
+ err_restore_page:
+ 	return phy_restore_page(phydev, old_page, ret);
+ }
diff --git a/target/linux/generic/backport-5.15/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch b/target/linux/generic/backport-5.15/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch
new file mode 100644
index 0000000000..86fc04695c
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch
@@ -0,0 +1,100 @@
+From 36152f87dda4af221b16258751451d9cd3d0fb0b Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae at motor-comm.com>
+Date: Thu, 2 Feb 2023 11:00:36 +0800
+Subject: [PATCH] net: phy: Add dts support for Motorcomm yt8531s gigabit
+ ethernet phy
+
+Add dts support for Motorcomm yt8531s gigabit ethernet phy.
+ Change yt8521_probe to support clk config of yt8531s. Becase
+ yt8521_probe does the things which yt8531s is needed, so
+ removed yt8531s function.
+ This patch has been verified on AM335x platform with yt8531s board.
+
+Signed-off-by: Frank Sae <Frank.Sae at motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/motorcomm.c | 51 ++++++++++++++++++++-----------------
+ 1 file changed, 27 insertions(+), 24 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -258,8 +258,6 @@
+ #define YT8531_SCR_CLK_SRC_CLOCK_FROM_DIGITAL	3
+ #define YT8531_SCR_CLK_SRC_REF_25M		4
+ #define YT8531_SCR_CLK_SRC_SSC_25M		5
+-#define YT8531S_SYNCE_CFG_REG			0xA012
+-#define YT8531S_SCR_SYNCE_ENABLE		BIT(6)
+ 
+ /* Extended Register  end */
+ 
+@@ -858,7 +856,32 @@ static int yt8521_probe(struct phy_devic
+ 			return -EINVAL;
+ 		}
+ 	} else if (phydev->drv->phy_id == PHY_ID_YT8531S) {
+-		return 0;
++		switch (freq) {
++		case YTPHY_DTS_OUTPUT_CLK_DIS:
++			mask = YT8531_SCR_SYNCE_ENABLE;
++			val = 0;
++			break;
++		case YTPHY_DTS_OUTPUT_CLK_25M:
++			mask = YT8531_SCR_SYNCE_ENABLE |
++			       YT8531_SCR_CLK_SRC_MASK |
++			       YT8531_SCR_CLK_FRE_SEL_125M;
++			val = YT8531_SCR_SYNCE_ENABLE |
++			      FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++					 YT8531_SCR_CLK_SRC_REF_25M);
++			break;
++		case YTPHY_DTS_OUTPUT_CLK_125M:
++			mask = YT8531_SCR_SYNCE_ENABLE |
++			       YT8531_SCR_CLK_SRC_MASK |
++			       YT8531_SCR_CLK_FRE_SEL_125M;
++			val = YT8531_SCR_SYNCE_ENABLE |
++			      YT8531_SCR_CLK_FRE_SEL_125M |
++			      FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++					 YT8531_SCR_CLK_SRC_PLL_125M);
++			break;
++		default:
++			phydev_warn(phydev, "Freq err:%u\n", freq);
++			return -EINVAL;
++		}
+ 	} else {
+ 		phydev_warn(phydev, "PHY id err\n");
+ 		return -EINVAL;
+@@ -869,26 +892,6 @@ static int yt8521_probe(struct phy_devic
+ }
+ 
+ /**
+- * yt8531s_probe() - read chip config then set suitable polling_mode
+- * @phydev: a pointer to a &struct phy_device
+- *
+- * returns 0 or negative errno code
+- */
+-static int yt8531s_probe(struct phy_device *phydev)
+-{
+-	int ret;
+-
+-	/* Disable SyncE clock output by default */
+-	ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG,
+-					 YT8531S_SCR_SYNCE_ENABLE, 0);
+-	if (ret < 0)
+-		return ret;
+-
+-	/* same as yt8521_probe */
+-	return yt8521_probe(phydev);
+-}
+-
+-/**
+  * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
+  * @phydev: a pointer to a &struct phy_device
+  *
+@@ -1970,7 +1973,7 @@ static struct phy_driver motorcomm_phy_d
+ 		PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
+ 		.name		= "YT8531S Gigabit Ethernet",
+ 		.get_features	= yt8521_get_features,
+-		.probe		= yt8531s_probe,
++		.probe		= yt8521_probe,
+ 		.read_page	= yt8521_read_page,
+ 		.write_page	= yt8521_write_page,
+ 		.get_wol	= ytphy_get_wol,
diff --git a/target/linux/generic/backport-5.15/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch b/target/linux/generic/backport-5.15/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch
new file mode 100644
index 0000000000..9dce5a8add
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch
@@ -0,0 +1,302 @@
+From 4ac94f728a588e7096dd5010cd7141a309ea7805 Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae at motor-comm.com>
+Date: Thu, 2 Feb 2023 11:00:37 +0800
+Subject: [PATCH] net: phy: Add driver for Motorcomm yt8531 gigabit ethernet
+ phy
+
+Add a driver for the motorcomm yt8531 gigabit ethernet phy. We have
+ verified the driver on AM335x platform with yt8531 board. On the
+ board, yt8531 gigabit ethernet phy works in utp mode, RGMII
+ interface, supports 1000M/100M/10M speeds, and wol(magic package).
+
+Signed-off-by: Frank Sae <Frank.Sae at motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew at lunn.ch>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ drivers/net/phy/Kconfig     |   2 +-
+ drivers/net/phy/motorcomm.c | 208 +++++++++++++++++++++++++++++++++++-
+ 2 files changed, 207 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -245,7 +245,7 @@ config MOTORCOMM_PHY
+ 	tristate "Motorcomm PHYs"
+ 	help
+ 	  Enables support for Motorcomm network PHYs.
+-	  Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
++	  Currently supports YT85xx Gigabit Ethernet PHYs.
+ 
+ config NATIONAL_PHY
+ 	tristate "National Semiconductor PHYs"
+--- 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/8531S PHY driver.
++ * Motorcomm 8511/8521/8531/8531S PHY driver.
+  *
+  * Author: Peter Geis <pgwipeout at gmail.com>
+  * Author: Frank <Frank.Sae at motor-comm.com>
+@@ -14,6 +14,7 @@
+ 
+ #define PHY_ID_YT8511		0x0000010a
+ #define PHY_ID_YT8521		0x0000011a
++#define PHY_ID_YT8531		0x4f51e91b
+ #define PHY_ID_YT8531S		0x4f51e91a
+ 
+ /* YT8521/YT8531S Register Overview
+@@ -517,6 +518,61 @@ err_restore_page:
+ 	return phy_restore_page(phydev, old_page, ret);
+ }
+ 
++static int yt8531_set_wol(struct phy_device *phydev,
++			  struct ethtool_wolinfo *wol)
++{
++	const u16 mac_addr_reg[] = {
++		YTPHY_WOL_MACADDR2_REG,
++		YTPHY_WOL_MACADDR1_REG,
++		YTPHY_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 = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
++		val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
++		val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
++		ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
++						 mask, val);
++		if (ret < 0)
++			return ret;
++
++		/* Enable WOL interrupt */
++		ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
++				 YTPHY_IER_WOL);
++		if (ret < 0)
++			return ret;
++	} else {
++		/* Disable WOL feature */
++		mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
++		ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
++						 mask, 0);
++
++		/* Disable WOL interrupt */
++		ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
++				 YTPHY_IER_WOL, 0);
++		if (ret < 0)
++			return ret;
++	}
++
++	return 0;
++}
++
+ static int yt8511_read_page(struct phy_device *phydev)
+ {
+ 	return __phy_read(phydev, YT8511_PAGE_SELECT);
+@@ -767,6 +823,17 @@ static int ytphy_rgmii_clk_delay_config(
+ 	return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
+ }
+ 
++static int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev)
++{
++	int ret;
++
++	phy_lock_mdio_bus(phydev);
++	ret = ytphy_rgmii_clk_delay_config(phydev);
++	phy_unlock_mdio_bus(phydev);
++
++	return ret;
++}
++
+ /**
+  * yt8521_probe() - read chip config then set suitable polling_mode
+  * @phydev: a pointer to a &struct phy_device
+@@ -891,6 +958,43 @@ static int yt8521_probe(struct phy_devic
+ 					  val);
+ }
+ 
++static int yt8531_probe(struct phy_device *phydev)
++{
++	struct device_node *node = phydev->mdio.dev.of_node;
++	u16 mask, val;
++	u32 freq;
++
++	if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
++		freq = YTPHY_DTS_OUTPUT_CLK_DIS;
++
++	switch (freq) {
++	case YTPHY_DTS_OUTPUT_CLK_DIS:
++		mask = YT8531_SCR_SYNCE_ENABLE;
++		val = 0;
++		break;
++	case YTPHY_DTS_OUTPUT_CLK_25M:
++		mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
++		       YT8531_SCR_CLK_FRE_SEL_125M;
++		val = YT8531_SCR_SYNCE_ENABLE |
++		      FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++				 YT8531_SCR_CLK_SRC_REF_25M);
++		break;
++	case YTPHY_DTS_OUTPUT_CLK_125M:
++		mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
++		       YT8531_SCR_CLK_FRE_SEL_125M;
++		val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M |
++		      FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
++				 YT8531_SCR_CLK_SRC_PLL_125M);
++		break;
++	default:
++		phydev_warn(phydev, "Freq err:%u\n", freq);
++		return -EINVAL;
++	}
++
++	return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
++					  val);
++}
++
+ /**
+  * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
+  * @phydev: a pointer to a &struct phy_device
+@@ -1387,6 +1491,94 @@ err_restore_page:
+ 	return phy_restore_page(phydev, old_page, ret);
+ }
+ 
++static int yt8531_config_init(struct phy_device *phydev)
++{
++	struct device_node *node = phydev->mdio.dev.of_node;
++	int ret;
++
++	ret = ytphy_rgmii_clk_delay_config_with_lock(phydev);
++	if (ret < 0)
++		return ret;
++
++	if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
++		/* disable auto sleep */
++		ret = ytphy_modify_ext_with_lock(phydev,
++						 YT8521_EXTREG_SLEEP_CONTROL1_REG,
++						 YT8521_ESC1R_SLEEP_SW, 0);
++		if (ret < 0)
++			return ret;
++	}
++
++	if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
++		/* enable RXC clock when no wire plug */
++		ret = ytphy_modify_ext_with_lock(phydev,
++						 YT8521_CLOCK_GATING_REG,
++						 YT8521_CGR_RX_CLK_EN, 0);
++		if (ret < 0)
++			return ret;
++	}
++
++	return 0;
++}
++
++/**
++ * yt8531_link_change_notify() - Adjust the tx clock direction according to
++ * the current speed and dts config.
++ * @phydev: a pointer to a &struct phy_device
++ *
++ * NOTE: This function is only used to adapt to VF2 with JH7110 SoC. Please
++ * keep "motorcomm,tx-clk-adj-enabled" not exist in dts when the soc is not
++ * JH7110.
++ */
++static void yt8531_link_change_notify(struct phy_device *phydev)
++{
++	struct device_node *node = phydev->mdio.dev.of_node;
++	bool tx_clk_adj_enabled = false;
++	bool tx_clk_1000_inverted;
++	bool tx_clk_100_inverted;
++	bool tx_clk_10_inverted;
++	u16 val = 0;
++	int ret;
++
++	if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled"))
++		tx_clk_adj_enabled = true;
++
++	if (!tx_clk_adj_enabled)
++		return;
++
++	if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted"))
++		tx_clk_10_inverted = true;
++	if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted"))
++		tx_clk_100_inverted = true;
++	if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted"))
++		tx_clk_1000_inverted = true;
++
++	if (phydev->speed < 0)
++		return;
++
++	switch (phydev->speed) {
++	case SPEED_1000:
++		if (tx_clk_1000_inverted)
++			val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
++		break;
++	case SPEED_100:
++		if (tx_clk_100_inverted)
++			val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
++		break;
++	case SPEED_10:
++		if (tx_clk_10_inverted)
++			val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
++		break;
++	default:
++		return;
++	}
++
++	ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG,
++					 YT8521_RC1R_TX_CLK_SEL_INVERTED, val);
++	if (ret < 0)
++		phydev_warn(phydev, "Modify TX_CLK_SEL err:%d\n", ret);
++}
++
+ /**
+  * yt8521_prepare_fiber_features() -  A small helper function that setup
+  * fiber's features.
+@@ -1970,6 +2162,17 @@ static struct phy_driver motorcomm_phy_d
+ 		.resume		= yt8521_resume,
+ 	},
+ 	{
++		PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
++		.name		= "YT8531 Gigabit Ethernet",
++		.probe		= yt8531_probe,
++		.config_init	= yt8531_config_init,
++		.suspend	= genphy_suspend,
++		.resume		= genphy_resume,
++		.get_wol	= ytphy_get_wol,
++		.set_wol	= yt8531_set_wol,
++		.link_change_notify = yt8531_link_change_notify,
++	},
++	{
+ 		PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
+ 		.name		= "YT8531S Gigabit Ethernet",
+ 		.get_features	= yt8521_get_features,
+@@ -1990,7 +2193,7 @@ static struct phy_driver motorcomm_phy_d
+ 
+ module_phy_driver(motorcomm_phy_drvs);
+ 
+-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
++MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver");
+ MODULE_AUTHOR("Peter Geis");
+ MODULE_AUTHOR("Frank");
+ MODULE_LICENSE("GPL");
+@@ -1998,6 +2201,7 @@ MODULE_LICENSE("GPL");
+ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
+ 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
+ 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
++	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
+ 	{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
+ 	{ /* sentinel */ }
+ };
diff --git a/target/linux/generic/backport-5.15/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch b/target/linux/generic/backport-5.15/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch
new file mode 100644
index 0000000000..29ae86dbbc
--- /dev/null
+++ b/target/linux/generic/backport-5.15/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch
@@ -0,0 +1,34 @@
+From 9753613f7399601f9bae6ee81e9d4274246c98ab Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <error27 at gmail.com>
+Date: Wed, 15 Feb 2023 07:21:47 +0300
+Subject: [PATCH] net: phy: motorcomm: uninitialized variables in
+ yt8531_link_change_notify()
+
+These booleans are never set to false, but are just used without being
+initialized.
+
+Fixes: 4ac94f728a58 ("net: phy: Add driver for Motorcomm yt8531 gigabit ethernet phy")
+Signed-off-by: Dan Carpenter <error27 at gmail.com>
+Reviewed-by: Frank Sae <Frank.Sae at motor-comm.com>
+Link: https://lore.kernel.org/r/Y+xd2yJet2ImHLoQ@kili
+Signed-off-by: Jakub Kicinski <kuba at kernel.org>
+---
+ drivers/net/phy/motorcomm.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/motorcomm.c
++++ b/drivers/net/phy/motorcomm.c
+@@ -1533,10 +1533,10 @@ static int yt8531_config_init(struct phy
+ static void yt8531_link_change_notify(struct phy_device *phydev)
+ {
+ 	struct device_node *node = phydev->mdio.dev.of_node;
++	bool tx_clk_1000_inverted = false;
++	bool tx_clk_100_inverted = false;
++	bool tx_clk_10_inverted = false;
+ 	bool tx_clk_adj_enabled = false;
+-	bool tx_clk_1000_inverted;
+-	bool tx_clk_100_inverted;
+-	bool tx_clk_10_inverted;
+ 	u16 val = 0;
+ 	int ret;
+ 
diff --git a/target/linux/generic/backport-5.15/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch b/target/linux/generic/backport-5.15/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch
index 95929b7fe3..2a9dc74947 100644
--- a/target/linux/generic/backport-5.15/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch
+++ b/target/linux/generic/backport-5.15/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch
@@ -19,7 +19,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
 
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
-@@ -17961,6 +17961,11 @@ L:	netdev at vger.kernel.org
+@@ -17962,6 +17962,11 @@ L:	netdev at vger.kernel.org
  S:	Maintained
  F:	drivers/net/ethernet/dlink/sundance.c
  
diff --git a/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch
index df66df794c..e562dd8396 100644
--- a/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch
+++ b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch
@@ -32,7 +32,7 @@ Signed-off-by: Guenter Roeck <linux at roeck-us.net>
 
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
-@@ -15898,6 +15898,13 @@ S:	Maintained
+@@ -15899,6 +15899,13 @@ S:	Maintained
  F:	include/sound/rt*.h
  F:	sound/soc/codecs/rt*
  
diff --git a/target/linux/rockchip/armv8/config-5.15 b/target/linux/rockchip/armv8/config-5.15
index 87715789c7..2359fd8729 100644
--- a/target/linux/rockchip/armv8/config-5.15
+++ b/target/linux/rockchip/armv8/config-5.15
@@ -350,6 +350,7 @@ CONFIG_MMC_SDHCI_OF_DWCMSHC=y
 # CONFIG_MMC_SDHCI_PCI is not set
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MODULES_USE_ELF_RELA=y
+CONFIG_MOTORCOMM_PHY=y
 CONFIG_MQ_IOSCHED_DEADLINE=y
 # CONFIG_MTD_CFI is not set
 CONFIG_MTD_CMDLINE_PARTS=y




More information about the lede-commits mailing list