]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
uboot-mediatek: update Airoha EN8811H driver 20146/head
authorTianling Shen <cnsztl@immortalwrt.org>
Tue, 9 Sep 2025 14:20:49 +0000 (22:20 +0800)
committerRobert Marko <robimarko@gmail.com>
Wed, 24 Sep 2025 11:31:49 +0000 (13:31 +0200)
Use the version sent to upstream U-Boot[1], which has much better
code quality and also works better (without packet loss).

The `en8811h_read_fw` func is adapted to use current read logic.

1. https://lore.kernel.org/u-boot/20250720122852.22563-1-lucienzx159@gmail.com/

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
Link: https://github.com/openwrt/openwrt/pull/20000
(cherry picked from commit b63a48b012b7af0da14ea04ff345512254dc69d6)
Link: https://github.com/openwrt/openwrt/pull/20146
Signed-off-by: Robert Marko <robimarko@gmail.com>
package/boot/uboot-mediatek/patches/160-net-phy-add-support-for-Airoha-ethernet-PHY-driver.patch
package/boot/uboot-mediatek/patches/442-add-bpi-r3-mini.patch

index 2a71a4eb9249d91a998426964dc6f4d4ac4ab0c4..cb7ef3089e17819e78e4b3e14e430392009e311f 100644 (file)
@@ -1015,26 +1015,31 @@ Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
 +#endif /* __EN8801S_H */
 --- /dev/null
 +++ b/drivers/net/phy/air_en8811h.c
-@@ -0,0 +1,725 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*************************************************
-+ * FILE NAME:  air_en8811h.c
-+ * PURPOSE:
-+ *      EN8811H PHY Driver for Uboot
-+ * NOTES:
+@@ -0,0 +1,886 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Driver for the Airoha EN8811H 2.5 Gigabit PHY.
 + *
-+ *  Copyright (C) 2023 Airoha Technology Corp.
-+ *************************************************/
-+
-+/* INCLUDE FILE DECLARATIONS
-+*/
-+#include <config.h>
-+#include <eth_phy.h>
++ * Limitations of the EN8811H:
++ * - Only full duplex supported
++ * - Forced speed (AN off) is not supported by hardware (100Mbps)
++ *
++ * Source originated from linux air_en8811h.c
++ *
++ * Copyright (C) 2025 Airoha Technology Corp.
++ */
 +#include <phy.h>
 +#include <errno.h>
++#include <log.h>
++#include <env.h>
 +#include <malloc.h>
-+#include <version.h>
-+#include "air_en8811h.h"
++#include <fs.h>
++#include <asm/unaligned.h>
++#include <linux/iopoll.h>
++#include <linux/bitops.h>
++#include <linux/compat.h>
++#include <dm/device_compat.h>
++#include <u-boot/crc.h>
 +
 +#ifdef CONFIG_PHY_AIROHA_FW_IN_UBI
 +#include <ubi_uboot.h>
@@ -1048,882 +1053,852 @@ Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
 +#include <mtd.h>
 +#endif
 +
-+#if AIR_UBOOT_REVISION > 0x202004
-+#include <linux/delay.h>
-+#endif
++#define EN8811H_PHY_ID                0x03a2a411
++
++#define AIR_FW_ADDR_DM        0x00000000
++#define AIR_FW_ADDR_DSP       0x00100000
++
++#define EN8811H_MD32_DM_SIZE  0x4000
++#define EN8811H_MD32_DSP_SIZE 0x20000
++
++ #define EN8811H_FW_CTRL_1            0x0f0018
++ #define EN8811H_FW_CTRL_1_START      0x0
++ #define EN8811H_FW_CTRL_1_FINISH     0x1
++ #define EN8811H_FW_CTRL_2            0x800000
++ #define EN8811H_FW_CTRL_2_LOADING    BIT(11)
++
++ /* MII Registers */
++ #define AIR_AUX_CTRL_STATUS          0x1d
++ #define AIR_AUX_CTRL_STATUS_SPEED_MASK       GENMASK(4, 2)
++ #define AIR_AUX_CTRL_STATUS_SPEED_100        0x4
++ #define AIR_AUX_CTRL_STATUS_SPEED_1000       0x8
++ #define AIR_AUX_CTRL_STATUS_SPEED_2500       0xc
++
++#define AIR_EXT_PAGE_ACCESS           0x1f
++#define AIR_PHY_PAGE_STANDARD         0x0000
++#define AIR_PHY_PAGE_EXTENDED_4               0x0004
++
++/* MII Registers Page 4*/
++#define AIR_BPBUS_MODE                        0x10
++#define AIR_BPBUS_MODE_ADDR_FIXED     0x0000
++#define AIR_BPBUS_MODE_ADDR_INCR      BIT(15)
++#define AIR_BPBUS_WR_ADDR_HIGH                0x11
++#define AIR_BPBUS_WR_ADDR_LOW         0x12
++#define AIR_BPBUS_WR_DATA_HIGH                0x13
++#define AIR_BPBUS_WR_DATA_LOW         0x14
++#define AIR_BPBUS_RD_ADDR_HIGH                0x15
++#define AIR_BPBUS_RD_ADDR_LOW         0x16
++#define AIR_BPBUS_RD_DATA_HIGH                0x17
++#define AIR_BPBUS_RD_DATA_LOW         0x18
++
++/* Registers on MDIO_MMD_VEND1 */
++#define EN8811H_PHY_FW_STATUS         0x8009
++#define EN8811H_PHY_READY             0x02
++
++/* Registers on MDIO_MMD_VEND2 */
++#define AIR_PHY_LED_BCR                       0x021
++#define AIR_PHY_LED_BCR_MODE_MASK     GENMASK(1, 0)
++#define AIR_PHY_LED_BCR_TIME_TEST     BIT(2)
++#define AIR_PHY_LED_BCR_CLK_EN                BIT(3)
++#define AIR_PHY_LED_BCR_EXT_CTRL      BIT(15)
++
++#define AIR_PHY_LED_DUR_ON            0x022
++
++#define AIR_PHY_LED_DUR_BLINK         0x023
++
++#define AIR_PHY_LED_ON(i)            (0x024 + ((i) * 2))
++#define AIR_PHY_LED_ON_MASK           (GENMASK(6, 0) | BIT(8))
++#define AIR_PHY_LED_ON_LINK1000               BIT(0)
++#define AIR_PHY_LED_ON_LINK100                BIT(1)
++#define AIR_PHY_LED_ON_LINK10         BIT(2)
++#define AIR_PHY_LED_ON_LINKDOWN               BIT(3)
++#define AIR_PHY_LED_ON_FDX            BIT(4) /* Full duplex */
++#define AIR_PHY_LED_ON_HDX            BIT(5) /* Half duplex */
++#define AIR_PHY_LED_ON_FORCE_ON               BIT(6)
++#define AIR_PHY_LED_ON_LINK2500               BIT(8)
++#define AIR_PHY_LED_ON_POLARITY               BIT(14)
++#define AIR_PHY_LED_ON_ENABLE                 BIT(15)
++
++#define AIR_PHY_LED_BLINK(i)         (0x025 + ((i) * 2))
++#define AIR_PHY_LED_BLINK_1000TX      BIT(0)
++#define AIR_PHY_LED_BLINK_1000RX      BIT(1)
++#define AIR_PHY_LED_BLINK_100TX               BIT(2)
++#define AIR_PHY_LED_BLINK_100RX               BIT(3)
++#define AIR_PHY_LED_BLINK_10TX                BIT(4)
++#define AIR_PHY_LED_BLINK_10RX                BIT(5)
++#define AIR_PHY_LED_BLINK_COLLISION   BIT(6)
++#define AIR_PHY_LED_BLINK_RX_CRC_ERR  BIT(7)
++#define AIR_PHY_LED_BLINK_RX_IDLE_ERR BIT(8)
++#define AIR_PHY_LED_BLINK_FORCE_BLINK BIT(9)
++#define AIR_PHY_LED_BLINK_2500TX      BIT(10)
++#define AIR_PHY_LED_BLINK_2500RX      BIT(11)
++
++#define EN8811H_FW_VERSION            0x3b3c
++
++#define EN8811H_POLARITY              0xca0f8
++#define EN8811H_POLARITY_TX_NORMAL    BIT(0)
++#define EN8811H_POLARITY_RX_REVERSE   BIT(1)
++
++#define EN8811H_CLK_CGM               0xcf958
++#define EN8811H_CLK_CGM_CKO   BIT(26)
++#define EN8811H_HWTRAP1               0xcf914
++#define EN8811H_HWTRAP1_CKO   BIT(12)
++
++#define air_upper_16_bits(n) ((u16)((n) >> 16))
++#define air_lower_16_bits(n) ((u16)((n) & 0xffff))
++#define clear_bit(bit, bitmap)        __clear_bit(bit, bitmap)
++
++/* Led definitions */
++#define EN8811H_LED_COUNT     3
++
++struct led {
++      unsigned long rules;
++      unsigned long state;
++};
 +
