kernel: backport Motorcomm YT8521/YT8531 support
authorTianling Shen <cnsztl@immortalwrt.org>
Sat, 13 May 2023 05:54:38 +0000 (13:54 +0800)
committerHauke Mehrtens <hauke@hauke-m.de>
Thu, 18 May 2023 15:42:53 +0000 (17:42 +0200)
It will be used on NanoPi R2C and OrangePi R1 Plus LTS board.

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
17 files changed:
target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch
target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch
target/linux/bcm27xx/patches-5.15/950-0520-dt-bindings-media-i2c-Add-IMX519-CMOS-sensor-binding.patch
target/linux/generic/backport-5.15/790-v6.0-net-mii-add-mii_bmcr_encode_fixed.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.2-01-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.2-02-net-phy-fix-yt8521-duplicated-argument-to-or.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.2-03-net-phy-add-Motorcomm-YT8531S-phy-id.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.3-04-net-phy-fix-the-spelling-problem-of-Sentinel.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.3-05-net-phy-motorcomm-change-the-phy-id-of-yt8521-and-yt8531s.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.3-06-net-phy-Add-BIT-macro-for-Motorcomm-yt8521-yt8531-gigabit.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.3-07-net-phy-Add-dts-support-for-Motorcomm-yt8521-gigabit.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.3-08-net-phy-Add-dts-support-for-Motorcomm-yt8531s-gigabit.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.3-09-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/791-v6.3-10-net-phy-motorcomm-uninitialized-variables-in.patch [new file with mode: 0644]
target/linux/generic/backport-5.15/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch
target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch
target/linux/rockchip/armv8/config-5.15

index bbf071cb0175a6b3d17f9bb346a8aaf1ad92a0e5..e756fa2fff6605fa9c23f25dd825d96b4c497113 100644 (file)
@@ -25,7 +25,7 @@ Signed-off-by: Naushir Patuck <naush@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
  
index f0068990690da2b0958c7cceaef203aaeaf87618..27fd0e57938bada839972424fdece77f4053a086 100644 (file)
@@ -132,7 +132,7 @@ Signed-off-by: David Plowman <david.plowman@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@vger.kernel.org
  S:    Maintained
  T:    git git://linuxtv.org/media_tree.git
index dbeb5cb288a6ad785dbdf25c098c4f59ec928c17..25e6555fdb5a402da412e024d9650d325e0ebe62 100644 (file)
@@ -132,7 +132,7 @@ Signed-off-by: Lee Jackson <info@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 (file)
index 0000000..87d7ff6
--- /dev/null
@@ -0,0 +1,55 @@
+From bdb6cfe7512f7a214815a3092f0be50963dcacbc Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@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@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..57137c3
--- /dev/null
@@ -0,0 +1,1724 @@
+From 70479a40954cf353e87a486997a3477108c75aa9 Mon Sep 17 00:00:00 2001
+From: Frank <Frank.Sae@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@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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@gmail.com>
++M:    Frank <Frank.Sae@motor-comm.com>
+ L:    netdev@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@gmail.com>
++ * Author: Frank <Frank.Sae@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 (file)
index 0000000..cce71c8
--- /dev/null
@@ -0,0 +1,49 @@
+From 4e0243e7128c9b25ea2739136076a95d6adaba5e Mon Sep 17 00:00:00 2001
+From: Frank <Frank.Sae@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@intel.com>
+Reported-by: Julia Lawall <julia.lawall@lip6.fr>
+Signed-off-by: Frank <Frank.Sae@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..da60c63
--- /dev/null
@@ -0,0 +1,140 @@
+From 813abcd98fb1b2cccf850cdfa092a4bfc50b2363 Mon Sep 17 00:00:00 2001
+From: Frank <Frank.Sae@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@motor-comm.com>
+Signed-off-by: David S. Miller <davem@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@gmail.com>
+  * Author: Frank <Frank.Sae@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 (file)
index 0000000..94fc32a
--- /dev/null
@@ -0,0 +1,26 @@
+From 4104a713204d62aca482eebb0c6226d82a0721eb Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@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@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20230128063558.5850-1-Frank.Sae@motor-comm.com
+Signed-off-by: Jakub Kicinski <kuba@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 (file)
index 0000000..076fa82
--- /dev/null
@@ -0,0 +1,29 @@
+From 3c1dc22162d673d595855d24f95200ed2643f88f Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@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@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20230128063558.5850-2-Frank.Sae@motor-comm.com
+Signed-off-by: Jakub Kicinski <kuba@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 (file)
index 0000000..ba9a6ab
--- /dev/null
@@ -0,0 +1,107 @@
+From 4869a146cd60fc8115230f0a45e15e534c531922 Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@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@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..6d89fae
--- /dev/null
@@ -0,0 +1,343 @@
+From a6e68f0f8769f79c67cdcfb6302feecd36197dec Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@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@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..86fc046
--- /dev/null
@@ -0,0 +1,100 @@
+From 36152f87dda4af221b16258751451d9cd3d0fb0b Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@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@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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 (file)
index 0000000..9dce5a8
--- /dev/null
@@ -0,0 +1,302 @@
+From 4ac94f728a588e7096dd5010cd7141a309ea7805 Mon Sep 17 00:00:00 2001
+From: Frank Sae <Frank.Sae@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@motor-comm.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@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@gmail.com>
+  * Author: Frank <Frank.Sae@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 (file)
index 0000000..29ae86d
--- /dev/null
@@ -0,0 +1,34 @@
+From 9753613f7399601f9bae6ee81e9d4274246c98ab Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <error27@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@gmail.com>
+Reviewed-by: Frank Sae <Frank.Sae@motor-comm.com>
+Link: https://lore.kernel.org/r/Y+xd2yJet2ImHLoQ@kili
+Signed-off-by: Jakub Kicinski <kuba@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;
index 95929b7fe3c86ee778f77a1d32896db2761c1234..2a9dc749478772c49384fc9422f805ebb46f433b 100644 (file)
@@ -19,7 +19,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
-@@ -17961,6 +17961,11 @@ L:    netdev@vger.kernel.org
+@@ -17962,6 +17962,11 @@ L:    netdev@vger.kernel.org
  S:    Maintained
  F:    drivers/net/ethernet/dlink/sundance.c
  
index df66df794c55ce3ef5c5c0840bd086278d17bbf3..e562dd8396c3df1cdb3b90fe90872e7afe699122 100644 (file)
@@ -32,7 +32,7 @@ Signed-off-by: Guenter Roeck <linux@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*
  
index 87715789c7d2f2bbdfa6df1038e237d46a898dd0..2359fd8729e895f9adafaf807d66ec8efc6309b4 100644 (file)
@@ -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