gl-infra-builder-FUjr/patches-mt798x/2003-kernel-backport-rtl8221b-from-openwrt-master.patch
Jianhui Zhao 15eee0e27c gl-mt6000: new device
```
python3 setup.py -c configs/config-mt798x.yml
cd mt798x
./scripts/gen_config.py target_mt7986_gl-mt6000 glinet_mt7986_wifi glinet_mt6000
make -j $(nproc)

```

Signed-off-by: Jianhui Zhao <jianhui.zhao@gl-inet.com>
2023-05-19 11:29:32 +08:00

924 lines
27 KiB
Diff

From d3356e2dc7a28d315bf950f7c691766acc2b37f8 Mon Sep 17 00:00:00 2001
From: Jianhui Zhao <jianhui.zhao@gl-inet.com>
Date: Fri, 19 May 2023 11:04:29 +0800
Subject: [PATCH] kernel: backport rtl8221b from openwrt master
Signed-off-by: Jianhui Zhao <jianhui.zhao@gl-inet.com>
---
.../hack-5.4/961-backport-rtl8221b.patch | 903 ++++++++++++++++++
1 file changed, 903 insertions(+)
create mode 100644 target/linux/generic/hack-5.4/961-backport-rtl8221b.patch
diff --git a/target/linux/generic/hack-5.4/961-backport-rtl8221b.patch b/target/linux/generic/hack-5.4/961-backport-rtl8221b.patch
new file mode 100644
index 0000000000..0b53a146cf
--- /dev/null
+++ b/target/linux/generic/hack-5.4/961-backport-rtl8221b.patch
@@ -0,0 +1,903 @@
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -1,6 +1,5 @@
+ // SPDX-License-Identifier: GPL-2.0+
+-/*
+- * drivers/net/phy/realtek.c
++/* drivers/net/phy/realtek.c
+ *
+ * Driver for Realtek PHYs
+ *
+@@ -9,8 +8,10 @@
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ */
+ #include <linux/bitops.h>
++#include <linux/of.h>
+ #include <linux/phy.h>
+ #include <linux/module.h>
++#include <linux/delay.h>
+
+ #define RTL821x_PHYSR 0x11
+ #define RTL821x_PHYSR_DUPLEX BIT(13)
+@@ -26,26 +27,56 @@
+ #define RTL821x_EXT_PAGE_SELECT 0x1e
+ #define RTL821x_PAGE_SELECT 0x1f
+
++#define RTL8211F_PHYCR1 0x18
++#define RTL8211F_PHYCR2 0x19
+ #define RTL8211F_INSR 0x1d
+
+ #define RTL8211F_TX_DELAY BIT(8)
+-#define RTL8211E_TX_DELAY BIT(1)
+-#define RTL8211E_RX_DELAY BIT(2)
+-#define RTL8211E_MODE_MII_GMII BIT(3)
++#define RTL8211F_RX_DELAY BIT(3)
++
++#define RTL8211F_ALDPS_PLL_OFF BIT(1)
++#define RTL8211F_ALDPS_ENABLE BIT(2)
++#define RTL8211F_ALDPS_XTAL_OFF BIT(12)
++
++#define RTL8211E_CTRL_DELAY BIT(13)
++#define RTL8211E_TX_DELAY BIT(12)
++#define RTL8211E_RX_DELAY BIT(11)
++
++#define RTL8211F_CLKOUT_EN BIT(0)
+
+ #define RTL8201F_ISR 0x1e
++#define RTL8201F_ISR_ANERR BIT(15)
++#define RTL8201F_ISR_DUPLEX BIT(13)
++#define RTL8201F_ISR_LINK BIT(11)
++#define RTL8201F_ISR_MASK (RTL8201F_ISR_ANERR | \
++ RTL8201F_ISR_DUPLEX | \
++ RTL8201F_ISR_LINK)
+ #define RTL8201F_IER 0x13
+
++#define RTL8221B_MMD_SERDES_CTRL MDIO_MMD_VEND1
++#define RTL8221B_MMD_PHY_CTRL MDIO_MMD_VEND2
++#define RTL8221B_SERDES_OPTION 0x697a
++#define RTL8221B_SERDES_OPTION_MODE_MASK GENMASK(5, 0)
++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII 0
++#define RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII 1
++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX 2
++#define RTL8221B_SERDES_OPTION_MODE_HISGMII 3
++
++#define RTL8221B_PHYCR1 0xa430
++#define RTL8221B_PHYCR1_ALDPS_EN BIT(2)
++#define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12)
++
+ #define RTL8366RB_POWER_SAVE 0x15
+ #define RTL8366RB_POWER_SAVE_ON BIT(12)
+
+ #define RTL_SUPPORTS_5000FULL BIT(14)
+ #define RTL_SUPPORTS_2500FULL BIT(13)
+ #define RTL_SUPPORTS_10000FULL BIT(0)
+-#define RTL_ADV_2500FULL BIT(7)
+-#define RTL_LPADV_10000FULL BIT(11)
+-#define RTL_LPADV_5000FULL BIT(6)
+-#define RTL_LPADV_2500FULL BIT(5)
++
++#define RTL9000A_GINMR 0x14
++#define RTL9000A_GINMR_LINK_STATUS BIT(4)
++
++#define RTLGEN_SPEED_MASK 0x0630
+
+ #define RTL_GENERIC_PHYID 0x001cc800
+
+@@ -53,6 +84,11 @@ MODULE_DESCRIPTION("Realtek PHY driver")
+ MODULE_AUTHOR("Johnson Leung");
+ MODULE_LICENSE("GPL");
+
++struct rtl821x_priv {
++ u16 phycr1;
++ u16 phycr2;
++};
++
+ static int rtl821x_read_page(struct phy_device *phydev)
+ {
+ return __phy_read(phydev, RTL821x_PAGE_SELECT);
+@@ -63,6 +99,37 @@ static int rtl821x_write_page(struct phy
+ return __phy_write(phydev, RTL821x_PAGE_SELECT, page);
+ }
+
++static int rtl821x_probe(struct phy_device *phydev)
++{
++ struct device *dev = &phydev->mdio.dev;
++ struct rtl821x_priv *priv;
++ int ret;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR1);
++ if (ret < 0)
++ return ret;
++
++ priv->phycr1 = ret & (RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF);
++ if (of_property_read_bool(dev->of_node, "realtek,aldps-enable"))
++ priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF;
++
++ ret = phy_read_paged(phydev, 0xa43, RTL8211F_PHYCR2);
++ if (ret < 0)
++ return ret;
++
++ priv->phycr2 = ret & RTL8211F_CLKOUT_EN;
++ if (of_property_read_bool(dev->of_node, "realtek,clkout-disable"))
++ priv->phycr2 &= ~RTL8211F_CLKOUT_EN;
++
++ phydev->priv = priv;
++
++ return 0;
++}
++
+ static int rtl8201_ack_interrupt(struct phy_device *phydev)
+ {
+ int err;
+@@ -93,24 +160,45 @@ static int rtl8211f_ack_interrupt(struct
+ static int rtl8201_config_intr(struct phy_device *phydev)
+ {
+ u16 val;
++ int err;
++
++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
++ err = rtl8201_ack_interrupt(phydev);
++ if (err)
++ return err;
+
+- if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ val = BIT(13) | BIT(12) | BIT(11);
+- else
++ err = phy_write_paged(phydev, 0x7, RTL8201F_IER, val);
++ } else {
+ val = 0;
++ err = phy_write_paged(phydev, 0x7, RTL8201F_IER, val);
++ if (err)
++ return err;
+
+- return phy_write_paged(phydev, 0x7, RTL8201F_IER, val);
++ err = rtl8201_ack_interrupt(phydev);
++ }
++
++ return err;
+ }
+
+ static int rtl8211b_config_intr(struct phy_device *phydev)
+ {
+ int err;
+
+- if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
++ err = rtl821x_ack_interrupt(phydev);
++ if (err)
++ return err;
++
+ err = phy_write(phydev, RTL821x_INER,
+ RTL8211B_INER_INIT);
+- else
++ } else {
+ err = phy_write(phydev, RTL821x_INER, 0);
++ if (err)
++ return err;
++
++ err = rtl821x_ack_interrupt(phydev);
++ }
+
+ return err;
+ }
+@@ -119,11 +207,20 @@ static int rtl8211e_config_intr(struct p
+ {
+ int err;
+
+- if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
++ err = rtl821x_ack_interrupt(phydev);
++ if (err)
++ return err;
++
+ err = phy_write(phydev, RTL821x_INER,
+ RTL8211E_INER_LINK_STATUS);
+- else
++ } else {
+ err = phy_write(phydev, RTL821x_INER, 0);
++ if (err)
++ return err;
++
++ err = rtl821x_ack_interrupt(phydev);
++ }
+
+ return err;
+ }
+@@ -131,13 +228,85 @@ static int rtl8211e_config_intr(struct p
+ static int rtl8211f_config_intr(struct phy_device *phydev)
+ {
+ u16 val;
++ int err;
++
++ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
++ err = rtl8211f_ack_interrupt(phydev);
++ if (err)
++ return err;
+
+- if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ val = RTL8211F_INER_LINK_STATUS;
+- else
++ err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val);
++ } else {
+ val = 0;
++ err = phy_write_paged(phydev, 0xa42, RTL821x_INER, val);
++ if (err)
++ return err;
++
++ err = rtl8211f_ack_interrupt(phydev);
++ }
+
+- return phy_write_paged(phydev, 0xa42, RTL821x_INER, val);
++ return err;
++}
++
++static int rtl8201_handle_interrupt(struct phy_device *phydev)
++{
++ int irq_status;
++
++ irq_status = phy_read(phydev, RTL8201F_ISR);
++ if (irq_status < 0) {
++ phydev_err(phydev, "Err: read RTL8201F_ISR: %d\n", irq_status);
++ return -1;
++ }
++
++ if (!(irq_status & RTL8201F_ISR_MASK))
++ return -1;
++
++ phy_queue_state_machine(phydev, 0);
++
++ return 0;
++}
++
++static int rtl821x_handle_interrupt(struct phy_device *phydev)
++{
++ int irq_status, irq_enabled;
++
++ irq_status = phy_read(phydev, RTL821x_INSR);
++ if (irq_status < 0) {
++ phydev_err(phydev, "Err: read RTL821x_INSR: %d\n", irq_status);
++ return -1;
++ }
++
++ irq_enabled = phy_read(phydev, RTL821x_INER);
++ if (irq_enabled < 0) {
++ phydev_err(phydev, "Err: read RTL821x_INER: %d\n", irq_enabled);
++ return -1;
++ }
++
++ if (!(irq_status & irq_enabled))
++ return -1;
++
++ phy_queue_state_machine(phydev, 0);
++
++ return 0;
++}
++
++static int rtl8211f_handle_interrupt(struct phy_device *phydev)
++{
++ int irq_status;
++
++ irq_status = phy_read_paged(phydev, 0xa43, RTL8211F_INSR);
++ if (irq_status < 0) {
++ phydev_err(phydev, "Err: read RTL8211F_INSR: %d\n", irq_status);
++ return -1;
++ }
++
++ if (!(irq_status & RTL8211F_INER_LINK_STATUS))
++ return -1;
++
++ phy_queue_state_machine(phydev, 0);
++
++ return 0;
+ }
+
+ static int rtl8211_config_aneg(struct phy_device *phydev)
+@@ -171,41 +340,96 @@ static int rtl8211c_config_init(struct p
+
+ static int rtl8211f_config_init(struct phy_device *phydev)
+ {
++ struct rtl821x_priv *priv = phydev->priv;
+ struct device *dev = &phydev->mdio.dev;
+- u16 val;
++ u16 val_txdly, val_rxdly;
+ int ret;
+
+- /* enable TX-delay for rgmii-{id,txid}, and disable it for rgmii and
+- * rgmii-rxid. The RX-delay can be enabled by the external RXDLY pin.
+- */
++ ret = phy_modify_paged_changed(phydev, 0xa43, RTL8211F_PHYCR1,
++ RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF,
++ priv->phycr1);
++ if (ret < 0) {
++ dev_err(dev, "aldps mode configuration failed: %pe\n",
++ ERR_PTR(ret));
++ return ret;
++ }
++
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
++ val_txdly = 0;
++ val_rxdly = 0;
++ break;
++
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+- val = 0;
++ val_txdly = 0;
++ val_rxdly = RTL8211F_RX_DELAY;
+ break;
+- case PHY_INTERFACE_MODE_RGMII_ID:
++
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+- val = RTL8211F_TX_DELAY;
++ val_txdly = RTL8211F_TX_DELAY;
++ val_rxdly = 0;
+ break;
++
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ val_txdly = RTL8211F_TX_DELAY;
++ val_rxdly = RTL8211F_RX_DELAY;
++ break;
++
+ default: /* the rest of the modes imply leaving delay as is. */
+ return 0;
+ }
+
+ ret = phy_modify_paged_changed(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY,
+- val);
++ val_txdly);
+ if (ret < 0) {
+ dev_err(dev, "Failed to update the TX delay register\n");
+ return ret;
+ } else if (ret) {
+ dev_dbg(dev,
+ "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n",
+- val ? "Enabling" : "Disabling");
++ val_txdly ? "Enabling" : "Disabling");
+ } else {
+ dev_dbg(dev,
+ "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
+- val ? "enabled" : "disabled");
++ val_txdly ? "enabled" : "disabled");
++ }
++
++ ret = phy_modify_paged_changed(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY,
++ val_rxdly);
++ if (ret < 0) {
++ dev_err(dev, "Failed to update the RX delay register\n");
++ return ret;
++ } else if (ret) {
++ dev_dbg(dev,
++ "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n",
++ val_rxdly ? "Enabling" : "Disabling");
++ } else {
++ dev_dbg(dev,
++ "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n",
++ val_rxdly ? "enabled" : "disabled");
+ }
+
++ ret = phy_modify_paged(phydev, 0xa43, RTL8211F_PHYCR2,
++ RTL8211F_CLKOUT_EN, priv->phycr2);
++ if (ret < 0) {
++ dev_err(dev, "clkout configuration failed: %pe\n",
++ ERR_PTR(ret));
++ return ret;
++ }
++
++ return genphy_soft_reset(phydev);
++}
++
++static int rtl821x_resume(struct phy_device *phydev)
++{
++ int ret;
++
++ ret = genphy_resume(phydev);
++ if (ret < 0)
++ return ret;
++
++ msleep(20);
++
+ return 0;
+ }
+
+@@ -217,16 +441,16 @@ static int rtl8211e_config_init(struct p
+ /* enable TX/RX delay for rgmii-* modes, and disable them for rgmii. */
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII:
+- val = 0;
++ val = RTL8211E_CTRL_DELAY | 0;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+- val = RTL8211E_TX_DELAY | RTL8211E_RX_DELAY;
++ val = RTL8211E_CTRL_DELAY | RTL8211E_TX_DELAY | RTL8211E_RX_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+- val = RTL8211E_RX_DELAY;
++ val = RTL8211E_CTRL_DELAY | RTL8211E_RX_DELAY;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+- val = RTL8211E_TX_DELAY;
++ val = RTL8211E_CTRL_DELAY | RTL8211E_TX_DELAY;
+ break;
+ default: /* the rest of the modes imply leaving delays as is. */
+ return 0;
+@@ -234,11 +458,12 @@ static int rtl8211e_config_init(struct p
+
+ /* According to a sample driver there is a 0x1c config register on the
+ * 0xa4 extension page (0x7) layout. It can be used to disable/enable
+- * the RX/TX delays otherwise controlled by RXDLY/TXDLY pins. It can
+- * also be used to customize the whole configuration register:
+- * 8:6 = PHY Address, 5:4 = Auto-Negotiation, 3 = Interface Mode Select,
+- * 2 = RX Delay, 1 = TX Delay, 0 = SELRGV (see original PHY datasheet
+- * for details).
++ * the RX/TX delays otherwise controlled by RXDLY/TXDLY pins.
++ * The configuration register definition:
++ * 14 = reserved
++ * 13 = Force Tx RX Delay controlled by bit12 bit11,
++ * 12 = RX Delay, 11 = TX Delay
++ * 10:0 = Test && debug settings reserved by realtek
+ */
+ oldpage = phy_select_page(phydev, 0x7);
+ if (oldpage < 0)
+@@ -248,7 +473,8 @@ static int rtl8211e_config_init(struct p
+ if (ret)
+ goto err_restore_page;
+
+- ret = __phy_modify(phydev, 0x1c, RTL8211E_TX_DELAY | RTL8211E_RX_DELAY,
++ ret = __phy_modify(phydev, 0x1c, RTL8211E_CTRL_DELAY
++ | RTL8211E_TX_DELAY | RTL8211E_RX_DELAY,
+ val);
+
+ err_restore_page:
+@@ -269,18 +495,53 @@ static int rtl8211b_resume(struct phy_de
+ return genphy_resume(phydev);
+ }
+
+-static int rtl8366rb_config_init(struct phy_device *phydev)
++/* get actual speed to cover the downshift case */
++static int rtlgen_get_speed(struct phy_device *phydev)
+ {
+- int ret;
++ int val;
+
+- ret = phy_set_bits(phydev, RTL8366RB_POWER_SAVE,
+- RTL8366RB_POWER_SAVE_ON);
+- if (ret) {
+- dev_err(&phydev->mdio.dev,
+- "error enabling power management\n");
++ if (!phydev->link)
++ return 0;
++
++ val = phy_read_paged(phydev, 0xa43, 0x12);
++ if (val < 0)
++ return val;
++
++ switch (val & RTLGEN_SPEED_MASK) {
++ case 0x0000:
++ phydev->speed = SPEED_10;
++ break;
++ case 0x0010:
++ phydev->speed = SPEED_100;
++ break;
++ case 0x0020:
++ phydev->speed = SPEED_1000;
++ break;
++ case 0x0200:
++ phydev->speed = SPEED_10000;
++ break;
++ case 0x0210:
++ phydev->speed = SPEED_2500;
++ break;
++ case 0x0220:
++ phydev->speed = SPEED_5000;
++ break;
++ default:
++ break;
+ }
+
+- return ret;
++ return 0;
++}
++
++static int rtlgen_read_status(struct phy_device *phydev)
++{
++ int ret;
++
++ ret = genphy_read_status(phydev);
++ if (ret < 0)
++ return ret;
++
++ return rtlgen_get_speed(phydev);
+ }
+
+ static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
+@@ -322,7 +583,7 @@ static int rtlgen_write_mmd(struct phy_d
+ return ret;
+ }
+
+-static int rtl8125_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
++static int rtl822x_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
+ {
+ int ret = rtlgen_read_mmd(phydev, devnum, regnum);
+
+@@ -346,7 +607,7 @@ static int rtl8125_read_mmd(struct phy_d
+ return ret;
+ }
+
+-static int rtl8125_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
++static int rtl822x_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
+ u16 val)
+ {
+ int ret = rtlgen_write_mmd(phydev, devnum, regnum, val);
+@@ -363,7 +624,7 @@ static int rtl8125_write_mmd(struct phy_
+ return ret;
+ }
+
+-static int rtl8125_get_features(struct phy_device *phydev)
++static int rtl822x_get_features(struct phy_device *phydev)
+ {
+ int val;
+
+@@ -381,19 +642,16 @@ static int rtl8125_get_features(struct p
+ return genphy_read_abilities(phydev);
+ }
+
+-static int rtl8125_config_aneg(struct phy_device *phydev)
++static int rtl822x_config_aneg(struct phy_device *phydev)
+ {
+ int ret = 0;
+
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+- u16 adv2500 = 0;
+-
+- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+- phydev->advertising))
+- adv2500 = RTL_ADV_2500FULL;
+-
+ ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
+- RTL_ADV_2500FULL, adv2500);
++ MDIO_AN_10GBT_CTRL_ADV10G |
++ MDIO_AN_10GBT_CTRL_ADV5G |
++ MDIO_AN_10GBT_CTRL_ADV2_5G,
++ linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising));
+ if (ret < 0)
+ return ret;
+ }
+@@ -401,32 +659,61 @@ static int rtl8125_config_aneg(struct ph
+ return __genphy_config_aneg(phydev, ret);
+ }
+
+-static int rtl8125_read_status(struct phy_device *phydev)
++static void rtl822x_update_interface(struct phy_device *phydev)
++{
++ /* Automatically switch SERDES interface between
++ * SGMII and 2500-BaseX according to speed.
++ */
++ switch (phydev->speed) {
++ case SPEED_2500:
++ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
++ break;
++ case SPEED_1000:
++ case SPEED_100:
++ case SPEED_10:
++ phydev->interface = PHY_INTERFACE_MODE_SGMII;
++ break;
++ default:
++ break;
++ }
++}
++
++static int rtl822x_read_status(struct phy_device *phydev)
+ {
++ int ret;
++
+ if (phydev->autoneg == AUTONEG_ENABLE) {
+ int lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
+
+ if (lpadv < 0)
+ return lpadv;
+
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+- phydev->lp_advertising, lpadv & RTL_LPADV_10000FULL);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
+- phydev->lp_advertising, lpadv & RTL_LPADV_5000FULL);
+- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+- phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL);
++ if (!(lpadv & MDIO_AN_10GBT_STAT_REMOK) ||
++ !(lpadv & MDIO_AN_10GBT_STAT_LOCOK))
++ lpadv = 0;
++
++ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv);
+ }
+
+- return genphy_read_status(phydev);
++ ret = rtlgen_read_status(phydev);
++ if (ret < 0)
++ return ret;
++
++ if (phydev->is_c45 && phydev->link)
++ rtl822x_update_interface(phydev);
++
++ return 0;
+ }
+
+ static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
+ {
+ int val;
+
+- phy_write(phydev, RTL821x_PAGE_SELECT, 0xa61);
+- val = phy_read(phydev, 0x13);
+- phy_write(phydev, RTL821x_PAGE_SELECT, 0);
++ mutex_lock(&phydev->mdio.bus->mdio_lock);
++ rtl821x_write_page(phydev, 0xa61);
++ val = __phy_read(phydev, 0x13);
++ rtl821x_write_page(phydev, 0);
++ mutex_unlock(&phydev->mdio.bus->mdio_lock);
+
+ return val >= 0 && val & RTL_SUPPORTS_2500FULL;
+ }
+@@ -437,21 +724,102 @@ static int rtlgen_match_phy_device(struc
+ !rtlgen_supports_2_5gbps(phydev);
+ }
+
+-static int rtl8125_match_phy_device(struct phy_device *phydev)
++static int rtl8226_match_phy_device(struct phy_device *phydev)
+ {
+ return phydev->phy_id == RTL_GENERIC_PHYID &&
+ rtlgen_supports_2_5gbps(phydev);
+ }
+
++static int rtl822x_probe(struct phy_device *phydev)
++{
++ struct device *dev = &phydev->mdio.dev;
++ int val;
++
++ val = phy_read_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_PHYCR1);
++ if (val < 0)
++ return val;
++
++ if (of_property_read_bool(dev->of_node, "realtek,aldps-enable"))
++ val |= RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN;
++ else
++ val &= ~(RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN);
++
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_PHYCR1, val);
++
++ return 0;
++}
++
++static int rtlgen_resume(struct phy_device *phydev)
++{
++ int ret = genphy_resume(phydev);
++
++ /* Internal PHY's from RTL8168h up may not be instantly ready */
++ msleep(20);
++
++ return ret;
++}
++
++static int rtl8221b_config_init(struct phy_device *phydev)
++{
++ u16 option_mode;
++ int val;
++
++ switch (phydev->interface) {
++ case PHY_INTERFACE_MODE_2500BASEX:
++ if (!phydev->is_c45) {
++ option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX;
++ break;
++ }
++ fallthrough;
++ case PHY_INTERFACE_MODE_SGMII:
++ option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII;
++ break;
++ default:
++ return 0;
++ }
++
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL,
++ 0x75f3, 0);
++
++ phy_modify_mmd_changed(phydev, RTL8221B_MMD_SERDES_CTRL,
++ RTL8221B_SERDES_OPTION,
++ RTL8221B_SERDES_OPTION_MODE_MASK, option_mode);
++ switch (option_mode) {
++ case RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII:
++ case RTL8221B_SERDES_OPTION_MODE_2500BASEX:
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd455);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020);
++ break;
++ case RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII:
++ case RTL8221B_SERDES_OPTION_MODE_HISGMII:
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd433);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020);
++ break;
++ }
++
++ /* Disable SGMII AN */
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7588, 0x2);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7589, 0x71d0);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7587, 0x3);
++ phy_read_mmd_poll_timeout(phydev, RTL8221B_MMD_SERDES_CTRL, 0x7587,
++ val, !(val & BIT(0)), 500, 100000, false);
++
++ return 0;
++}
++
+ static struct phy_driver realtek_drvs[] = {
+ {
+ PHY_ID_MATCH_EXACT(0x00008201),
+ .name = "RTL8201CP Ethernet",
++ .read_page = rtl821x_read_page,
++ .write_page = rtl821x_write_page,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc816),
+ .name = "RTL8201F Fast Ethernet",
+- .ack_interrupt = &rtl8201_ack_interrupt,
+ .config_intr = &rtl8201_config_intr,
++ .handle_interrupt = rtl8201_handle_interrupt,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .read_page = rtl821x_read_page,
+@@ -476,8 +844,8 @@ static struct phy_driver realtek_drvs[]
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc912),
+ .name = "RTL8211B Gigabit Ethernet",
+- .ack_interrupt = &rtl821x_ack_interrupt,
+ .config_intr = &rtl8211b_config_intr,
++ .handle_interrupt = rtl821x_handle_interrupt,
+ .read_mmd = &genphy_read_mmd_unsupported,
+ .write_mmd = &genphy_write_mmd_unsupported,
+ .suspend = rtl8211b_suspend,
+@@ -495,8 +863,8 @@ static struct phy_driver realtek_drvs[]
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc914),
+ .name = "RTL8211DN Gigabit Ethernet",
+- .ack_interrupt = rtl821x_ack_interrupt,
+ .config_intr = rtl8211e_config_intr,
++ .handle_interrupt = rtl821x_handle_interrupt,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .read_page = rtl821x_read_page,
+@@ -505,8 +873,8 @@ static struct phy_driver realtek_drvs[]
+ PHY_ID_MATCH_EXACT(0x001cc915),
+ .name = "RTL8211E Gigabit Ethernet",
+ .config_init = &rtl8211e_config_init,
+- .ack_interrupt = &rtl821x_ack_interrupt,
+ .config_intr = &rtl8211e_config_intr,
++ .handle_interrupt = rtl821x_handle_interrupt,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .read_page = rtl821x_read_page,
+@@ -514,47 +882,103 @@ static struct phy_driver realtek_drvs[]
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc916),
+ .name = "RTL8211F Gigabit Ethernet",
++ .probe = rtl821x_probe,
+ .config_init = &rtl8211f_config_init,
+- .ack_interrupt = &rtl8211f_ack_interrupt,
++ .read_status = rtlgen_read_status,
+ .config_intr = &rtl8211f_config_intr,
++ .handle_interrupt = rtl8211f_handle_interrupt,
+ .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .resume = rtl821x_resume,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
+ }, {
+ .name = "Generic FE-GE Realtek PHY",
+ .match_phy_device = rtlgen_match_phy_device,
++ .read_status = rtlgen_read_status,
+ .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .resume = rtlgen_resume,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
+ .read_mmd = rtlgen_read_mmd,
+ .write_mmd = rtlgen_write_mmd,
+ }, {
+- .name = "RTL8125 2.5Gbps internal",
+- .match_phy_device = rtl8125_match_phy_device,
+- .get_features = rtl8125_get_features,
+- .config_aneg = rtl8125_config_aneg,
+- .read_status = rtl8125_read_status,
++ .name = "RTL8226 2.5Gbps PHY",
++ .match_phy_device = rtl8226_match_phy_device,
++ .get_features = rtl822x_get_features,
++ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
++ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .resume = rtlgen_resume,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
+- .read_mmd = rtl8125_read_mmd,
+- .write_mmd = rtl8125_write_mmd,
+- }, {
+- PHY_ID_MATCH_EXACT(0x001cc961),
+- .name = "RTL8366RB Gigabit Ethernet",
+- .config_init = &rtl8366rb_config_init,
+- /* These interrupts are handled by the irq controller
+- * embedded inside the RTL8366RB, they get unmasked when the
+- * irq is requested and ACKed by reading the status register,
+- * which is done by the irqchip code.
+- */
+- .ack_interrupt = genphy_no_ack_interrupt,
+- .config_intr = genphy_no_config_intr,
++ .read_mmd = rtl822x_read_mmd,
++ .write_mmd = rtl822x_write_mmd,
++ .soft_reset = genphy_soft_reset,
++ }, {
++ PHY_ID_MATCH_EXACT(0x001cc840),
++ .name = "RTL8226B_RTL8221B 2.5Gbps PHY",
++ .get_features = rtl822x_get_features,
++ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
++ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+- .resume = genphy_resume,
++ .resume = rtlgen_resume,
++ .read_page = rtl821x_read_page,
++ .write_page = rtl821x_write_page,
++ .read_mmd = rtl822x_read_mmd,
++ .write_mmd = rtl822x_write_mmd,
++ .soft_reset = genphy_soft_reset,
++ }, {
++ PHY_ID_MATCH_EXACT(0x001cc838),
++ .name = "RTL8226-CG 2.5Gbps PHY",
++ .get_features = rtl822x_get_features,
++ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
++ .read_status = rtl822x_read_status,
++ .suspend = genphy_suspend,
++ .resume = rtlgen_resume,
++ .read_page = rtl821x_read_page,
++ .write_page = rtl821x_write_page,
++ .soft_reset = genphy_soft_reset,
++ }, {
++ PHY_ID_MATCH_EXACT(0x001cc848),
++ .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
++ .get_features = rtl822x_get_features,
++ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
++ .read_status = rtl822x_read_status,
++ .suspend = genphy_suspend,
++ .resume = rtlgen_resume,
++ .read_page = rtl821x_read_page,
++ .write_page = rtl821x_write_page,
++ .soft_reset = genphy_soft_reset,
++ }, {
++ PHY_ID_MATCH_EXACT(0x001cc849),
++ .name = "RTL8221B-VB-CG 2.5Gbps PHY",
++ .get_features = rtl822x_get_features,
++ .config_init = rtl8221b_config_init,
++ .config_aneg = rtl822x_config_aneg,
++ .probe = rtl822x_probe,
++ .read_status = rtl822x_read_status,
++ .suspend = genphy_suspend,
++ .resume = rtlgen_resume,
++ .read_page = rtl821x_read_page,
++ .write_page = rtl821x_write_page,
++ .soft_reset = genphy_soft_reset,
++ }, {
++ PHY_ID_MATCH_EXACT(0x001cc84a),
++ .name = "RTL8221B-VM-CG 2.5Gbps PHY",
++ .get_features = rtl822x_get_features,
++ .config_aneg = rtl822x_config_aneg,
++ .config_init = rtl8221b_config_init,
++ .probe = rtl822x_probe,
++ .read_status = rtl822x_read_status,
++ .suspend = genphy_suspend,
++ .resume = rtlgen_resume,
++ .read_page = rtl821x_read_page,
++ .write_page = rtl821x_write_page,
++ .soft_reset = genphy_soft_reset,
+ },
+ };
+
--
2.34.1