-+/**************************
-+ * GPIO5  <-> BASE_T_LED0,
-+ * GPIO4  <-> BASE_T_LED1,
-+ * GPIO3  <-> BASE_T_LED2,
-+ **************************/
-+/* User-defined.B */
-+#define AIR_LED_SUPPORT
-+#ifdef AIR_LED_SUPPORT
-+static const struct air_base_t_led_cfg_s led_cfg[3] = {
-+/*********************************************************************
-+ *Enable,   GPIO,        LED Polarity,     LED ON,      LED Blink
-+**********************************************************************/
-+    {1, AIR_LED0_GPIO5, AIR_ACTIVE_HIGH, AIR_LED0_ON, AIR_LED0_BLK},
-+    {1, AIR_LED1_GPIO4, AIR_ACTIVE_HIGH, AIR_LED1_ON, AIR_LED1_BLK},
-+    {1, AIR_LED2_GPIO3, AIR_ACTIVE_HIGH, AIR_LED2_ON, AIR_LED2_BLK},
++enum {
++      AIR_PHY_LED_STATE_FORCE_ON,
++      AIR_PHY_LED_STATE_FORCE_BLINK,
 +};
-+static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
-+#endif
-+/* User-defined.E */
-+/*************************************************************
-+ *                       F U N C T I O N S
-+ **************************************************************/
-+/* Airoha MII read function */
-+static int air_mii_cl22_read(struct mii_dev *bus, int phy_addr, int phy_register)
-+{
-+    int read_data = bus->read(bus, phy_addr, MDIO_DEVAD_NONE, phy_register);
 +
-+    if (read_data < 0)
-+        return -EIO;
-+    return read_data;
-+}
++enum {
++      AIR_PHY_LED_DUR_BLINK_32MS,
++      AIR_PHY_LED_DUR_BLINK_64MS,
++      AIR_PHY_LED_DUR_BLINK_128MS,
++      AIR_PHY_LED_DUR_BLINK_256MS,
++      AIR_PHY_LED_DUR_BLINK_512MS,
++      AIR_PHY_LED_DUR_BLINK_1024MS,
++};
 +
-+/* Airoha MII write function */
-+static int air_mii_cl22_write(struct mii_dev *bus, int phy_addr, int phy_register, int write_data)
-+{
-+    int ret = 0;
++enum {
++      AIR_LED_DISABLE,
++      AIR_LED_ENABLE,
++};
 +
-+    ret = bus->write(bus, phy_addr, MDIO_DEVAD_NONE, phy_register, write_data);
-+    if (ret < 0) {
-+        printf("bus->write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return ret;
-+}
++enum {
++      AIR_ACTIVE_LOW,
++      AIR_ACTIVE_HIGH,
++};
 +
-+static int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
-+{
-+    int ret = 0;
-+    int data;
++enum {
++      AIR_LED_MODE_DISABLE,
++      AIR_LED_MODE_USER_DEFINE,
++};
 +
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, devad);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, reg);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return INVALID_DATA;
-+    }
-+    data = phy_read(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG);
-+    return data;
-+}
++/* Trigger specific enum */
++enum air_led_trigger_netdev_modes {
++      AIR_TRIGGER_NETDEV_LINK = 0,
++      AIR_TRIGGER_NETDEV_LINK_10,
++      AIR_TRIGGER_NETDEV_LINK_100,
++      AIR_TRIGGER_NETDEV_LINK_1000,
++      AIR_TRIGGER_NETDEV_LINK_2500,
++      AIR_TRIGGER_NETDEV_LINK_5000,
++      AIR_TRIGGER_NETDEV_LINK_10000,
++      AIR_TRIGGER_NETDEV_HALF_DUPLEX,
++      AIR_TRIGGER_NETDEV_FULL_DUPLEX,
++      AIR_TRIGGER_NETDEV_TX,
++      AIR_TRIGGER_NETDEV_RX,
++      AIR_TRIGGER_NETDEV_TX_ERR,
++      AIR_TRIGGER_NETDEV_RX_ERR,
++
++      /* Keep last */
++      __AIR_TRIGGER_NETDEV_MAX,
++};
 +
-+static int air_mii_cl45_write(struct phy_device *phydev, int devad, u16 reg, u16 write_data)
-+{
-+    int ret = 0;
++/* Default LED setup:
++ * GPIO5 <-> LED0  On: Link detected, blink Rx/Tx
++ * GPIO4 <-> LED1  On: Link detected at 2500 and 1000 Mbps
++ * GPIO3 <-> LED2  On: Link detected at 2500 and  100 Mbps
++ */
++#define AIR_DEFAULT_TRIGGER_LED0 (BIT(AIR_TRIGGER_NETDEV_LINK)      | \
++                                BIT(AIR_TRIGGER_NETDEV_RX)        | \
++                                BIT(AIR_TRIGGER_NETDEV_TX))
++#define AIR_DEFAULT_TRIGGER_LED1 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \
++                                BIT(AIR_TRIGGER_NETDEV_LINK_1000))
++#define AIR_DEFAULT_TRIGGER_LED2 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \
++                                BIT(AIR_TRIGGER_NETDEV_LINK_100))
++
++#define AIR_PHY_LED_DUR_UNIT  781
++#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS)
++
++struct en8811h_priv {
++      int firmware_version;
++      bool            mcu_needs_restart;
++      struct led      led[EN8811H_LED_COUNT];
++};
 +
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, devad);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, reg);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, write_data);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return 0;
-+}
-+/* Use default PBUS_PHY_ID */
-+/* EN8811H PBUS write function */
-+static int air_pbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned long pbus_data)
++static int air_phy_read_page(struct phy_device *phydev)
 +{
-+    int ret = 0;
-+    struct mii_dev *mbus = phydev->bus;
-+
-+    ret = air_mii_cl22_write(mbus, ((phydev->addr) + 8), 0x1F, (unsigned int)(pbus_address >> 6));
-+    if (ret < 0)
-+        return ret;
-+    ret = air_mii_cl22_write(mbus, ((phydev->addr) + 8), (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF));
-+    if (ret < 0)
-+        return ret;
-+    ret = air_mii_cl22_write(mbus, ((phydev->addr) + 8), 0x10, (unsigned int)(pbus_data >> 16));
-+    if (ret < 0)
-+        return ret;
-+    return 0;
++      return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS);
 +}
 +
-+/* EN8811H BUCK write function */
-+static int air_buckpbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned int pbus_data)
++static int air_phy_write_page(struct phy_device *phydev, int page)
 +{
-+    int ret = 0;
-+
-+    /* page 4 */
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)4);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (unsigned int)0);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x11, (unsigned int)((pbus_address >> 16) & 0xffff));
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x12, (unsigned int)(pbus_address & 0xffff));
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x13, (unsigned int)((pbus_data >> 16) & 0xffff));
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, (unsigned int)(pbus_data & 0xffff));
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return 0;
++      return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);
 +}
 +
-+/* EN8811H BUCK read function */
-+static unsigned int air_buckpbus_reg_read(struct phy_device *phydev, unsigned long pbus_address)
++int air_phy_select_page(struct phy_device *phydev, int page)
 +{
-+    unsigned int pbus_data = 0, pbus_data_low, pbus_data_high;
-+    int ret = 0;
++      int ret, oldpage;
 +
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)4);        /* page 4 */
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (unsigned int)0);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x15, (unsigned int)((pbus_address >> 16) & 0xffff));
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x16, (unsigned int)(pbus_address & 0xffff));
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return PBUS_INVALID_DATA;
-+    }
++      oldpage = air_phy_read_page(phydev);
++      if (oldpage < 0)
++              return oldpage;
 +
-+    pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, 0x17);
-+    pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, 0x18);
-+    pbus_data = (pbus_data_high << 16) + pbus_data_low;
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)0);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return pbus_data;
++      if (oldpage != page) {
++              ret = air_phy_write_page(phydev, page);
++              if (ret < 0)
++                      return ret;
++      }
++
++      return oldpage;
 +}
 +
-+static int MDIOWriteBuf(struct phy_device *phydev, unsigned long address, unsigned long array_size, const unsigned char *buffer)
++int air_phy_restore_page(struct phy_device *phydev, int oldpage, int ret)
 +{
-+    unsigned int write_data, offset ;
-+    int ret = 0;
++      int r;
 +
-+    /* page 4 */
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)4);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    /* address increment*/
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (unsigned int)0x8000);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x11, (unsigned int)((address >> 16) & 0xffff));
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x12, (unsigned int)(address & 0xffff));
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
++      if (oldpage < 0)
++              return oldpage;
 +
-+    for (offset = 0; offset < array_size; offset += 4) {
-+        write_data = (buffer[offset + 3] << 8) | buffer[offset + 2];
-+        ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x13, write_data);
-+        if (ret < 0) {
-+            printf("phy_write, ret: %d\n", ret);
-+            return ret;
-+        }
-+        write_data = (buffer[offset + 1] << 8) | buffer[offset];
-+        ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, write_data);
-+        if (ret < 0) {
-+            printf("phy_write, ret: %d\n", ret);
-+            return ret;
-+        }
-+    }
-+    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)0);
-+    if (ret < 0) {
-+        printf("phy_write, ret: %d\n", ret);
-+        return ret;
-+    }
-+    return 0;
++      r = air_phy_write_page(phydev, oldpage);
++      if (ret >= 0 && r < 0)
++              ret = r;
++
++      return ret;
 +}
 +
-+#ifdef  AIR_LED_SUPPORT
-+static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity, int polar,
-+                                   u16 on_evt, u16 blk_evt)
++static int air_buckpbus_reg_write(struct phy_device *phydev,
++                                u32 pbus_address, u32 pbus_data)
 +{
-+    int ret = 0;
-+
-+    if (AIR_ACTIVE_HIGH == polar)
-+        on_evt |= LED_ON_POL;
-+    else
-+        on_evt &= ~LED_ON_POL;
-+
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), on_evt | LED_ON_EN);
-+    if (ret < 0)
-+        return ret;
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt);
-+    if (ret < 0)
-+        return ret;
-+    return 0;
++      int ret, saved_page;
++
++      saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
++      if (saved_page < 0)
++              return saved_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
++                      AIR_BPBUS_MODE_ADDR_FIXED);
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
++                      air_upper_16_bits(pbus_address));
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
++                      air_lower_16_bits(pbus_address));
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
++                      air_upper_16_bits(pbus_data));
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
++                      air_lower_16_bits(pbus_data));
++      if (ret < 0)
++              goto restore_page;
++
++restore_page:
++      if (ret < 0)
++              printf("%s 0x%08x failed: %d\n", __func__,
++                     pbus_address, ret);
++
++      return air_phy_restore_page(phydev, saved_page, ret);
 +}
 +
-+static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
++static int air_buckpbus_reg_read(struct phy_device *phydev,
++                               u32 pbus_address, u32 *pbus_data)
 +{
-+    u16 cl45_data;
-+    int err = 0;
-+
-+    cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_BCR);
-+    switch (mode) {
-+    case AIR_LED_MODE_DISABLE:
-+        cl45_data &= ~LED_BCR_EXT_CTRL;
-+        cl45_data &= ~LED_BCR_MODE_MASK;
-+        cl45_data |= LED_BCR_MODE_DISABLE;
-+        break;
-+    case AIR_LED_MODE_USER_DEFINE:
-+        cl45_data |= LED_BCR_EXT_CTRL;
-+        cl45_data |= LED_BCR_CLK_EN;
-+        break;
-+    default:
-+        printf("LED mode%d is not supported!\n", mode);
-+        return -EINVAL;
-+    }
-+    err = air_mii_cl45_write(phydev, 0x1f, LED_BCR, cl45_data);
-+    if (err < 0)
-+        return err;
-+    return 0;
++      int pbus_data_low, pbus_data_high;
++      int ret = 0, saved_page;
++
++      saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
++      if (saved_page < 0)
++              return saved_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
++                      AIR_BPBUS_MODE_ADDR_FIXED);
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
++                      air_upper_16_bits(pbus_address));
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
++                      air_lower_16_bits(pbus_address));
++      if (ret < 0)
++              goto restore_page;
++
++      pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
++      if (pbus_data_high < 0) {
++              ret = pbus_data_high;
++              goto restore_page;
++      }
++
++      pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
++      if (pbus_data_low < 0) {
++              ret = pbus_data_low;
++              goto restore_page;
++      }
++
++      *pbus_data = pbus_data_low | (pbus_data_high << 16);
++
++restore_page:
++      if (ret < 0)
++              printf("%s 0x%08x failed: %d\n", __func__,
++                     pbus_address, ret);
++
++      return air_phy_restore_page(phydev, saved_page, ret);
 +}
 +
-+static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
++static int air_buckpbus_reg_modify(struct phy_device *phydev,
++                                 u32 pbus_address, u32 mask, u32 set)
 +{
-+    u16 cl45_data;
-+    int err;
-+
-+    cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity));
-+    if (LED_ENABLE == state)
-+        cl45_data |= LED_ON_EN;
-+    else
-+        cl45_data &= ~LED_ON_EN;
-+
-+    err = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data);
-+    if (err < 0)
-+        return err;
-+    return 0;
++      int pbus_data_low, pbus_data_high;
++      u32 pbus_data_old, pbus_data_new;
++      int ret = 0, saved_page;
++
++      saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
++      if (saved_page < 0)
++              return saved_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
++                      AIR_BPBUS_MODE_ADDR_FIXED);
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
++                      air_upper_16_bits(pbus_address));
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
++                      air_lower_16_bits(pbus_address));
++      if (ret < 0)
++              goto restore_page;
++
++      pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
++      if (pbus_data_high < 0)
++              return pbus_data_high;
++
++      pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
++      if (pbus_data_low < 0)
++              return pbus_data_low;
++
++      pbus_data_old = pbus_data_low | (pbus_data_high << 16);
++      pbus_data_new = (pbus_data_old & ~mask) | set;
++      if (pbus_data_new == pbus_data_old)
++              return 0;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
++                      air_upper_16_bits(pbus_address));
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
++                      air_lower_16_bits(pbus_address));
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
++                      air_upper_16_bits(pbus_data_new));
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
++                      air_lower_16_bits(pbus_data_new));
++      if (ret < 0)
++              goto restore_page;
++
++restore_page:
++      if (ret < 0)
++              printf("%s 0x%08x failed: %d\n", __func__,
++                     pbus_address, ret);
++
++      return air_phy_restore_page(phydev, saved_page, ret);
 +}
 +
-+static int en8811h_led_init(struct phy_device *phydev)
++static int air_write_buf(struct phy_device *phydev, unsigned long address,
++                       unsigned long array_size, const unsigned char *buffer)
 +{
-+    unsigned int led_gpio = 0, reg_value = 0;
-+    u16 cl45_data = led_dur;
-+    int ret, led_id;
-+
-+    cl45_data = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data);
-+    if (ret < 0)
-+        return ret;
-+    cl45_data >>= 1;
-+    ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data);
-+    if (ret < 0)
-+        return ret;
++      unsigned int offset;
++      int ret, saved_page;
++      u16 val;
++
++      saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
++      if (saved_page < 0)
++              return saved_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
++                      AIR_BPBUS_MODE_ADDR_INCR);
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
++                      air_upper_16_bits(address));
++      if (ret < 0)
++              goto restore_page;
++
++      ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
++                      air_lower_16_bits(address));
++      if (ret < 0)
++              goto restore_page;
++
++      for (offset = 0; offset < array_size; offset += 4) {
++              val = get_unaligned_le16(&buffer[offset + 2]);
++              ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, val);
++              if (ret < 0)
++                      goto restore_page;
++
++              val = get_unaligned_le16(&buffer[offset]);
++              ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, val);
++              if (ret < 0)
++                      goto restore_page;
++      }
++
++restore_page:
++      if (ret < 0)
++              printf("%s 0x%08lx failed: %d\n", __func__,
++                     address, ret);
++
++      return air_phy_restore_page(phydev, saved_page, ret);
++}
 +
-+    ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
-+    if (ret != 0) {
-+        printf("LED fail to set mode, ret %d !\n", ret);
-+        return ret;
-+    }
-+    for(led_id = 0; led_id < EN8811H_LED_COUNT; led_id++)
-+    {
-+        /* LED0 <-> GPIO5, LED1 <-> GPIO4, LED0 <-> GPIO3 */
-+        if ( led_cfg[led_id].gpio != (led_id + (AIR_LED0_GPIO5 - (2 * led_id)))) {
-+            printf("LED%d uses incorrect GPIO%d !\n", led_id, led_cfg[led_id].gpio);
-+            return -EINVAL;
-+        }
-+        reg_value = 0;
-+        if (led_cfg[led_id].en == LED_ENABLE)
-+        {
-+            led_gpio |= BIT(led_cfg[led_id].gpio);
-+            ret = airoha_led_set_state(phydev, led_id, led_cfg[led_id].en);
-+            if (ret != 0) {
-+                printf("LED fail to set state, ret %d !\n", ret);
-+                return ret;
-+            }
-+            ret = airoha_led_set_usr_def(phydev, led_id, led_cfg[led_id].pol, led_cfg[led_id].on_cfg, led_cfg[led_id].blk_cfg);
-+            if (ret != 0) {
-+                printf("LED fail to set default, ret %d !\n", ret);
-+                return ret;
-+            }
-+        }
-+    }
-+    ret = air_buckpbus_reg_write(phydev, 0xcf8b8, led_gpio);
-+    if (ret < 0)
-+        return ret;
-+    printf("LED initialize OK !\n");
-+    return 0;
++static int en8811h_wait_mcu_ready(struct phy_device *phydev)
++{
++      int ret, reg_value;
++
++      /* Because of mdio-lock, may have to wait for multiple loads */
++      ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
++                                      EN8811H_PHY_FW_STATUS, reg_value,
++                                      reg_value == EN8811H_PHY_READY,
++                                      20000, 7500000, true);
++      if (ret) {
++              printf("MCU not ready: 0x%x\n", reg_value);
++              return -ENODEV;
++      }
++
++      return 0;
 +}
-+#endif /* AIR_LED_SUPPORT */
 +
-+static char *firmware_buf;
-+static int en8811h_load_firmware(struct phy_device *phydev)
++__weak int en8811h_read_fw(void **addr)
 +{
-+    u32 pbus_value;
-+    int ret = 0;
++      u32 ca_crc32;
++      void *buffer;
++      int ret;
 +
-+    if (!firmware_buf) {
-+        firmware_buf = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
-+        if (!firmware_buf) {
-+            printf("[Airoha] cannot allocated buffer for firmware.\n");
-+            return -ENOMEM;
-+        }
++      /* Allocate memory to store both DM and DSP firmware */
++      buffer = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
++      if (!buffer) {
++              printf("Failed to allocate memory for firmware\n");
++              return -ENOMEM;
++      }
 +
 +#ifdef CONFIG_PHY_AIROHA_FW_IN_UBI
-+        ret = ubi_volume_read("en8811h-fw", firmware_buf, 0, EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
-+        if (ret) {
-+            printf("[Airoha] read firmware from UBI failed.\n");
-+            free(firmware_buf);
-+            firmware_buf = NULL;
-+            return ret;
-+        }
++      ret = ubi_volume_read("en8811h-fw", buffer, 0, EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
++      if (ret) {
++              printf("[Airoha] read firmware from UBI failed.\n");
++              free(buffer);
++              buffer = NULL;
++              return ret;
++      }
 +#elif defined(CONFIG_PHY_AIROHA_FW_IN_MMC)
-+        struct mmc *mmc = find_mmc_device(0);
-+        if (!mmc) {
-+            printf("[Airoha] opening MMC device failed.\n");
-+            free(firmware_buf);
-+            firmware_buf = NULL;
-+            return -ENODEV;
-+        }
-+        if (mmc_init(mmc)) {
-+            printf("[Airoha] initializing MMC device failed.\n");
-+            free(firmware_buf);
-+            firmware_buf = NULL;
-+            return -ENODEV;
-+        }
-+        if (IS_SD(mmc)) {
-+            printf("[Airoha] SD card is not supported.\n");
-+            free(firmware_buf);
-+            firmware_buf = NULL;
-+            return -EINVAL;
-+        }
-+        ret = mmc_set_part_conf(mmc, 1, 2, 2);
-+        if (ret) {
-+            printf("[Airoha] cannot access eMMC boot1 hw partition.\n");
-+            free(firmware_buf);
-+            firmware_buf = NULL;
-+            return ret;
-+        }
-+        ret = blk_dread(mmc_get_blk_desc(mmc), 0, 0x120, firmware_buf);
-+        mmc_set_part_conf(mmc, 1, 1, 0);
-+        if (ret != 0x120) {
-+            printf("[Airoha] cannot read firmware from eMMC.\n");
-+            free(firmware_buf);
-+            firmware_buf = NULL;
-+            return -EIO;
-+        }
++      struct mmc *mmc = find_mmc_device(0);
++      if (!mmc) {
++              printf("[Airoha] opening MMC device failed.\n");
++              free(buffer);
++              buffer = NULL;
++              return -ENODEV;
++      }
++      if (mmc_init(mmc)) {
++              printf("[Airoha] initializing MMC device failed.\n");
++              free(buffer);
++              buffer = NULL;
++              return -ENODEV;
++      }
++      if (IS_SD(mmc)) {
++      printf("[Airoha] SD card is not supported.\n");
++              free(buffer);
++              buffer = NULL;
++              return -EINVAL;
++      }
++      ret = mmc_set_part_conf(mmc, 1, 2, 2);
++      if (ret) {
++              printf("[Airoha] cannot access eMMC boot1 hw partition.\n");
++              free(buffer);
++              buffer = NULL;
++              return ret;
++      }
++      ret = blk_dread(mmc_get_blk_desc(mmc), 0, 0x120, buffer);
++      mmc_set_part_conf(mmc, 1, 1, 0);
++      if (ret != 0x120) {
++              printf("[Airoha] cannot read firmware from eMMC.\n");
++              free(buffer);
++              buffer = NULL;
++              return -EIO;
++      }
 +#else
 +#warning EN8811H firmware loading not implemented
-+        free(firmware_buf);
-+        firmware_buf = NULL;
-+        return -EOPNOTSUPP;
++      free(buffer);
++      buffer = NULL;
++      return -EOPNOTSUPP;
 +#endif
-+    }
 +
-+    ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x0);
-+    if (ret < 0)
-+        return ret;
-+    pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
-+    pbus_value |= BIT(11);
-+    ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
-+    if (ret < 0)
-+        return ret;
-+    /* Download DM */
-+    ret = MDIOWriteBuf(phydev, 0x00000000, EN8811H_MD32_DM_SIZE, firmware_buf);
-+    if (ret < 0) {
-+        printf("[Airoha] MDIOWriteBuf 0x00000000 fail.\n");
-+        return ret;
-+    }
-+    /* Download PM */
-+    ret = MDIOWriteBuf(phydev, 0x00100000, EN8811H_MD32_DSP_SIZE, firmware_buf + EN8811H_MD32_DM_SIZE);
-+    if (ret < 0) {
-+        printf("[Airoha] MDIOWriteBuf 0x00100000 fail.\n");
-+        return ret;
-+    }
-+    pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
-+    pbus_value &= ~BIT(11);
-+    ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
-+    if (ret < 0)
-+        return ret;
-+    ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
-+    if (ret < 0)
-+        return ret;
-+    return 0;
-+}
-+
-+static int en8811h_config(struct phy_device *phydev)
-+{
-+    int ret = 0;
-+    int pid1 = 0, pid2 = 0;
++      /* Calculate CRC32 of DM firmware for verification */
++      ca_crc32 = crc32(0, (unsigned char *)buffer, EN8811H_MD32_DM_SIZE);
++      debug("DM crc32: 0x%x\n", ca_crc32);
 +
-+    ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0);
-+    if (ret < 0)
-+        return ret;
++      /* Calculate CRC32 of DSP firmware for verification */
++      ca_crc32 = crc32(0, (unsigned char *)buffer + EN8811H_MD32_DM_SIZE,
++              EN8811H_MD32_DSP_SIZE);
++      debug("DSP crc32: 0x%x\n", ca_crc32);
 +
-+    pid1 = phy_read(phydev, MDIO_DEVAD_NONE, MII_PHYSID1);
-+    pid2 = phy_read(phydev, MDIO_DEVAD_NONE, MII_PHYSID2);
-+    if ((EN8811H_PHY_ID1 != pid1) || (EN8811H_PHY_ID2 != pid2)) {
-+        printf("EN8811H does not exist !\n");
-+        return -ENODEV;
-+    }
++      *addr = buffer;
++      debug("Found Airoha Firmware.\n");
 +
-+    return 0;
++      return 0;
 +}
 +
-+static int en8811h_get_autonego(struct phy_device *phydev, int *an)
++static int en8811h_load_firmware(struct phy_device *phydev)
 +{
-+    int reg;
-+    reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
-+    if (reg < 0)
-+        return -EINVAL;
-+    if (reg & BMCR_ANENABLE)
-+        *an = AUTONEG_ENABLE;
-+    else
-+        *an = AUTONEG_DISABLE;
-+    return 0;
++      struct en8811h_priv *priv = phydev->priv;
++      void *buffer;
++      int ret;
++
++      ret = en8811h_read_fw(&buffer);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
++                                   EN8811H_FW_CTRL_1_START);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
++                                    EN8811H_FW_CTRL_2_LOADING,
++                                    EN8811H_FW_CTRL_2_LOADING);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_write_buf(phydev, AIR_FW_ADDR_DM, EN8811H_MD32_DM_SIZE,
++                          (unsigned char *)buffer);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, EN8811H_MD32_DSP_SIZE,
++                          (unsigned char *)buffer + EN8811H_MD32_DM_SIZE);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
++                                    EN8811H_FW_CTRL_2_LOADING, 0);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
++                                   EN8811H_FW_CTRL_1_FINISH);
++      if (ret < 0)
++              goto en8811h_load_firmware_out;
++
++      ret = en8811h_wait_mcu_ready(phydev);
++
++      air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
++                            &priv->firmware_version);
++      printf("MD32 firmware version: %08x\n",
++             priv->firmware_version);
++
++en8811h_load_firmware_out:
++      free(buffer);
++      if (ret < 0)
++              printf("Firmware loading failed: %d\n", ret);
++
++      return ret;
 +}
 +
-+static int en8811h_startup(struct phy_device *phydev)
++static int en8811h_restart_mcu(struct phy_device *phydev)
 +{
-+    ofnode node = phy_get_ofnode(phydev);
-+    int ret = 0, lpagb = 0, lpa = 0, common_adv_gb = 0, common_adv = 0, advgb = 0, adv = 0, reg = 0, an = AUTONEG_DISABLE, bmcr = 0, reg_value;
-+    int old_link = phydev->link;
-+    u32 pbus_value = 0, retry;
-+
-+    eth_phy_reset(phydev->dev, 1);
-+    mdelay(10);
-+    eth_phy_reset(phydev->dev, 0);
-+    mdelay(1);
-+
-+    ret = en8811h_load_firmware(phydev);
-+    if (ret) {
-+        printf("EN8811H load firmware fail.\n");
-+        return ret;
-+    }
-+    retry = MAX_RETRY;
-+    do {
-+        mdelay(300);
-+        reg_value = air_mii_cl45_read(phydev, 0x1e, 0x8009);
-+        if (EN8811H_PHY_READY == reg_value) {
-+            printf("EN8811H PHY ready!\n");
-+            break;
-+        }
-+        retry--;
-+    } while (retry);
-+    if (0 == retry) {
-+        printf("EN8811H PHY is not ready. (MD32 FW Status reg: 0x%x)\n", reg_value);
-+        pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
-+        printf("Check MD32 FW Version(0x3b3c) : %08x\n", pbus_value);
-+        printf("EN8811H initialize fail!\n");
-+        return 0;
-+    }
-+    /* Mode selection*/
-+    printf("EN8811H Mode 1 !\n");
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
-+    if (ret < 0)
-+        return ret;
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
-+    if (ret < 0)
-+        return ret;
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
-+    if (ret < 0)
-+        return ret;
-+    ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
-+    if (ret < 0)
-+        return ret;
-+
-+    /* Serdes polarity */
-+    pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
-+    pbus_value &= 0xfffffffc;
-+    pbus_value |= ofnode_read_bool(node, "airoha,rx-pol-reverse") ?
-+            EN8811H_RX_POLARITY_REVERSE : EN8811H_RX_POLARITY_NORMAL;
-+    pbus_value |= ofnode_read_bool(node, "airoha,tx-pol-reverse") ?
-+            EN8811H_TX_POLARITY_REVERSE : EN8811H_TX_POLARITY_NORMAL;
-+    ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value);
-+    if (ret < 0)
-+        return ret;
-+    pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
-+    printf("Tx, Rx Polarity(0xca0f8): %08x\n", pbus_value);
-+    pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
-+    printf("MD32 FW Version(0x3b3c) : %08x\n", pbus_value);
-+#if defined(AIR_LED_SUPPORT)
-+    ret = en8811h_led_init(phydev);
-+    if (ret < 0) {
-+        printf("en8811h_led_init fail\n");
-+    }
-+#endif
-+    printf("EN8811H initialize OK ! (%s)\n", EN8811H_DRIVER_VERSION);
-+
-+    ret = genphy_update_link(phydev);
-+    if (ret)
-+    {
-+        printf("ret %d!\n", ret);
-+        return ret;
-+    }
-+
-+    ret = genphy_parse_link(phydev);
-+    if (ret)
-+    {
-+        printf("ret %d!\n", ret);
-+        return ret;
-+    }
++      int ret;
 +
-+    if (old_link && phydev->link)
-+       return 0;
++      ret = phy_write_mmd(phydev, 0x1e, 0x8009, 0x0);
++      if (ret < 0)
++              return ret;
 +
-+    phydev->speed = SPEED_100;
-+    phydev->duplex = DUPLEX_FULL;
-+    phydev->pause = 0;
-+    phydev->asym_pause = 0;
++      ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
++                                   EN8811H_FW_CTRL_1_START);
++      if (ret < 0)
++              return ret;
 +
-+    reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
-+    if (reg < 0)
-+    {
-+        printf("MII_BMSR reg %d!\n", reg);
-+        return reg;
-+    }
-+    reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
-+    if (reg < 0)
-+    {
-+        printf("MII_BMSR reg %d!\n", reg);
-+        return reg;
-+    }
-+    if(reg & BMSR_LSTATUS)
-+    {
-+        pbus_value = air_buckpbus_reg_read(phydev, 0x109D4);
-+        if (0x10 & pbus_value) {
-+            phydev->speed = SPEED_2500;
-+            phydev->duplex = DUPLEX_FULL;
-+        }
-+        else
-+        {
-+            ret = en8811h_get_autonego(phydev, &an);
-+            if ((AUTONEG_ENABLE == an) && (0 == ret))
-+            {
-+                printf("AN mode!\n");
-+                printf("SPEED 1000/100!\n");
-+                lpagb = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);
-+                if (lpagb < 0 )
-+                    return lpagb;
-+                advgb = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
-+                if (adv < 0 )
-+                    return adv;
-+                common_adv_gb = (lpagb & (advgb << 2));
-+
-+                lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
-+                if (lpa < 0 )
-+                    return lpa;
-+                adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
-+                if (adv < 0 )
-+                    return adv;
-+                common_adv = (lpa & adv);
-+
-+                phydev->speed = SPEED_10;
-+                phydev->duplex = DUPLEX_HALF;
-+                if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF))
-+                {
-+                    phydev->speed = SPEED_1000;
-+                    if (common_adv_gb & LPA_1000FULL)
-+
-+                        phydev->duplex = DUPLEX_FULL;
-+                }
-+                else if (common_adv & (LPA_100FULL | LPA_100HALF))
-+                {
-+                    phydev->speed = SPEED_100;
-+                    if (common_adv & LPA_100FULL)
-+                        phydev->duplex = DUPLEX_FULL;
-+                }
-+                else
-+                {
-+                    if (common_adv & LPA_10FULL)
-+                        phydev->duplex = DUPLEX_FULL;
-+                }
-+            }
-+            else
-+            {
-+                printf("Force mode!\n");
-+                bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
-+
-+                if (bmcr < 0)
-+                    return bmcr;
-+
-+                if (bmcr & BMCR_FULLDPLX)
-+                    phydev->duplex = DUPLEX_FULL;
-+                else
-+                    phydev->duplex = DUPLEX_HALF;
-+
-+                if (bmcr & BMCR_SPEED1000)
-+                    phydev->speed = SPEED_1000;
-+                else if (bmcr & BMCR_SPEED100)
-+                    phydev->speed = SPEED_100;
-+                else
-+                    phydev->speed = SPEED_100;
-+            }
-+        }
-+    }
-+
-+    return ret;
++      return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
++                                    EN8811H_FW_CTRL_1_FINISH);
 +}
 +
-+#if AIR_UBOOT_REVISION > 0x202303
-+U_BOOT_PHY_DRIVER(en8811h) = {
-+    .name = "Airoha EN8811H",
-+    .uid = EN8811H_PHY_ID,
-+    .mask = 0x0ffffff0,
-+    .config = &en8811h_config,
-+    .startup = &en8811h_startup,
-+    .shutdown = &genphy_shutdown,
-+};
-+#else
-+static struct phy_driver AIR_EN8811H_driver = {
-+    .name = "Airoha EN8811H",
-+    .uid = EN8811H_PHY_ID,
-+    .mask = 0x0ffffff0,
-+    .config = &en8811h_config,
-+    .startup = &en8811h_startup,
-+    .shutdown = &genphy_shutdown,
++static int air_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                unsigned long rules)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      u16 on = 0, blink = 0;
++      int ret;
++
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
++
++      priv->led[index].rules = rules;
++
++      if (rules & BIT(AIR_TRIGGER_NETDEV_FULL_DUPLEX))
++              on |= AIR_PHY_LED_ON_FDX;
++
++      if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_10) | BIT(AIR_TRIGGER_NETDEV_LINK)))
++              on |= AIR_PHY_LED_ON_LINK10;
++
++      if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_100) | BIT(AIR_TRIGGER_NETDEV_LINK)))
++              on |= AIR_PHY_LED_ON_LINK100;
++
++      if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_1000) | BIT(AIR_TRIGGER_NETDEV_LINK)))
++              on |= AIR_PHY_LED_ON_LINK1000;
++
++      if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | BIT(AIR_TRIGGER_NETDEV_LINK)))
++              on |= AIR_PHY_LED_ON_LINK2500;
++
++      if (rules & BIT(AIR_TRIGGER_NETDEV_RX)) {
++              blink |= AIR_PHY_LED_BLINK_10RX   |
++                       AIR_PHY_LED_BLINK_100RX  |
++                       AIR_PHY_LED_BLINK_1000RX |
++                       AIR_PHY_LED_BLINK_2500RX;
++      }
++
++      if (rules & BIT(AIR_TRIGGER_NETDEV_TX)) {
++              blink |= AIR_PHY_LED_BLINK_10TX   |
++                       AIR_PHY_LED_BLINK_100TX  |
++                       AIR_PHY_LED_BLINK_1000TX |
++                       AIR_PHY_LED_BLINK_2500TX;
++      }
++
++      if (blink || on) {
++              /* switch hw-control on, so led-on and led-blink are off */
++              clear_bit(AIR_PHY_LED_STATE_FORCE_ON,
++                        &priv->led[index].state);
++              clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
++                        &priv->led[index].state);
++      } else {
++              priv->led[index].rules = 0;
++      }
++
++      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),
++                           AIR_PHY_LED_ON_MASK, on);
++
++      if (ret < 0)
++              return ret;
++
++      return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index),
++                           blink);
 +};
 +
-+int phy_air_en8811h_init(void)
++static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol)
 +{
-+    phy_register(&AIR_EN8811H_driver);
-+    return 0;
-+}
-+#endif
---- /dev/null
-+++ b/drivers/net/phy/air_en8811h.h
-@@ -0,0 +1,163 @@
-+/* SPDX-License-Identifier: GPL-2.0 */
-+/*************************************************
-+ * FILE NAME:  air_en8811h.h
-+ * PURPOSE:
-+ *      EN8811H PHY Driver for Uboot
-+ * NOTES:
-+ *
-+ *  Copyright (C) 2023 Airoha Technology Corp.
-+ *************************************************/
-+
-+#ifndef __EN8811H_H
-+#define __EN8811H_H
-+
-+#define AIR_UBOOT_REVISION ((((U_BOOT_VERSION_NUM / 1000) % 10) << 20) | \
-+                      (((U_BOOT_VERSION_NUM / 100) % 10) << 16) | \
-+                      (((U_BOOT_VERSION_NUM / 10) % 10) << 12) | \
-+                      ((U_BOOT_VERSION_NUM % 10) << 8) | \
-+                      (((U_BOOT_VERSION_NUM_PATCH / 10) % 10) << 4) | \
-+                      ((U_BOOT_VERSION_NUM_PATCH % 10) << 0))
-+
-+#define EN8811H_PHY_ID1             0x03a2
-+#define EN8811H_PHY_ID2             0xa411
-+#define EN8811H_PHY_ID              ((EN8811H_PHY_ID1 << 16) | EN8811H_PHY_ID2)
-+#define EN8811H_SPEED_2500          0x03
-+#define EN8811H_PHY_READY           0x02
-+#define MAX_RETRY                   5
-+
-+#define EN8811H_MD32_DM_SIZE   0x4000
-+#define EN8811H_MD32_DSP_SIZE  0x20000
++      int val = 0;
++      int err;
 +
-+#define EN8811H_TX_POLARITY_NORMAL   0x1
-+#define EN8811H_TX_POLARITY_REVERSE  0x0
++      if (index >= EN8811H_LED_COUNT)
++              return -EINVAL;
 +
-+#define EN8811H_RX_POLARITY_NORMAL  (0x0 << 1)
-+#define EN8811H_RX_POLARITY_REVERSE (0x1 << 1)
++      if (state == AIR_LED_ENABLE)
++              val |= AIR_PHY_LED_ON_ENABLE;
++      else
++              val &= ~AIR_PHY_LED_ON_ENABLE;
 +
-+#ifndef BIT
-+#define BIT(nr)                       (1UL << (nr))
-+#endif
++      if (pol == AIR_ACTIVE_HIGH)
++              val |= AIR_PHY_LED_ON_POLARITY;
++      else
++              val &= ~AIR_PHY_LED_ON_POLARITY;
 +
-+/* CL45 MDIO control */
-+#define MII_MMD_ACC_CTL_REG         0x0d
-+#define MII_MMD_ADDR_DATA_REG       0x0e
-+#define MMD_OP_MODE_DATA            BIT(14)
-+/* MultiGBASE-T AN register */
-+#define MULTIG_ANAR_2500M           (0x0080)
-+#define MULTIG_LPAR_2500M           (0x0020)
-+
-+#define EN8811H_DRIVER_VERSION  "v1.0.4"
-+
-+/************************************************************
-+ * For reference only
-+ * LED0 Link 2500/Blink 2500 TxRx (GPIO5)    <-> BASE_T_LED0,
-+ * LED1 Link 1000/Blink 1000 TxRx (GPIO4)    <-> BASE_T_LED1,
-+ * LED2 Link 100/Blink 100 TxRx   (GPIO3)    <-> BASE_T_LED2,
-+ ************************************************************/
-+/* User-defined.B */
-+#define AIR_LED0_ON      (LED_ON_EVT_LINK_2500M)
-+#define AIR_LED0_BLK     (LED_BLK_EVT_2500M_TX_ACT | LED_BLK_EVT_2500M_RX_ACT)
-+#define AIR_LED1_ON      (LED_ON_EVT_LINK_1000M)
-+#define AIR_LED1_BLK    (LED_BLK_EVT_1000M_TX_ACT | LED_BLK_EVT_1000M_RX_ACT)
-+#define AIR_LED2_ON      (LED_ON_EVT_LINK_100M)
-+#define AIR_LED2_BLK     (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT)
-+/* User-defined.E */
++      err = phy_write_mmd(phydev, 0x1f, AIR_PHY_LED_ON(index), val);
++      if (err < 0)
++              return err;
 +
-+#define LED_ON_CTRL(i)              (0x024 + ((i)*2))
-+#define LED_ON_EN                   (1 << 15)
-+#define LED_ON_POL                  (1 << 14)
-+#define LED_ON_EVT_MASK             (0x1ff)
-+/* LED ON Event Option.B */
-+#define LED_ON_EVT_LINK_2500M       (1 << 8)
-+#define LED_ON_EVT_FORCE            (1 << 6)
-+#define LED_ON_EVT_HDX              (1 << 5)
-+#define LED_ON_EVT_FDX              (1 << 4)
-+#define LED_ON_EVT_LINK_DOWN        (1 << 3)
-+#define LED_ON_EVT_LINK_100M        (1 << 1)
-+#define LED_ON_EVT_LINK_1000M       (1 << 0)
-+/* LED ON Event Option.E */
-+
-+#define LED_BLK_CTRL(i)             (0x025 + ((i)*2))
-+#define LED_BLK_EVT_MASK            (0xfff)
-+/* LED Blinking Event Option.B*/
-+#define LED_BLK_EVT_2500M_RX_ACT    (1 << 11)
-+#define LED_BLK_EVT_2500M_TX_ACT    (1 << 10)
-+#define LED_BLK_EVT_FORCE           (1 << 9)
-+#define LED_BLK_EVT_100M_RX_ACT     (1 << 3)
-+#define LED_BLK_EVT_100M_TX_ACT     (1 << 2)
-+#define LED_BLK_EVT_1000M_RX_ACT    (1 << 1)
-+#define LED_BLK_EVT_1000M_TX_ACT    (1 << 0)
-+/* LED Blinking Event Option.E*/
-+#define LED_ENABLE                  1
-+#define LED_DISABLE                 0
++      return 0;
++}
 +
-+#define EN8811H_LED_COUNT       3
++static int air_leds_init(struct phy_device *phydev, int num, u16 dur, int mode)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      int ret, i;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK,
++                          dur);
++      if (ret < 0)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON,
++                          dur >> 1);
++      if (ret < 0)
++              return ret;
++
++      switch (mode) {
++      case AIR_LED_MODE_DISABLE:
++              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
++                                   AIR_PHY_LED_BCR_EXT_CTRL |
++                                   AIR_PHY_LED_BCR_MODE_MASK, 0);
++              break;
++      case AIR_LED_MODE_USER_DEFINE:
++              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
++                                   AIR_PHY_LED_BCR_EXT_CTRL |
++                                   AIR_PHY_LED_BCR_CLK_EN,
++                                   AIR_PHY_LED_BCR_EXT_CTRL |
++                                   AIR_PHY_LED_BCR_CLK_EN);
++              if (ret < 0)
++                      return ret;
++              break;
++      default:
++              printf("LED mode %d is not supported\n", mode);
++              return -EINVAL;
++      }
++
++      for (i = 0; i < num; ++i) {
++              ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH);
++              if (ret < 0) {
++                      printf("LED%d init failed: %d\n", i, ret);
++                      return ret;
++              }
++              air_led_hw_control_set(phydev, i, priv->led[i].rules);
++      }
++
++      return 0;
++}
 +
-+#define LED_BCR                     (0x021)
-+#define LED_BCR_EXT_CTRL            (1 << 15)
-+#define LED_BCR_CLK_EN              (1 << 3)
-+#define LED_BCR_TIME_TEST           (1 << 2)
-+#define LED_BCR_MODE_MASK           (3)
-+#define LED_BCR_MODE_DISABLE        (0)
-+#define LED_BCR_MODE_2LED           (1)
-+#define LED_BCR_MODE_3LED_1         (2)
-+#define LED_BCR_MODE_3LED_2         (3)
++static int en8811h_config(struct phy_device *phydev)
++{
++      struct en8811h_priv *priv = phydev->priv;
++      ofnode node = phy_get_ofnode(phydev);
++      u32 pbus_value = 0;
++      int ret = 0;
++
++      /* If restart happened in .probe(), no need to restart now */
++      if (priv->mcu_needs_restart) {
++              ret = en8811h_restart_mcu(phydev);
++              if (ret < 0)
++                      return ret;
++      } else {
++              ret = en8811h_load_firmware(phydev);
++              if (ret) {
++                      printf("Load firmware fail.\n");
++                      return ret;
++              }
++              /* Next calls to .config() mcu needs to restart */
++              priv->mcu_needs_restart = true;
++      }
++
++      ret = phy_write_mmd(phydev, 0x1e, 0x800c, 0x0);
++      if (ret < 0)
++              return ret;
++      ret = phy_write_mmd(phydev, 0x1e, 0x800d, 0x0);
++      if (ret < 0)
++              return ret;
++      ret = phy_write_mmd(phydev, 0x1e, 0x800e, 0x1101);
++      if (ret < 0)
++              return ret;
++      ret = phy_write_mmd(phydev, 0x1e, 0x800f, 0x0002);
++      if (ret < 0)
++              return ret;
++
++      /* Serdes polarity */
++      pbus_value = 0;
++      if (ofnode_read_bool(node, "airoha,pnswap-rx"))
++              pbus_value |=  EN8811H_POLARITY_RX_REVERSE;
++      else
++              pbus_value &= ~EN8811H_POLARITY_RX_REVERSE;
++      if (ofnode_read_bool(node, "airoha,pnswap-tx"))
++              pbus_value &= ~EN8811H_POLARITY_TX_NORMAL;
++      else
++              pbus_value |=  EN8811H_POLARITY_TX_NORMAL;
++      ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
++                                    EN8811H_POLARITY_RX_REVERSE |
++                                    EN8811H_POLARITY_TX_NORMAL, pbus_value);
++      if (ret < 0)
++              return ret;
++
++      ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,
++                          AIR_LED_MODE_USER_DEFINE);
++      if (ret < 0) {
++              printf("Failed to disable leds: %d\n", ret);
++              return ret;
++      }
++
++      return 0;
++}
 +
-+#define LED_ON_DUR                  (0x022)
-+#define LED_ON_DUR_MASK             (0xffff)
++static int en8811h_parse_status(struct phy_device *phydev)
++{
++      int ret = 0, reg_value;
++
++      phydev->duplex = DUPLEX_FULL;
++
++      reg_value = phy_read(phydev, MDIO_DEVAD_NONE, AIR_AUX_CTRL_STATUS);
++      if (reg_value < 0)
++              return reg_value;
++
++      switch (reg_value & AIR_AUX_CTRL_STATUS_SPEED_MASK) {
++      case AIR_AUX_CTRL_STATUS_SPEED_2500:
++              phydev->speed = SPEED_2500;
++              break;
++      case AIR_AUX_CTRL_STATUS_SPEED_1000:
++              phydev->speed = SPEED_1000;
++              break;
++      case AIR_AUX_CTRL_STATUS_SPEED_100:
++              phydev->speed = SPEED_100;
++              break;
++      default:
++              printf("Auto-neg error, defaulting to 100M/FD\n");
++              phydev->speed = SPEED_100;
++              break;
++      }
++
++      return ret;
++}
 +
-+#define LED_BLK_DUR                 (0x023)
-+#define LED_BLK_DUR_MASK            (0xffff)
++static int en8811h_startup(struct phy_device *phydev)
++{
++      int ret = 0;
 +
-+#define LED_GPIO_SEL_MASK           0x7FFFFFF
++      ret = genphy_update_link(phydev);
++      if (ret)
++              return ret;
 +
-+#define UNIT_LED_BLINK_DURATION     1024
++      return en8811h_parse_status(phydev);
++}
 +
-+#define INVALID_DATA                0xffff
-+#define PBUS_INVALID_DATA           0xffffffff
++static int en8811h_probe(struct phy_device *phydev)
++{
++      struct en8811h_priv *priv;
 +
-+struct air_base_t_led_cfg_s {
-+    u16 en;
-+    u16 gpio;
-+    u16 pol;
-+    u16 on_cfg;
-+    u16 blk_cfg;
-+};
++      priv = malloc(sizeof(*priv));
++      if (!priv)
++              return -ENOMEM;
++      memset(priv, 0, sizeof(*priv));
 +
-+enum {
-+    AIR_LED2_GPIO3 = 3,
-+    AIR_LED1_GPIO4,
-+    AIR_LED0_GPIO5,
-+    AIR_LED_LAST
-+};
++      priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0;
++      priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1;
++      priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2;
 +
-+enum {
-+    AIR_BASE_T_LED0,
-+    AIR_BASE_T_LED1,
-+    AIR_BASE_T_LED2,
-+    AIR_BASE_T_LED3
-+};
++      /* mcu has just restarted after firmware load */
++      priv->mcu_needs_restart = false;
 +
-+enum {
-+    AIR_LED_BLK_DUR_32M,
-+    AIR_LED_BLK_DUR_64M,
-+    AIR_LED_BLK_DUR_128M,
-+    AIR_LED_BLK_DUR_256M,
-+    AIR_LED_BLK_DUR_512M,
-+    AIR_LED_BLK_DUR_1024M,
-+    AIR_LED_BLK_DUR_LAST
-+};
++      phydev->priv = priv;
 +
-+enum {
-+    AIR_ACTIVE_LOW,
-+    AIR_ACTIVE_HIGH,
-+};
++      return 0;
++}
 +
-+enum {
-+    AIR_LED_MODE_DISABLE,
-+    AIR_LED_MODE_USER_DEFINE,
-+    AIR_LED_MODE_LAST
++U_BOOT_PHY_DRIVER(en8811h) = {
++      .name = "Airoha EN8811H",
++      .uid = EN8811H_PHY_ID,
++      .mask = 0x0ffffff0,
++      .config = &en8811h_config,
++      .probe = &en8811h_probe,
++      .startup = &en8811h_startup,
++      .shutdown = &genphy_shutdown,
 +};
-+
-+#endif /* End of __EN8811H_MD32_H */
-+
---- a/drivers/net/eth-phy-uclass.c
-+++ b/drivers/net/eth-phy-uclass.c
-@@ -154,7 +154,7 @@ static int eth_phy_of_to_plat(struct ude
-       return 0;
- }
--static void eth_phy_reset(struct udevice *dev, int value)
-+void eth_phy_reset(struct udevice *dev, int value)
- {
-       struct eth_phy_device_priv *uc_priv = dev_get_uclass_priv(dev);
-       u32 delay;
---- a/include/eth_phy.h
-+++ b/include/eth_phy.h
-@@ -14,5 +14,6 @@ int eth_phy_binds_nodes(struct udevice *
- int eth_phy_set_mdio_bus(struct udevice *eth_dev, struct mii_dev *mdio_bus);
- struct mii_dev *eth_phy_get_mdio_bus(struct udevice *eth_dev);
- int eth_phy_get_addr(struct udevice *dev);
-+void eth_phy_reset(struct udevice *dev, int value);
- #endif
index ad556b7d82dc207bb332e93cce26c4a0b44e2c41..fd01261624e0ab9107b032f51712033d4f527b47 100644 (file)
 +              compatible = "ethernet-phy-id03a2.a411";
 +              reg = <14>;
 +
-+              airoha,rx-pol-reverse;
++              airoha,pnswap-rx;
 +
 +              reset-gpios = <&gpio 49 GPIO_ACTIVE_LOW>;
 +              reset-assert-us = <10000>;