uboot-mediatek: add builds for BananaPi BPi-R3 mini
authorDaniel Golle <daniel@makrotopia.org>
Fri, 26 Jan 2024 17:26:39 +0000 (17:26 +0000)
committerDaniel Golle <daniel@makrotopia.org>
Thu, 15 Feb 2024 19:30:08 +0000 (19:30 +0000)
The R3 mini comes with two Airoha EN8811H PHYs for 2.5G Ethernet.
The driver added to U-Boot expects the firmware for the PHY to be
stored inside UBI volume en8811h-fw or MMC boot1 hardware partition.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
package/boot/uboot-mediatek/Makefile
package/boot/uboot-mediatek/patches/160-net-phy-add-support-for-Airoha-ethernet-PHY-driver.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/442-add-bpi-r3-mini.patch [new file with mode: 0644]

index df8c9431eccc0b99b023166040dfad7274a18904..61aaf4de2575e9b7298e3456582d43654093b8ef 100644 (file)
@@ -416,6 +416,30 @@ define U-Boot/mt7986_bananapi_bpi-r3-nor
   FIP_COMPRESS:=1
 endef
 
+define U-Boot/mt7986_bananapi_bpi-r3-mini-emmc
+  NAME:=BananaPi BPi-R3
+  BUILD_SUBTARGET:=filogic
+  BUILD_DEVICES:=bananapi_bpi-r3-mini
+  UBOOT_CONFIG:=mt7986a_bpi-r3-mini-emmc
+  UBOOT_IMAGE:=u-boot.fip
+  BL2_BOOTDEV:=emmc
+  BL2_SOC:=mt7986
+  BL2_DDRTYPE:=ddr4
+  DEPENDS:=+trusted-firmware-a-mt7986-emmc-ddr4
+endef
+
+define U-Boot/mt7986_bananapi_bpi-r3-mini-snand
+  NAME:=BananaPi BPi-R3
+  BUILD_SUBTARGET:=filogic
+  BUILD_DEVICES:=bananapi_bpi-r3-mini
+  UBOOT_CONFIG:=mt7986a_bpi-r3-mini-snand
+  UBOOT_IMAGE:=u-boot.fip
+  BL2_BOOTDEV:=spim-nand-ubi
+  BL2_SOC:=mt7986
+  BL2_DDRTYPE:=ddr4
+  DEPENDS:=+trusted-firmware-a-mt7986-spim-nand-ubi-ddr4
+endef
+
 define U-Boot/mt7986_glinet_gl-mt6000
   NAME:=GL.iNet GL-MT6000
   BUILD_SUBTARGET:=filogic
@@ -632,6 +656,8 @@ UBOOT_TARGETS := \
        mt7986_bananapi_bpi-r3-sdmmc \
        mt7986_bananapi_bpi-r3-snand \
        mt7986_bananapi_bpi-r3-nor \
+       mt7986_bananapi_bpi-r3-mini-emmc \
+       mt7986_bananapi_bpi-r3-mini-snand \
        mt7986_glinet_gl-mt6000 \
        mt7986_jdcloud_re-cp-03 \
        mt7986_tplink_tl-xdr4288 \
diff --git a/package/boot/uboot-mediatek/patches/160-net-phy-add-support-for-Airoha-ethernet-PHY-driver.patch b/package/boot/uboot-mediatek/patches/160-net-phy-add-support-for-Airoha-ethernet-PHY-driver.patch
new file mode 100644 (file)
index 0000000..f8e8659
--- /dev/null
@@ -0,0 +1,1929 @@
+From 70157a6148ad47734f1dc646b4157ca83cc5df9f Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Thu, 13 Jul 2023 16:34:48 +0800
+Subject: [PATCH] net: phy: add support for Airoha ethernet PHY driver
+
+This patch adds support for Airoha ethernet PHY driver.
+
+If GMAC2 of your board connects to Airoha EN8801S, please change the eth
+node as follow:
+
+&eth {
+        status = "okay";
+        mediatek,gmac-id = <1>;
+        mediatek,sgmiisys = <&sgmiisys1>;
+        phy-mode = "sgmii";
+        phy-handle = <&phy5>;
+
+        phy5: eth-phy@5 {
+                reg = <24>;
+        };
+};
+
+If GMAC2 of your board connects to Airoha EN8811H, please change the eth
+node as follow:
+
+&eth {
+        status = "okay";
+        mediatek,gmac-id = <1>;
+        mediatek,sgmiisys = <&sgmiisys1>;
+        phy-mode = "2500base-x";
+        phy-handle = <&phy5>;
+
+        fixed-link {
+                speed = <2500>;
+                full-duplex;
+        };
+
+        phy5: eth-phy@5 {
+                reg = <15>;
+        };
+};
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ .../drivers/net/phy/Kconfig                   |   15 +
+ .../drivers/net/phy/Makefile                  |    2 +
+ .../drivers/net/phy/air_en8801s.c             |  633 ++
+ .../drivers/net/phy/air_en8801s.h             |  267 +
+ .../drivers/net/phy/air_en8811h.c             |  649 ++
+ .../drivers/net/phy/air_en8811h.h             |  160 +
+ .../drivers/net/phy/air_en8811h_fw.h          | 9227 +++++++++++++++++
+ 7 files changed, 10953 insertions(+)
+ create mode 100644 drivers/net/phy/air_en8801s.c
+ create mode 100644 drivers/net/phy/air_en8801s.h
+ create mode 100644 drivers/net/phy/air_en8811h.c
+ create mode 100644 drivers/net/phy/air_en8811h.h
+ create mode 100644 drivers/net/phy/air_en8811h_fw.h
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -77,6 +77,37 @@ config PHY_ADIN
+       help
+               Add support for configuring RGMII on Analog Devices ADIN PHYs.
++menuconfig PHY_AIROHA
++      bool "Airoha Ethernet PHYs support"
++
++config PHY_AIROHA_EN8801S
++      bool "Airoha Ethernet EN8801S support"
++      depends on PHY_AIROHA
++      help
++              AIROHA EN8801S supported.
++
++config PHY_AIROHA_EN8811H
++      bool "Airoha Ethernet EN8811H support"
++      depends on PHY_AIROHA
++      help
++              AIROHA EN8811H supported.
++
++choice
++      prompt "Location of the Airoha PHY firmware"
++      default PHY_AIROHA_FW_IN_UBI
++      depends on PHY_AIROHA_EN8811H
++
++config PHY_AIROHA_FW_IN_MMC
++      bool "Airoha firmware in MMC boot1 partition"
++
++config PHY_AIROHA_FW_IN_UBI
++      bool "Airoha firmware in UBI volume en8811h-fw on NAND flash"
++
++config PHY_AIROHA_FW_IN_MTD
++      bool "Airoha firmware in MTD partition on raw flash"
++
++endchoice
++
+ menuconfig PHY_AQUANTIA
+       bool "Aquantia Ethernet PHYs support"
+       select PHY_GIGE
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -11,6 +11,8 @@ obj-$(CONFIG_MV88E6352_SWITCH) += mv88e6
+ obj-$(CONFIG_PHYLIB) += phy.o
+ obj-$(CONFIG_PHYLIB_10G) += generic_10g.o
+ obj-$(CONFIG_PHY_ADIN) += adin.o
++obj-$(CONFIG_PHY_AIROHA_EN8801S) += air_en8801s.o
++obj-$(CONFIG_PHY_AIROHA_EN8811H) += air_en8811h.o
+ obj-$(CONFIG_PHY_AQUANTIA) += aquantia.o
+ obj-$(CONFIG_PHY_ATHEROS) += atheros.o
+ obj-$(CONFIG_PHY_BROADCOM) += broadcom.o
+--- /dev/null
++++ b/drivers/net/phy/air_en8801s.c
+@@ -0,0 +1,633 @@
++// SPDX-License-Identifier: GPL-2.0
++/*************************************************
++ * FILE NAME:  air_en8801s.c
++ * PURPOSE:
++ *      EN8801S PHY Driver for Uboot
++ * NOTES:
++ *
++ *  Copyright (C) 2023 Airoha Technology Corp.
++ *************************************************/
++
++/* INCLUDE FILE DECLARATIONS
++ */
++#include <common.h>
++#include <phy.h>
++#include <errno.h>
++#include <version.h>
++#include "air_en8801s.h"
++
++#if AIR_UBOOT_REVISION > 0x202004
++#include <linux/delay.h>
++#endif
++
++static struct phy_device *s_phydev = 0;
++/******************************************************
++ * The following led_cfg example is for reference only.
++ * LED5 1000M/LINK/ACT   (GPIO5)  <-> BASE_T_LED0,
++ * LED6 10/100M/LINK/ACT (GPIO9)  <-> BASE_T_LED1,
++ * LED4 100M/LINK/ACT    (GPIO8)  <-> BASE_T_LED2,
++ ******************************************************/
++/* User-defined.B */
++#define AIR_LED_SUPPORT
++#ifdef AIR_LED_SUPPORT
++static const AIR_BASE_T_LED_CFG_T led_cfg[4] =
++{
++    /*
++     *    LED Enable,     GPIO,       LED Polarity,            LED ON,               LED Blink
++     */
++         {LED_ENABLE,       5,       AIR_ACTIVE_LOW,      BASE_T_LED0_ON_CFG,    BASE_T_LED0_BLK_CFG}, /* BASE-T LED0 */
++         {LED_ENABLE,       9,       AIR_ACTIVE_LOW,      BASE_T_LED1_ON_CFG,    BASE_T_LED1_BLK_CFG}, /* BASE-T LED1 */
++         {LED_ENABLE,       8,       AIR_ACTIVE_LOW,      BASE_T_LED2_ON_CFG,    BASE_T_LED2_BLK_CFG}, /* BASE-T LED2 */
++         {LED_DISABLE,      1,       AIR_ACTIVE_LOW,      BASE_T_LED3_ON_CFG,    BASE_T_LED3_BLK_CFG}  /* BASE-T LED3 */
++};
++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 airoha_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;
++}
++
++/* Airoha MII write function */
++static int airoha_cl22_write(struct mii_dev *bus, int phy_addr, int phy_register, int write_data)
++{
++    int ret = bus->write(bus, phy_addr, MDIO_DEVAD_NONE, phy_register, write_data);
++
++    return ret;
++}
++
++static int airoha_cl45_write(struct phy_device *phydev, int devad, int reg, int val)
++{
++    int ret = 0;
++
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, devad);
++    AIR_RTN_ERR(ret);
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, reg);
++    AIR_RTN_ERR(ret);
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
++    AIR_RTN_ERR(ret);
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, val);
++    AIR_RTN_ERR(ret);
++    return ret;
++}
++
++static int airoha_cl45_read(struct phy_device *phydev, int devad, int reg)
++{
++    int read_data, ret;
++
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, devad);
++    AIR_RTN_ERR(ret);
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, reg);
++    AIR_RTN_ERR(ret);
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
++    AIR_RTN_ERR(ret);
++    read_data = phy_read(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG);
++    if (read_data < 0)
++        return -EIO;
++    return read_data;
++}
++
++/* EN8801 PBUS write function */
++int airoha_pbus_write(struct mii_dev *bus, int pbus_addr, int pbus_reg, unsigned long pbus_data)
++{
++    int ret = 0;
++
++    ret = airoha_cl22_write(bus, pbus_addr, 0x1F, (pbus_reg >> 6));
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl22_write(bus, pbus_addr, ((pbus_reg >> 2) & 0xf), (pbus_data & 0xFFFF));
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl22_write(bus, pbus_addr, 0x10, (pbus_data >> 16));
++    AIR_RTN_ERR(ret);
++    return ret;
++}
++
++/* EN8801 PBUS read function */
++unsigned long airoha_pbus_read(struct mii_dev *bus, int pbus_addr, int pbus_reg)
++{
++    unsigned long pbus_data;
++    unsigned int pbus_data_low, pbus_data_high;
++
++    airoha_cl22_write(bus, pbus_addr, 0x1F, (pbus_reg >> 6));
++    pbus_data_low = airoha_cl22_read(bus, pbus_addr, ((pbus_reg >> 2) & 0xf));
++    pbus_data_high = airoha_cl22_read(bus, pbus_addr, 0x10);
++    pbus_data = (pbus_data_high << 16) + pbus_data_low;
++    return pbus_data;
++}
++
++/* Airoha Token Ring Write function */
++static int airoha_tr_reg_write(struct phy_device *phydev, unsigned long tr_address, unsigned long tr_data)
++{
++    int ret;
++
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, 0x52b5);       /* page select */
++    AIR_RTN_ERR(ret);
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x11, (int)(tr_data & 0xffff));
++    AIR_RTN_ERR(ret);
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x12, (int)(tr_data >> 16));
++    AIR_RTN_ERR(ret);
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (int)(tr_address | TrReg_WR));
++    AIR_RTN_ERR(ret);
++    ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, 0x0);          /* page resetore */
++    AIR_RTN_ERR(ret);
++    return ret;
++}
++
++int airoha_phy_process(void)
++{
++    int ret = 0, pbus_addr = EN8801S_PBUS_PHY_ID;
++    unsigned long pbus_data;
++    struct mii_dev *mbus;
++
++    mbus = s_phydev->bus;
++    pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x19e0);
++    pbus_data |= BIT(0);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x19e0, pbus_data);
++    if(ret)
++        printf("error: airoha_pbus_write fail ret: %d\n", ret);
++    pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x19e0);
++    pbus_data &= ~BIT(0);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x19e0, pbus_data);
++    if(ret)
++        printf("error: airoha_pbus_write fail ret: %d\n", ret);
++
++    if(ret)
++        printf("error: FCM regs reset fail, ret: %d\n", ret);
++    else
++        debug("FCM regs reset successful\n");
++    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)
++{
++    int ret = 0;
++
++    if (AIR_ACTIVE_HIGH == polar) {
++        on_evt |= LED_ON_POL;
++    } else {
++        on_evt &= ~LED_ON_POL;
++    }
++    ret = airoha_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), on_evt | LED_ON_EN);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt);
++    AIR_RTN_ERR(ret);
++    return 0;
++}
++
++static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
++{
++    u16 cl45_data;
++    int err = 0;
++
++    cl45_data = airoha_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 = airoha_cl45_write(phydev, 0x1f, LED_BCR, cl45_data);
++    AIR_RTN_ERR(err);
++    return 0;
++}
++
++static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
++{
++    u16 cl45_data;
++    int err;
++
++    cl45_data = airoha_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity));
++    if (LED_ENABLE == state) {
++        cl45_data |= LED_ON_EN;
++    } else {
++        cl45_data &= ~LED_ON_EN;
++    }
++
++    err = airoha_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data);
++    AIR_RTN_ERR(err);
++    return 0;
++}
++
++static int en8801s_led_init(struct phy_device *phydev)
++{
++
++    unsigned long led_gpio = 0, reg_value = 0;
++    int ret = 0, led_id;
++    struct mii_dev *mbus = phydev->bus;
++    int gpio_led_rg[3] = {0x1870, 0x1874, 0x1878};
++    u16 cl45_data = led_dur;
++
++    ret = airoha_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data);
++    AIR_RTN_ERR(ret);
++    cl45_data >>= 1;
++    ret = airoha_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data);
++    AIR_RTN_ERR(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 < EN8801S_LED_COUNT; led_id++) {
++        reg_value = 0;
++        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;
++        }
++        if (LED_ENABLE == led_cfg[led_id].en) {
++            if ( (led_cfg[led_id].gpio < 0) || led_cfg[led_id].gpio > 9) {
++                printf("GPIO%d is out of range!! GPIO number is 0~9.\n", led_cfg[led_id].gpio);
++                return -EIO;
++            }
++            led_gpio |= BIT(led_cfg[led_id].gpio);
++            reg_value = airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, gpio_led_rg[led_cfg[led_id].gpio / 4]);
++            LED_SET_GPIO_SEL(led_cfg[led_id].gpio, led_id, reg_value);
++            debug("[Airoha] gpio%d, reg_value 0x%lx\n", led_cfg[led_id].gpio, reg_value);
++            ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, gpio_led_rg[led_cfg[led_id].gpio / 4], reg_value);
++            AIR_RTN_ERR(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 usr def, ret %d !\n", ret);
++                return ret;
++            }
++        }
++    }
++    reg_value = (airoha_pbus_read(mbus, EN8801S_PBUS_PHY_ID, 0x1880) & ~led_gpio);
++    ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x1880, reg_value);
++    AIR_RTN_ERR(ret);
++    ret = airoha_pbus_write(mbus, EN8801S_PBUS_PHY_ID, 0x186c, led_gpio);
++    AIR_RTN_ERR(ret);
++
++    printf("LED initialize OK !\n");
++    return 0;
++}
++#endif /* AIR_LED_SUPPORT */
++
++static int en8801s_config(struct phy_device *phydev)
++{
++    int reg_value = 0, ret = 0;
++    struct mii_dev *mbus = phydev->bus;
++    int retry, pbus_addr = EN8801S_PBUS_DEFAULT_ID;
++    int phy_addr = EN8801S_MDIO_PHY_ID;
++    unsigned long pbus_data = 0;
++    gephy_all_REG_LpiReg1Ch      GPHY_RG_LPI_1C;
++    gephy_all_REG_dev1Eh_reg324h GPHY_RG_1E_324;
++    gephy_all_REG_dev1Eh_reg012h GPHY_RG_1E_012;
++    gephy_all_REG_dev1Eh_reg017h GPHY_RG_1E_017;
++
++    s_phydev = phydev;
++    retry = MAX_OUI_CHECK;
++    while (1) {
++        /* PHY OUI */
++        pbus_data = airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_ETHER_PHY_OUI);
++        if (EN8801S_PBUS_OUI == pbus_data) {
++            printf("PBUS addr 0x%x: Start initialized.\n", pbus_addr);
++            ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_BUCK_CTL, 0x03);
++            AIR_RTN_ERR(ret);
++            break;
++        } else
++            pbus_addr = EN8801S_PBUS_PHY_ID;
++
++        if (0 == --retry) {
++            printf("EN8801S Probe fail !\n");
++            return 0;
++        }
++    }
++
++    /* SMI ADDR */
++    pbus_data = airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_SMI_ADDR);
++    pbus_data = (pbus_data & 0xffff0000) | (unsigned long)(pbus_addr << 8) | (unsigned long)(EN8801S_MDIO_DEFAULT_ID);
++    ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_SMI_ADDR, pbus_data);
++    AIR_RTN_ERR(ret);
++    mdelay(10);
++
++    pbus_data = (airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_LTR_CTL) & (~0x3)) | BIT(2) ;
++    ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_LTR_CTL, pbus_data);
++    AIR_RTN_ERR(ret);
++    mdelay(500);
++    pbus_data = (pbus_data & ~BIT(2)) | EN8801S_RX_POLARITY_NORMAL | EN8801S_TX_POLARITY_NORMAL;
++    ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_LTR_CTL, pbus_data);
++    AIR_RTN_ERR(ret);
++    mdelay(500);
++    /* SMI ADDR */
++    pbus_data = airoha_pbus_read(mbus, pbus_addr, EN8801S_RG_SMI_ADDR);
++    pbus_data = (pbus_data & 0xffff0000) | (unsigned long)(EN8801S_PBUS_PHY_ID << 8) | (unsigned long)(EN8801S_MDIO_PHY_ID);
++    ret = airoha_pbus_write(mbus, pbus_addr, EN8801S_RG_SMI_ADDR, pbus_data);
++    pbus_addr = EN8801S_PBUS_PHY_ID;
++    AIR_RTN_ERR(ret);
++    mdelay(10);
++
++    /* Optimze 10M IoT */
++    pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1690);
++    pbus_data |= (1 << 31);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x1690, pbus_data);
++    AIR_RTN_ERR(ret);
++    /* set SGMII Base Page */
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00);
++    AIR_RTN_ERR(ret);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD801);
++    AIR_RTN_ERR(ret);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x0,  0x9140);
++    AIR_RTN_ERR(ret);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x0003);
++    AIR_RTN_ERR(ret);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00);
++    AIR_RTN_ERR(ret);
++    /* Set FCM control */
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x004b);
++    AIR_RTN_ERR(ret);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007);
++    AIR_RTN_ERR(ret);
++
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x142c, 0x05050505);
++    AIR_RTN_ERR(ret);
++    pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1440);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x1440, pbus_data & ~BIT(11));
++    AIR_RTN_ERR(ret);
++
++    pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1408);
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x1408, pbus_data | BIT(5));
++    AIR_RTN_ERR(ret);
++
++    /* Set GPHY Perfomance*/
++    /* Token Ring */
++    ret = airoha_tr_reg_write(phydev, RgAddr_R1000DEC_15h, 0x0055A0);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_R1000DEC_17h, 0x07FF3F);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_PMA_00h,      0x00001E);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_PMA_01h,      0x6FB90A);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_PMA_17h,      0x060671);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_PMA_18h,      0x0E2F00);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_TR_26h,       0x444444);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_03h,     0x000000);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_06h,     0x2EBAEF);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_08h,     0x00000B);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Ch,     0x00504D);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Dh,     0x02314F);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_0Fh,     0x003028);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_10h,     0x005010);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_11h,     0x040001);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_13h,     0x018670);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_14h,     0x00024A);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_1Bh,     0x000072);
++    AIR_RTN_ERR(ret);
++    ret = airoha_tr_reg_write(phydev, RgAddr_DSPF_1Ch,     0x003210);
++    AIR_RTN_ERR(ret);
++    /* CL22 & CL45 */
++    ret = airoha_cl22_write(mbus, phy_addr, 0x1f, 0x03);
++    AIR_RTN_ERR(ret);
++    GPHY_RG_LPI_1C.DATA = airoha_cl22_read(mbus, phy_addr, RgAddr_LPI_1Ch);
++    if (GPHY_RG_LPI_1C.DATA < 0)
++        return -EIO;
++    GPHY_RG_LPI_1C.DataBitField.smi_deton_th = 0x0C;
++    ret = airoha_cl22_write(mbus, phy_addr, RgAddr_LPI_1Ch, GPHY_RG_LPI_1C.DATA);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl22_write(mbus, phy_addr, RgAddr_LPI_1Ch, 0xC92);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl22_write(mbus, phy_addr, RgAddr_AUXILIARY_1Dh, 0x1);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl22_write(mbus, phy_addr, 0x1f, 0x0);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x120, 0x8014);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x122, 0xffff);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x123, 0xffff);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x144, 0x0200);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x14A, 0xEE20);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x189, 0x0110);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x19B, 0x0111);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x234, 0x0181);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x238, 0x0120);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x239, 0x0117);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x268, 0x07F4);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x2D1, 0x0733);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x323, 0x0011);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x324, 0x013F);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x326, 0x0037);
++    AIR_RTN_ERR(ret);
++
++    reg_value = airoha_cl45_read(phydev, 0x1E, 0x324);
++    if (reg_value < 0)
++        return -EIO;
++    GPHY_RG_1E_324.DATA = (int)reg_value;
++    GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = 0;
++    ret = airoha_cl45_write(phydev, 0x1E, 0x324, GPHY_RG_1E_324.DATA);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x19E, 0xC2);
++    AIR_RTN_ERR(ret);
++    ret = airoha_cl45_write(phydev, 0x1E, 0x013, 0x0);
++    AIR_RTN_ERR(ret);
++
++    /* EFUSE */
++    airoha_pbus_write(mbus, pbus_addr, 0x1C08, 0x40000040);
++    retry = MAX_RETRY;
++    while (0 != retry) {
++        mdelay(1);
++        pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C08);
++        if ((pbus_data & (1 << 30)) == 0) {
++            break;
++        }
++        retry--;
++    }
++    pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C38);          /* RAW#2 */
++    reg_value = airoha_cl45_read(phydev, 0x1E, 0x12);
++    if (reg_value < 0)
++        return -EIO;
++    GPHY_RG_1E_012.DATA = reg_value;
++    GPHY_RG_1E_012.DataBitField.da_tx_i2mpb_a_tbt = pbus_data & 0x03f;
++    ret = airoha_cl45_write(phydev, 0x1E, 0x12, GPHY_RG_1E_012.DATA);
++    AIR_RTN_ERR(ret);
++    reg_value = airoha_cl45_read(phydev, 0x1E, 0x17);
++    if (reg_value < 0)
++        return -EIO;
++    GPHY_RG_1E_017.DataBitField.da_tx_i2mpb_b_tbt = (reg_value >> 8) & 0x03f;
++    ret = airoha_cl45_write(phydev, 0x1E, 0x17, GPHY_RG_1E_017.DATA);
++    AIR_RTN_ERR(ret);
++
++    ret = airoha_pbus_write(mbus, pbus_addr, 0x1C08, 0x40400040);
++    AIR_RTN_ERR(ret);
++    retry = MAX_RETRY;
++    while (0 != retry) {
++        mdelay(1);
++        reg_value = airoha_pbus_read(mbus, pbus_addr, 0x1C08);
++        if ((reg_value & (1 << 30)) == 0) {
++            break;
++        }
++        retry--;
++    }
++    pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1C30);          /* RAW#16 */
++    GPHY_RG_1E_324.DataBitField.smi_det_deglitch_off = (pbus_data >> 12) & 0x01;
++    ret = airoha_cl45_write(phydev, 0x1E, 0x324, GPHY_RG_1E_324.DATA);
++    AIR_RTN_ERR(ret);
++#ifdef AIR_LED_SUPPORT
++    ret = en8801s_led_init(phydev);
++    if (ret != 0){
++        printf("en8801s_led_init fail (ret:%d) !\n", ret);
++    }
++#endif
++    printf("EN8801S initialize OK ! (%s)\n", EN8801S_DRIVER_VERSION);
++    return 0;
++}
++
++int en8801s_read_status(struct phy_device *phydev)
++{
++    int ret, pbus_addr = EN8801S_PBUS_PHY_ID;
++    struct mii_dev *mbus;
++    unsigned long pbus_data;
++
++    mbus = phydev->bus;
++    if (SPEED_10 == phydev->speed) {
++        /* set the bit for Optimze 10M IoT */
++        debug("[Airoha] SPEED_10 0x1694\n");
++        pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1694);
++        pbus_data |= (1 << 31);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x1694, pbus_data);
++        AIR_RTN_ERR(ret);
++    } else {
++        debug("[Airoha] SPEED_1000/100 0x1694\n");
++        /* clear the bit for other speeds */
++        pbus_data = airoha_pbus_read(mbus, pbus_addr, 0x1694);
++        pbus_data &= ~(1 << 31);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x1694, pbus_data);
++        AIR_RTN_ERR(ret);
++    }
++
++    airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00);
++    if(SPEED_1000 == phydev->speed) {
++        debug("[Airoha] SPEED_1000\n");
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD801);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x0,  0x9140);
++        AIR_RTN_ERR(ret);
++
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x0003);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c000c00);
++        AIR_RTN_ERR(ret);
++        mdelay(2);  /* delay 2 ms */
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x004b);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007);
++        AIR_RTN_ERR(ret);
++    }
++    else if (SPEED_100 == phydev->speed) {
++        debug("[Airoha] SPEED_100\n");
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD401);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x0,  0x9140);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x0007);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c11);
++        AIR_RTN_ERR(ret);
++        mdelay(2);  /* delay 2 ms */
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x0027);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007);
++        AIR_RTN_ERR(ret);
++    }
++    else {
++        debug("[Airoha] SPEED_10\n");
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x10, 0xD001);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x0,  0x9140);
++        AIR_RTN_ERR(ret);
++
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x0A14, 0x000b);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x0600, 0x0c11);
++        AIR_RTN_ERR(ret);
++         mdelay(2);  /* delay 2 ms */
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x1404, 0x0047);
++        AIR_RTN_ERR(ret);
++        ret = airoha_pbus_write(mbus, pbus_addr, 0x140c, 0x0007);
++        AIR_RTN_ERR(ret);
++    }
++    return 0;
++}
++
++static int en8801s_startup(struct phy_device *phydev)
++{
++    int ret;
++
++    ret = genphy_update_link(phydev);
++    if (ret)
++        return ret;
++    ret = genphy_parse_link(phydev);
++    if (ret)
++        return ret;
++    return en8801s_read_status(phydev);
++}
++#if AIR_UBOOT_REVISION > 0x202303
++U_BOOT_PHY_DRIVER(en8801s) = {
++    .name = "Airoha EN8801S",
++    .uid = EN8801S_PHY_ID,
++    .mask = 0x0ffffff0,
++    .features = PHY_GBIT_FEATURES,
++    .config = &en8801s_config,
++    .startup = &en8801s_startup,
++    .shutdown = &genphy_shutdown,
++};
++#else
++static struct phy_driver AIR_EN8801S_driver = {
++    .name = "Airoha EN8801S",
++    .uid = EN8801S_PHY_ID,
++    .mask = 0x0ffffff0,
++    .features = PHY_GBIT_FEATURES,
++    .config = &en8801s_config,
++    .startup = &en8801s_startup,
++    .shutdown = &genphy_shutdown,
++};
++
++int phy_air_en8801s_init(void)
++{
++    phy_register(&AIR_EN8801S_driver);
++    return 0;
++}
++#endif
+--- /dev/null
++++ b/drivers/net/phy/air_en8801s.h
+@@ -0,0 +1,267 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*************************************************
++ * FILE NAME:  air_en8801s.h
++ * PURPOSE:
++ *      EN8801S PHY Driver for Uboot
++ * NOTES:
++ *
++ *  Copyright (C) 2023 Airoha Technology Corp.
++ *************************************************/
++
++#ifndef __EN8801S_H
++#define __EN8801S_H
++
++/************************************************************************
++*                  D E F I N E S
++************************************************************************/
++#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 EN8801S_MDIO_DEFAULT_ID 0x1d
++#define EN8801S_PBUS_DEFAULT_ID (EN8801S_MDIO_DEFAULT_ID + 1)
++#define EN8801S_MDIO_PHY_ID     0x18       /* Range PHY_ADDRESS_RANGE .. 0x1e */
++#define EN8801S_PBUS_PHY_ID     (EN8801S_MDIO_PHY_ID + 1)
++#define EN8801S_DRIVER_VERSION      "v1.1.3"
++
++#define EN8801S_RG_ETHER_PHY_OUI    0x19a4
++#define EN8801S_RG_SMI_ADDR         0x19a8
++#define EN8801S_PBUS_OUI            0x17a5
++#define EN8801S_RG_BUCK_CTL         0x1a20
++#define EN8801S_RG_LTR_CTL          0x0cf8
++
++#define EN8801S_PHY_ID1         0x03a2
++#define EN8801S_PHY_ID2         0x9461
++#define EN8801S_PHY_ID          (unsigned long)((EN8801S_PHY_ID1 << 16) | EN8801S_PHY_ID2)
++
++/*
++SFP Sample for verification
++Tx Reverse, Rx Reverse
++*/
++#define EN8801S_TX_POLARITY_NORMAL   0x0
++#define EN8801S_TX_POLARITY_REVERSE  0x1
++
++#define EN8801S_RX_POLARITY_NORMAL   (0x1 << 1)
++#define EN8801S_RX_POLARITY_REVERSE  (0x0 << 1)
++
++#ifndef BIT
++#define BIT(nr)                       (1UL << (nr))
++#endif
++
++#define MAX_RETRY               5
++#define MAX_OUI_CHECK           2
++
++/* CL45 MDIO control */
++#define MII_MMD_ACC_CTL_REG     0x0d
++#define MII_MMD_ADDR_DATA_REG   0x0e
++#define MMD_OP_MODE_DATA        BIT(14)
++
++#define MAX_TRG_COUNTER         5
++
++/* TokenRing Reg Access */
++#define TrReg_PKT_XMT_STA       0x8000
++#define TrReg_WR                0x8000
++#define TrReg_RD                0xA000
++
++#define RgAddr_LPI_1Ch       0x1c
++#define RgAddr_AUXILIARY_1Dh 0x1d
++#define RgAddr_PMA_00h       0x0f80
++#define RgAddr_PMA_01h       0x0f82
++#define RgAddr_PMA_17h       0x0fae
++#define RgAddr_PMA_18h       0x0fb0
++#define RgAddr_DSPF_03h      0x1686
++#define RgAddr_DSPF_06h      0x168c
++#define RgAddr_DSPF_08h      0x1690
++#define RgAddr_DSPF_0Ch      0x1698
++#define RgAddr_DSPF_0Dh      0x169a
++#define RgAddr_DSPF_0Fh      0x169e
++#define RgAddr_DSPF_10h      0x16a0
++#define RgAddr_DSPF_11h      0x16a2
++#define RgAddr_DSPF_13h      0x16a6
++#define RgAddr_DSPF_14h      0x16a8
++#define RgAddr_DSPF_1Bh      0x16b6
++#define RgAddr_DSPF_1Ch      0x16b8
++#define RgAddr_TR_26h        0x0ecc
++#define RgAddr_R1000DEC_15h  0x03aa
++#define RgAddr_R1000DEC_17h  0x03ae
++
++/*
++The following led_cfg example is for reference only.
++LED5 1000M/LINK/ACT  (GPIO5)  <-> BASE_T_LED0,
++LED6 10/100M/LINK/ACT(GPIO9)  <-> BASE_T_LED1,
++LED4 100M/LINK/ACT   (GPIO8)  <-> BASE_T_LED2,
++*/
++/* User-defined.B */
++#define BASE_T_LED0_ON_CFG      (LED_ON_EVT_LINK_1000M)
++#define BASE_T_LED0_BLK_CFG     (LED_BLK_EVT_1000M_TX_ACT | LED_BLK_EVT_1000M_RX_ACT)
++#define BASE_T_LED1_ON_CFG      (LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M)
++#define BASE_T_LED1_BLK_CFG     (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT | \
++                                 LED_BLK_EVT_10M_TX_ACT | LED_BLK_EVT_10M_RX_ACT )
++#define BASE_T_LED2_ON_CFG      (LED_ON_EVT_LINK_100M)
++#define BASE_T_LED2_BLK_CFG     (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT)
++#define BASE_T_LED3_ON_CFG      (0x0)
++#define BASE_T_LED3_BLK_CFG     (0x0)
++/* User-defined.E */
++
++#define EN8801S_LED_COUNT       4
++
++#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_ON_CTRL(i)              (0x024 + ((i)*2))
++#define LED_ON_EN                   (1 << 15)
++#define LED_ON_POL                  (1 << 14)
++#define LED_ON_EVT_MASK             (0x7f)
++/* LED ON Event Option.B */
++#define LED_ON_EVT_FORCE            (1 << 6)
++#define LED_ON_EVT_LINK_DOWN        (1 << 3)
++#define LED_ON_EVT_LINK_10M         (1 << 2)
++#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            (0x3ff)
++/* LED Blinking Event Option.B*/
++#define LED_BLK_EVT_FORCE           (1 << 9)
++#define LED_BLK_EVT_10M_RX_ACT      (1 << 5)
++#define LED_BLK_EVT_10M_TX_ACT      (1 << 4)
++#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_ON_DUR                  (0x022)
++#define LED_ON_DUR_MASK             (0xffff)
++#define LED_BLK_DUR                 (0x023)
++#define LED_BLK_DUR_MASK            (0xffff)
++
++#define LED_ENABLE                  1
++#define LED_DISABLE                 0
++
++#define UNIT_LED_BLINK_DURATION     1024
++
++#define AIR_RTN_ON_ERR(cond, err)  \
++    do { if ((cond)) return (err); } while(0)
++
++#define AIR_RTN_ERR(err)                       AIR_RTN_ON_ERR(err < 0, err)
++
++#define LED_SET_EVT(reg, cod, result, bit) do         \
++    {                                                 \
++        if(reg & cod) {                               \
++            result |= bit;                            \
++        }                                             \
++    } while(0)
++
++#define LED_SET_GPIO_SEL(gpio, led, val) do           \
++    {                                                 \
++            val |= (led << (8 * (gpio % 4)));         \
++    } while(0)
++
++/* DATA TYPE DECLARATIONS
++ */
++typedef struct
++{
++    int DATA_Lo;
++    int DATA_Hi;
++}TR_DATA_T;
++
++typedef union
++{
++    struct
++    {
++        /* b[15:00] */
++        int smi_deton_wt                             : 3;
++        int smi_det_mdi_inv                          : 1;
++        int smi_detoff_wt                            : 3;
++        int smi_sigdet_debouncing_en                 : 1;
++        int smi_deton_th                             : 6;
++        int rsv_14                                   : 2;
++    } DataBitField;
++    int DATA;
++} gephy_all_REG_LpiReg1Ch, *Pgephy_all_REG_LpiReg1Ch;
++
++typedef union
++{
++    struct
++    {
++        /* b[15:00] */
++        int rg_smi_detcnt_max                        : 6;
++        int rsv_6                                    : 2;
++        int rg_smi_det_max_en                        : 1;
++        int smi_det_deglitch_off                     : 1;
++        int rsv_10                                   : 6;
++    } DataBitField;
++    int DATA;
++} gephy_all_REG_dev1Eh_reg324h, *Pgephy_all_REG_dev1Eh_reg324h;
++
++typedef union
++{
++    struct
++    {
++        /* b[15:00] */
++        int da_tx_i2mpb_a_tbt                        : 6;
++        int rsv_6                                    : 4;
++        int da_tx_i2mpb_a_gbe                        : 6;
++    } DataBitField;
++    int DATA;
++} gephy_all_REG_dev1Eh_reg012h, *Pgephy_all_REG_dev1Eh_reg012h;
++
++typedef union
++{
++    struct
++    {
++        /* b[15:00] */
++        int da_tx_i2mpb_b_tbt                        : 6;
++        int rsv_6                                    : 2;
++        int da_tx_i2mpb_b_gbe                        : 6;
++        int rsv_14                                   : 2;
++    } DataBitField;
++    int DATA;
++} gephy_all_REG_dev1Eh_reg017h, *Pgephy_all_REG_dev1Eh_reg017h;
++
++typedef struct AIR_BASE_T_LED_CFG_S
++{
++    u16 en;
++    u16 gpio;
++    u16 pol;
++    u16 on_cfg;
++    u16 blk_cfg;
++}AIR_BASE_T_LED_CFG_T;
++
++typedef 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
++} AIR_LED_BLK_DUT_T;
++
++typedef enum
++{
++    AIR_ACTIVE_LOW,
++    AIR_ACTIVE_HIGH,
++} AIR_LED_POLARITY;
++typedef enum
++{
++    AIR_LED_MODE_DISABLE,
++    AIR_LED_MODE_USER_DEFINE,
++    AIR_LED_MODE_LAST
++} AIR_LED_MODE_T;
++
++/************************************************************************
++*                  F U N C T I O N    P R O T O T Y P E S
++************************************************************************/
++
++unsigned long airoha_pbus_read(struct mii_dev *bus, int pbus_addr, int pbus_reg);
++int airoha_pbus_write(struct mii_dev *bus, int pbus_addr, int pbus_reg, unsigned long pbus_data);
++int airoha_phy_process(void);
++#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:
++ *
++ *  Copyright (C) 2023 Airoha Technology Corp.
++ *************************************************/
++
++/* INCLUDE FILE DECLARATIONS
++*/
++#include <common.h>
++#include <eth_phy.h>
++#include <phy.h>
++#include <errno.h>
++#include <malloc.h>
++#include <version.h>
++#include "air_en8811h.h"
++
++#ifdef CONFIG_PHY_AIROHA_FW_IN_UBI
++#include <ubi_uboot.h>
++#endif
++
++#ifdef CONFIG_PHY_AIROHA_FW_IN_MMC
++#include <mmc.h>
++#endif
++
++#ifdef CONFIG_PHY_AIROHA_FW_IN_MTD
++#include <mtd.h>
++#endif
++
++#if AIR_UBOOT_REVISION > 0x202004
++#include <linux/delay.h>
++#endif
++
++/**************************
++ * 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},
++};
++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;
++}
++
++/* 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;
++
++    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;
++}
++
++static int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
++{
++    int ret = 0;
++    int data;
++
++    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;
++}
++
++static int air_mii_cl45_write(struct phy_device *phydev, int devad, u16 reg, u16 write_data)
++{
++    int ret = 0;
++
++    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)
++{
++    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;
++}
++
++/* EN8811H BUCK write function */
++static int air_buckpbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned int pbus_data)
++{
++    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;
++}
++
++/* EN8811H BUCK read function */
++static unsigned int air_buckpbus_reg_read(struct phy_device *phydev, unsigned long pbus_address)
++{
++    unsigned int pbus_data = 0, pbus_data_low, pbus_data_high;
++    int ret = 0;
++
++    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;
++    }
++
++    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;
++}
++
++static int MDIOWriteBuf(struct phy_device *phydev, unsigned long address, unsigned long array_size, const unsigned char *buffer)
++{
++    unsigned int write_data, offset ;
++    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;
++    }
++    /* 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;
++    }
++
++    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;
++}
++
++#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)
++{
++    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;
++}
++
++static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
++{
++    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;
++}
++
++static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
++{
++    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;
++}
++
++static int en8811h_led_init(struct phy_device *phydev)
++{
++    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;
++
++    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;
++}
++#endif /* AIR_LED_SUPPORT */
++
++static char *firmware_buf;
++static int en8811h_load_firmware(struct phy_device *phydev)
++{
++    u32 pbus_value;
++    int ret = 0;
++
++    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;
++        }
++
++#ifdef CONFIG_PHY_AIROHA_FW_IN_UBI
++        ret = ubi_volume_read("en8811h-fw", firmware_buf, 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;
++        }
++#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;
++        }
++#else
++#warning EN8811H firmware loading not implemented
++        free(firmware_buf);
++        firmware_buf = 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;
++
++    ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0);
++    if (ret < 0)
++        return ret;
++
++    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;
++    }
++
++    return 0;
++}
++
++static int en8811h_get_autonego(struct phy_device *phydev, int *an)
++{
++    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;
++}
++
++static int en8811h_startup(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;
++    }
++
++    if (old_link && phydev->link)
++       return 0;
++
++    phydev->speed = SPEED_100;
++    phydev->duplex = DUPLEX_FULL;
++    phydev->pause = 0;
++    phydev->asym_pause = 0;
++
++    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;
++}
++
++#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,
++};
++
++int phy_air_en8811h_init(void)
++{
++    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
++
++#define EN8811H_TX_POLARITY_NORMAL   0x1
++#define EN8811H_TX_POLARITY_REVERSE  0x0
++
++#define EN8811H_RX_POLARITY_NORMAL  (0x0 << 1)
++#define EN8811H_RX_POLARITY_REVERSE (0x1 << 1)
++
++#ifndef BIT
++#define BIT(nr)                       (1UL << (nr))
++#endif
++
++/* 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 */
++
++#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
++
++#define EN8811H_LED_COUNT       3
++
++#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)
++
++#define LED_ON_DUR                  (0x022)
++#define LED_ON_DUR_MASK             (0xffff)
++
++#define LED_BLK_DUR                 (0x023)
++#define LED_BLK_DUR_MASK            (0xffff)
++
++#define LED_GPIO_SEL_MASK           0x7FFFFFF
++
++#define UNIT_LED_BLINK_DURATION     1024
++
++#define INVALID_DATA                0xffff
++#define PBUS_INVALID_DATA           0xffffffff
++
++struct air_base_t_led_cfg_s {
++    u16 en;
++    u16 gpio;
++    u16 pol;
++    u16 on_cfg;
++    u16 blk_cfg;
++};
++
++enum {
++    AIR_LED2_GPIO3 = 3,
++    AIR_LED1_GPIO4,
++    AIR_LED0_GPIO5,
++    AIR_LED_LAST
++};
++
++enum {
++    AIR_BASE_T_LED0,
++    AIR_BASE_T_LED1,
++    AIR_BASE_T_LED2,
++    AIR_BASE_T_LED3
++};
++
++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
++};
++
++enum {
++    AIR_ACTIVE_LOW,
++    AIR_ACTIVE_HIGH,
++};
++
++enum {
++    AIR_LED_MODE_DISABLE,
++    AIR_LED_MODE_USER_DEFINE,
++    AIR_LED_MODE_LAST
++};
++
++#endif /* End of __EN8811H_MD32_H */
++
+--- a/drivers/net/eth-phy-uclass.c
++++ b/drivers/net/eth-phy-uclass.c
+@@ -155,7 +155,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
diff --git a/package/boot/uboot-mediatek/patches/442-add-bpi-r3-mini.patch b/package/boot/uboot-mediatek/patches/442-add-bpi-r3-mini.patch
new file mode 100644 (file)
index 0000000..5df81ae
--- /dev/null
@@ -0,0 +1,779 @@
+--- /dev/null
++++ b/configs/mt7986a_bpi-r3-mini-emmc_defconfig
+@@ -0,0 +1,203 @@
++CONFIG_ARM=y
++CONFIG_POSITION_INDEPENDENT=y
++CONFIG_ARCH_MEDIATEK=y
++CONFIG_TARGET_MT7986=y
++CONFIG_TEXT_BASE=0x41e00000
++CONFIG_SYS_MALLOC_F_LEN=0x4000
++CONFIG_SYS_HAS_NONCACHED_MEMORY=y
++CONFIG_NR_DRAM_BANKS=1
++CONFIG_DEFAULT_DEVICE_TREE="mt7986a-bpi-r3-mini"
++CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r3-mini_emmc_env"
++CONFIG_DEFAULT_FDT_FILE="mediatek/mt7986a-bpi-r3-mini.dtb"
++CONFIG_OF_LIBFDT_OVERLAY=y
++CONFIG_OF_SYSTEM_SETUP=y
++CONFIG_DEBUG_UART_BASE=0x11002000
++CONFIG_DEBUG_UART_CLOCK=40000000
++CONFIG_DEBUG_UART=y
++CONFIG_SYS_LOAD_ADDR=0x46000000
++CONFIG_SMBIOS_PRODUCT_NAME=""
++CONFIG_AUTOBOOT_KEYED=y
++CONFIG_BOOTDELAY=30
++CONFIG_AUTOBOOT_MENU_SHOW=y
++CONFIG_CFB_CONSOLE_ANSI=y
++CONFIG_BOARD_LATE_INIT=y
++CONFIG_BUTTON=y
++CONFIG_BUTTON_GPIO=y
++CONFIG_GPIO_HOG=y
++CONFIG_CMD_ENV_FLAGS=y
++CONFIG_FIT=y
++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y
++CONFIG_LED=y
++CONFIG_LED_BLINK=y
++CONFIG_LED_GPIO=y
++CONFIG_LOGLEVEL=7
++CONFIG_LOG=y
++CONFIG_SYS_PROMPT="MT7986> "
++CONFIG_CMD_BOOTMENU=y
++CONFIG_CMD_BOOTP=y
++CONFIG_CMD_BUTTON=y
++CONFIG_CMD_CACHE=y
++CONFIG_CMD_CDP=y
++CONFIG_CMD_CPU=y
++CONFIG_CMD_DHCP=y
++CONFIG_CMD_DM=y
++CONFIG_CMD_DNS=y
++CONFIG_CMD_ECHO=y
++CONFIG_CMD_ENV_READMEM=y
++CONFIG_CMD_ERASEENV=y
++CONFIG_CMD_EXT4=y
++CONFIG_CMD_FAT=y
++CONFIG_CMD_FDT=y
++CONFIG_CMD_FS_GENERIC=y
++CONFIG_CMD_FS_UUID=y
++CONFIG_CMD_GPIO=y
++CONFIG_CMD_GPT=y
++CONFIG_CMD_HASH=y
++CONFIG_CMD_ITEST=y
++CONFIG_CMD_LED=y
++CONFIG_CMD_LICENSE=y
++CONFIG_CMD_LINK_LOCAL=y
++# CONFIG_CMD_MBR is not set
++CONFIG_CMD_MDIO=y
++CONFIG_CMD_MII=y
++CONFIG_CMD_MMC=y
++CONFIG_CMD_MTD=y
++CONFIG_CMD_PCI=y
++CONFIG_CMD_PSTORE=y
++CONFIG_CMD_PSTORE_MEM_ADDR=0x42ff0000
++CONFIG_CMD_SF_TEST=y
++CONFIG_CMD_PING=y
++CONFIG_CMD_PXE=y
++CONFIG_CMD_PWM=y
++CONFIG_CMD_SMC=y
++CONFIG_CMD_TFTPBOOT=y
++CONFIG_CMD_TFTPSRV=y
++CONFIG_CMD_UBI=y
++CONFIG_CMD_UBI_RENAME=y
++CONFIG_CMD_UBIFS=y
++CONFIG_CMD_ASKENV=y
++CONFIG_CMD_PART=y
++CONFIG_CMD_RARP=y
++CONFIG_CMD_SETEXPR=y
++CONFIG_CMD_SLEEP=y
++CONFIG_CMD_SNTP=y
++CONFIG_CMD_SOURCE=y
++CONFIG_CMD_STRINGS=y
++CONFIG_CMD_USB=y
++CONFIG_CMD_UUID=y
++CONFIG_DISPLAY_CPUINFO=y
++CONFIG_DM_MDIO=y
++CONFIG_DM_MMC=y
++CONFIG_DM_MTD=y
++CONFIG_DM_REGULATOR=y
++CONFIG_DM_REGULATOR_FIXED=y
++CONFIG_DM_REGULATOR_GPIO=y
++CONFIG_DM_USB=y
++CONFIG_DM_PWM=y
++CONFIG_PWM_MTK=y
++CONFIG_HUSH_PARSER=y
++CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
++CONFIG_SYS_RELOC_GD_ENV_ADDR=y
++CONFIG_VERSION_VARIABLE=y
++CONFIG_PARTITION_UUIDS=y
++CONFIG_NETCONSOLE=y
++CONFIG_REGMAP=y
++CONFIG_SYSCON=y
++CONFIG_CLK=y
++CONFIG_DM_GPIO=y
++CONFIG_DM_SCSI=y
++CONFIG_AHCI=y
++CONFIG_AHCI_PCI=y
++CONFIG_SCSI_AHCI=y
++CONFIG_SCSI=y
++CONFIG_CMD_SCSI=y
++CONFIG_PHY=y
++CONFIG_PHY_MTK_TPHY=y
++CONFIG_PHY_ETHERNET_ID=y
++CONFIG_PHY_FIXED=y
++CONFIG_MTK_AHCI=y
++CONFIG_DM_ETH=y
++CONFIG_DM_ETH_PHY=y
++CONFIG_MEDIATEK_ETH=y
++CONFIG_PHY_AIROHA=y
++CONFIG_PHY_AIROHA_EN8811H=y
++CONFIG_PHY_AIROHA_FW_IN_MMC=y
++CONFIG_PCI=y
++CONFIG_MTD=y
++CONFIG_MTD_UBI_FASTMAP=y
++CONFIG_DM_PCI=y
++CONFIG_PCIE_MEDIATEK=y
++CONFIG_PINCTRL=y
++CONFIG_PINCONF=y
++CONFIG_PINCTRL_MT7622=y
++CONFIG_POWER_DOMAIN=y
++CONFIG_PRE_CONSOLE_BUFFER=y
++CONFIG_PRE_CON_BUF_ADDR=0x4007EF00
++CONFIG_MTK_POWER_DOMAIN=y
++CONFIG_RAM=y
++CONFIG_DM_SERIAL=y
++CONFIG_MTK_SERIAL=y
++CONFIG_MMC=y
++CONFIG_MMC_DEFAULT_DEV=1
++CONFIG_MMC_HS200_SUPPORT=y
++CONFIG_MMC_MTK=y
++CONFIG_MMC_SUPPORTS_TUNING=y
++CONFIG_SUPPORT_EMMC_BOOT=y
++CONFIG_SPI=y
++CONFIG_DM_SPI=y
++CONFIG_MTK_SPI_NAND=y
++CONFIG_MTK_SPI_NAND_MTD=y
++CONFIG_SYSRESET_WATCHDOG=y
++CONFIG_WDT_MTK=y
++CONFIG_LZO=y
++CONFIG_ZSTD=y
++CONFIG_HEXDUMP=y
++CONFIG_RANDOM_UUID=y
++CONFIG_REGEX=y
++CONFIG_USB=y
++CONFIG_USB_HOST=y
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_XHCI_MTK=y
++CONFIG_USB_STORAGE=y
++CONFIG_OF_EMBED=y
++CONFIG_ENV_OVERWRITE=y
++CONFIG_ENV_IS_IN_MMC=y
++CONFIG_ENV_OFFSET=0x400000
++CONFIG_ENV_OFFSET_REDUND=0x440000
++CONFIG_ENV_SIZE=0x40000
++CONFIG_ENV_SIZE_REDUND=0x40000
++CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
++CONFIG_NET_RANDOM_ETHADDR=y
++CONFIG_REGMAP=y
++CONFIG_SYSCON=y
++CONFIG_CLK=y
++CONFIG_SUPPORT_EMMC_BOOT=y
++CONFIG_MMC_HS200_SUPPORT=y
++CONFIG_MMC_MTK=y
++CONFIG_MEDIATEK_ETH=y
++CONFIG_PINCTRL=y
++CONFIG_PINCONF=y
++CONFIG_PINCTRL_MT7986=y
++CONFIG_POWER_DOMAIN=y
++CONFIG_MTK_POWER_DOMAIN=y
++CONFIG_DM_REGULATOR=y
++CONFIG_DM_REGULATOR_FIXED=y
++CONFIG_DM_SERIAL=y
++CONFIG_MTK_SERIAL=y
++CONFIG_HEXDUMP=y
++CONFIG_USE_DEFAULT_ENV_FILE=y
++CONFIG_MTD_SPI_NAND=y
++CONFIG_MTK_SPIM=y
++#CONFIG_MTK_SNOR=y
++CONFIG_DM_SPI_FLASH=y
++CONFIG_SPI_FLASH_MTD=y
++CONFIG_SPI_FLASH_WINBOND=y
++# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
++CONFIG_CMD_SF=y
++CONFIG_CMD_NAND=y
++CONFIG_CMD_NAND_TRIMFFS=y
++CONFIG_LMB_MAX_REGIONS=64
++CONFIG_USE_IPADDR=y
++CONFIG_IPADDR="192.168.1.1"
++CONFIG_USE_SERVERIP=y
++CONFIG_SERVERIP="192.168.1.254"
+--- /dev/null
++++ b/configs/mt7986a_bpi-r3-mini-snand_defconfig
+@@ -0,0 +1,203 @@
++CONFIG_ARM=y
++CONFIG_POSITION_INDEPENDENT=y
++CONFIG_ARCH_MEDIATEK=y
++CONFIG_TARGET_MT7986=y
++CONFIG_TEXT_BASE=0x41e00000
++CONFIG_SYS_MALLOC_F_LEN=0x4000
++CONFIG_SYS_HAS_NONCACHED_MEMORY=y
++CONFIG_NR_DRAM_BANKS=1
++CONFIG_DEFAULT_DEVICE_TREE="mt7986a-bpi-r3-mini"
++CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r3-mini_snand_env"
++CONFIG_DEFAULT_FDT_FILE="mediatek/mt7986a-bpi-r3-mini.dtb"
++CONFIG_OF_LIBFDT_OVERLAY=y
++CONFIG_OF_SYSTEM_SETUP=y
++CONFIG_DEBUG_UART_BASE=0x11002000
++CONFIG_DEBUG_UART_CLOCK=40000000
++CONFIG_DEBUG_UART=y
++CONFIG_SYS_LOAD_ADDR=0x46000000
++CONFIG_SMBIOS_PRODUCT_NAME=""
++CONFIG_AUTOBOOT_KEYED=y
++CONFIG_BOOTDELAY=30
++CONFIG_AUTOBOOT_MENU_SHOW=y
++CONFIG_CFB_CONSOLE_ANSI=y
++CONFIG_BOARD_LATE_INIT=y
++CONFIG_BUTTON=y
++CONFIG_BUTTON_GPIO=y
++CONFIG_GPIO_HOG=y
++CONFIG_CMD_ENV_FLAGS=y
++CONFIG_FIT=y
++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y
++CONFIG_LED=y
++CONFIG_LED_BLINK=y
++CONFIG_LED_GPIO=y
++CONFIG_LOGLEVEL=7
++CONFIG_LOG=y
++CONFIG_SYS_PROMPT="MT7986> "
++CONFIG_CMD_BOOTMENU=y
++CONFIG_CMD_BOOTP=y
++CONFIG_CMD_BUTTON=y
++CONFIG_CMD_CACHE=y
++CONFIG_CMD_CDP=y
++CONFIG_CMD_CPU=y
++CONFIG_CMD_DHCP=y
++CONFIG_CMD_DM=y
++CONFIG_CMD_DNS=y
++CONFIG_CMD_ECHO=y
++CONFIG_CMD_ENV_READMEM=y
++CONFIG_CMD_ERASEENV=y
++CONFIG_CMD_EXT4=y
++CONFIG_CMD_FAT=y
++CONFIG_CMD_FDT=y
++CONFIG_CMD_FS_GENERIC=y
++CONFIG_CMD_FS_UUID=y
++CONFIG_CMD_GPIO=y
++CONFIG_CMD_GPT=y
++CONFIG_CMD_HASH=y
++CONFIG_CMD_ITEST=y
++CONFIG_CMD_LED=y
++CONFIG_CMD_LICENSE=y
++CONFIG_CMD_LINK_LOCAL=y
++# CONFIG_CMD_MBR is not set
++CONFIG_CMD_MDIO=y
++CONFIG_CMD_MII=y
++CONFIG_CMD_MMC=y
++CONFIG_CMD_MTD=y
++CONFIG_CMD_PCI=y
++CONFIG_CMD_PSTORE=y
++CONFIG_CMD_PSTORE_MEM_ADDR=0x42ff0000
++CONFIG_CMD_SF_TEST=y
++CONFIG_CMD_PING=y
++CONFIG_CMD_PXE=y
++CONFIG_CMD_PWM=y
++CONFIG_CMD_SMC=y
++CONFIG_CMD_TFTPBOOT=y
++CONFIG_CMD_TFTPSRV=y
++CONFIG_CMD_UBI=y
++CONFIG_CMD_UBI_RENAME=y
++CONFIG_CMD_UBIFS=y
++CONFIG_CMD_ASKENV=y
++CONFIG_CMD_PART=y
++CONFIG_CMD_RARP=y
++CONFIG_CMD_SETEXPR=y
++CONFIG_CMD_SLEEP=y
++CONFIG_CMD_SNTP=y
++CONFIG_CMD_SOURCE=y
++CONFIG_CMD_STRINGS=y
++CONFIG_CMD_USB=y
++CONFIG_CMD_UUID=y
++CONFIG_DISPLAY_CPUINFO=y
++CONFIG_DM_MMC=y
++CONFIG_DM_MTD=y
++CONFIG_DM_REGULATOR=y
++CONFIG_DM_REGULATOR_FIXED=y
++CONFIG_DM_REGULATOR_GPIO=y
++CONFIG_DM_USB=y
++CONFIG_DM_PWM=y
++CONFIG_PWM_MTK=y
++CONFIG_HUSH_PARSER=y
++CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
++CONFIG_SYS_RELOC_GD_ENV_ADDR=y
++CONFIG_VERSION_VARIABLE=y
++CONFIG_PARTITION_UUIDS=y
++CONFIG_NETCONSOLE=y
++CONFIG_REGMAP=y
++CONFIG_SYSCON=y
++CONFIG_CLK=y
++CONFIG_DM_GPIO=y
++CONFIG_DM_SCSI=y
++CONFIG_AHCI=y
++CONFIG_AHCI_PCI=y
++CONFIG_SCSI_AHCI=y
++CONFIG_SCSI=y
++CONFIG_CMD_SCSI=y
++CONFIG_DM_MDIO=y
++CONFIG_PHY=y
++CONFIG_PHY_MTK_TPHY=y
++CONFIG_PHY_ETHERNET_ID=y
++CONFIG_PHY_FIXED=y
++CONFIG_MTK_AHCI=y
++CONFIG_DM_ETH=y
++CONFIG_DM_ETH_PHY=y
++CONFIG_MEDIATEK_ETH=y
++CONFIG_PCI=y
++CONFIG_MTD=y
++CONFIG_MTD_UBI_FASTMAP=y
++CONFIG_DM_PCI=y
++CONFIG_PCIE_MEDIATEK=y
++CONFIG_PINCTRL=y
++CONFIG_PINCONF=y
++CONFIG_PINCTRL_MT7622=y
++CONFIG_POWER_DOMAIN=y
++CONFIG_PRE_CONSOLE_BUFFER=y
++CONFIG_PRE_CON_BUF_ADDR=0x4007EF00
++CONFIG_MTK_POWER_DOMAIN=y
++CONFIG_RAM=y
++CONFIG_DM_SERIAL=y
++CONFIG_MTK_SERIAL=y
++CONFIG_MMC=y
++CONFIG_MMC_DEFAULT_DEV=1
++CONFIG_MMC_HS200_SUPPORT=y
++CONFIG_MMC_MTK=y
++CONFIG_MMC_SUPPORTS_TUNING=y
++CONFIG_SUPPORT_EMMC_BOOT=y
++CONFIG_SPI=y
++CONFIG_DM_SPI=y
++CONFIG_MTK_SPI_NAND=y
++CONFIG_MTK_SPI_NAND_MTD=y
++CONFIG_SYSRESET_WATCHDOG=y
++CONFIG_WDT_MTK=y
++CONFIG_LZO=y
++CONFIG_ZSTD=y
++CONFIG_HEXDUMP=y
++CONFIG_RANDOM_UUID=y
++CONFIG_REGEX=y
++CONFIG_USB=y
++CONFIG_USB_HOST=y
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_XHCI_MTK=y
++CONFIG_USB_STORAGE=y
++CONFIG_OF_EMBED=y
++CONFIG_ENV_OVERWRITE=y
++CONFIG_ENV_IS_IN_UBI=y
++CONFIG_ENV_UBI_PART="ubi"
++CONFIG_ENV_SIZE=0x1f000
++CONFIG_ENV_SIZE_REDUND=0x1f000
++CONFIG_ENV_UBI_VOLUME="ubootenv"
++CONFIG_ENV_UBI_VOLUME_REDUND="ubootenv2"
++CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
++CONFIG_NET_RANDOM_ETHADDR=y
++CONFIG_REGMAP=y
++CONFIG_SYSCON=y
++CONFIG_CLK=y
++CONFIG_SUPPORT_EMMC_BOOT=y
++CONFIG_MMC_HS200_SUPPORT=y
++CONFIG_MMC_MTK=y
++CONFIG_MEDIATEK_ETH=y
++CONFIG_PHY_AIROHA=y
++CONFIG_PHY_AIROHA_EN8811H=y
++CONFIG_PHY_AIROHA_FW_IN_UBI=y
++CONFIG_PINCTRL=y
++CONFIG_PINCONF=y
++CONFIG_PINCTRL_MT7986=y
++CONFIG_POWER_DOMAIN=y
++CONFIG_MTK_POWER_DOMAIN=y
++CONFIG_DM_REGULATOR=y
++CONFIG_DM_REGULATOR_FIXED=y
++CONFIG_DM_SERIAL=y
++CONFIG_MTK_SERIAL=y
++CONFIG_HEXDUMP=y
++CONFIG_USE_DEFAULT_ENV_FILE=y
++CONFIG_MTD_SPI_NAND=y
++CONFIG_MTK_SPIM=y
++#CONFIG_MTK_SNOR=y
++#CONFIG_DM_SPI_FLASH=y
++#CONFIG_SPI_FLASH_MTD=y
++#CONFIG_SPI_FLASH_WINBOND=y
++# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
++#CONFIG_CMD_SF=y
++CONFIG_CMD_NAND=y
++CONFIG_CMD_NAND_TRIMFFS=y
++CONFIG_LMB_MAX_REGIONS=64
++CONFIG_USE_IPADDR=y
++CONFIG_IPADDR="192.168.1.1"
++CONFIG_USE_SERVERIP=y
+--- /dev/null
++++ b/bananapi_bpi-r3-mini_snand_env
+@@ -0,0 +1,61 @@
++ipaddr=192.168.1.1
++serverip=192.168.1.254
++loadaddr=0x46000000
++console=earlycon=uart8250,mmio32,0x11002000 console=ttyS0
++bootargs=root=ubi.block=0,fit root=/dev/fit0
++bootcmd=if pstore check ; then run boot_recovery ; else run boot_ubi ; fi
++bootconf=config-mt7986a-bananapi-bpi-r3-mini
++bootdelay=0
++bootfile=openwrt-mediatek-filogic-bananapi_bpi-r3-mini-initramfs-recovery.itb
++bootfile_bl2=openwrt-mediatek-filogic-bananapi_bpi-r3-mini-snand-preloader.bin
++bootfile_fip=openwrt-mediatek-filogic-bananapi_bpi-r3-mini-snand-bl31-uboot.fip
++bootfile_upg=openwrt-mediatek-filogic-bananapi_bpi-r3-mini-squashfs-sysupgrade.itb
++bootfile_en8811h_fw=EthMD32.bin
++bootled_pwr=green:status
++bootled_rec=blue:status
++bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60
++bootmenu_default=0
++bootmenu_delay=0
++bootmenu_title=      \e[0;34m( ( ( \e[1;39mOpenWrt\e[0;34m ) ) )  \e[0;36m[SPI-NAND]\e[0m
++bootmenu_0=Initialize environment.=run _firstboot
++bootmenu_0d=Run default boot command.=run boot_default
++bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
++bootmenu_2=Boot production system from NAND.=run boot_production ; run bootmenu_confirm_return
++bootmenu_3=Boot recovery system from NAND.=run boot_recovery ; run bootmenu_confirm_return
++bootmenu_4=Load production system via TFTP then write to NAND.=setenv noboot 1 ; setenv replacevol 1 ; run boot_tftp_production ; setenv noboot ; setenv replacevol ; run bootmenu_confirm_return
++bootmenu_5=Load recovery system via TFTP then write to NAND.=setenv noboot 1 ; setenv replacevol 1 ; run boot_tftp_recovery ; setenv noboot ; setenv replacevol ; run bootmenu_confirm_return
++bootmenu_6=\e[31mLoad Airoha EN8811H firmware via TFTP then write to NAND.\e[0m=run boot_tftp_write_en8811h_fw ; run bootmenu_confirm_return
++bootmenu_7=\e[31mLoad BL31+U-Boot FIP via TFTP then write to NAND.\e[0m=run boot_tftp_write_fip ; run bootmenu_confirm_return
++bootmenu_8=\e[31mLoad BL2 preloader via TFTP then write to NAND.\e[0m=run boot_tftp_write_bl2 ; run bootmenu_confirm_return
++bootmenu_9=Reboot.=reset
++bootmenu_10=Reset all settings to factory defaults.=run reset_factory ; reset
++boot_first=if button reset ; then led $bootled_rec on ; run boot_tftp_recovery ; setenv flag_recover 1 ; run boot_default ; fi ; bootmenu
++boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; setenv replacevol 1 ; run boot_tftp_forever
++boot_production=led $bootled_pwr on ; run ubi_read_production && bootm $loadaddr#$bootconf ; led $bootled_pwr off
++boot_recovery=led $bootled_rec on ; run ubi_read_recovery && bootm $loadaddr#$bootconf ; led $bootled_rec off
++boot_ubi=run boot_production ; run boot_recovery
++boot_tftp_forever=led $bootled_rec on ; while true ; do run boot_tftp_recovery ; sleep 1 ; done
++boot_tftp_production=tftpboot $loadaddr $bootfile_upg && env exists replacevol && iminfo $loadaddr && run ubi_write_production ; if env exists noboot ; then else bootm $loadaddr#$bootconf ; fi
++boot_tftp_recovery=tftpboot $loadaddr $bootfile && env exists replacevol && iminfo $loadaddr && run ubi_write_recovery ; if env exists noboot ; then else bootm $loadaddr#$bootconf ; fi
++boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr#$bootconf
++boot_tftp_write_fip=tftpboot $loadaddr $bootfile_fip && run ubi_write_fip && run reset_factory
++boot_tftp_write_bl2=tftpboot $loadaddr $bootfile_bl2 && run snand_write_bl2
++boot_tftp_write_en8811h_fw=tftpboot $loadaddr $bootfile_en8811h_fw && run ubi_write_en8811h_fw
++part_default=production
++part_recovery=recovery
++reset_factory=mw $loadaddr 0xff 0x1f000 ; ubi write $loadaddr ubootenv 0x1f000 ; ubi write $loadaddr ubootenv2 0x1f000 ; ubi remove rootfs_data
++snand_write_bl2=mtd erase bl2 && mtd write bl2 $loadaddr 0x0 0x40000 && mtd write bl2 $loadaddr 0x40000 0x40000 && mtd write bl2 $loadaddr 0x80000 0x40000 && mtd write bl2 $loadaddr 0xc0000 0x40000
++ubi_create_env=ubi check ubootenv || ubi create ubootenv 0x1f000 dynamic ; ubi check ubootenv2 || ubi create ubootenv2 0x1f000 dynamic
++ubi_prepare_rootfs=if ubi check rootfs_data ; then else if env exists rootfs_data_max ; then ubi create rootfs_data $rootfs_data_max dynamic || ubi create rootfs_data - dynamic ; else ubi create rootfs_data - dynamic ; fi ; fi
++ubi_read_production=ubi read $loadaddr fit && iminfo $loadaddr && run ubi_prepare_rootfs
++ubi_read_recovery=ubi check recovery && ubi read $loadaddr recovery
++ubi_read_emmc_install=ubi check emmc_install && ubi read $loadaddr emmc_install
++ubi_remove_rootfs=ubi check rootfs_data && ubi remove rootfs_data
++ubi_write_fip=ubi check fip && ubi remove fip ; ubi create fip 0x200000 static ; ubi write $loadaddr fip 0x200000
++ubi_write_en8811h_fw=ubi check en8811h-fw && ubi remove en8811h-fw ; ubi create en8811h-fw 0x24000 static ; ubi write $loadaddr en8811h-fw 0x24000
++ubi_write_production=ubi check fit && ubi remove fit ; run ubi_remove_rootfs ; ubi create fit $filesize dynamic && ubi write $loadaddr fit $filesize
++ubi_write_recovery=ubi check recovery && ubi remove recovery ; run ubi_remove_rootfs ; ubi create recovery $filesize dynamic && ubi write $loadaddr recovery $filesize
++_init_env=setenv _init_env ; run ubi_create_env ; saveenv ; saveenv
++_firstboot=setenv _firstboot ; run _switch_to_menu ; run _init_env ; run boot_first
++_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title
++_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title       \e[33m$ver\e[0m"
+--- /dev/null
++++ b/bananapi_bpi-r3-mini_emmc_env
+@@ -0,0 +1,59 @@
++ipaddr=192.168.1.1
++serverip=192.168.1.254
++loadaddr=0x46000000
++console=earlycon=uart8250,mmio32,0x11002000 console=ttyS0
++bootargs=root=/dev/fit0
++bootcmd=if pstore check ; then run boot_recovery ; else run boot_emmc ; fi
++bootconf=config-mt7986a-bananapi-bpi-r3-mini
++bootdelay=0
++bootfile=openwrt-mediatek-filogic-bananapi_bpi-r3-mini-initramfs-recovery.itb
++bootfile_bl2=openwrt-mediatek-filogic-bananapi_bpi-r3-mini-emmc-preloader.bin
++bootfile_fip=openwrt-mediatek-filogic-bananapi_bpi-r3-mini-emmc-bl31-uboot.fip
++bootfile_upg=openwrt-mediatek-filogic-bananapi_bpi-r3-mini-squashfs-sysupgrade.itb
++bootfile_en8811h_fw=EthMD32.bin
++bootled_pwr=green:status
++bootled_rec=blue:status
++bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60
++bootmenu_default=0
++bootmenu_delay=0
++bootmenu_title=      \e[0;34m( ( ( \e[1;39mOpenWrt\e[0;34m ) ) )  \e[0;36m[eMMC]\e[0m
++bootmenu_0=Initialize environment.=run _firstboot
++bootmenu_0d=Run default boot command.=run boot_default
++bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
++bootmenu_2=Boot production system from eMMC.=run boot_production ; run bootmenu_confirm_return
++bootmenu_3=Boot recovery system from eMMC.=run boot_recovery ; run bootmenu_confirm_return
++bootmenu_4=Load production system via TFTP then write to eMMC.=setenv noboot 1 ; setenv replacevol 1 ; run boot_tftp_production ; setenv noboot ; setenv replacevol ; run bootmenu_confirm_return
++bootmenu_5=Load recovery system via TFTP then write to eMMC.=setenv noboot 1 ; setenv replacevol 1 ; run boot_tftp_recovery ; setenv noboot ; setenv replacevol ; run bootmenu_confirm_return
++bootmenu_6=\e[31mLoad Airoha EN8811H firmware via TFTP then write to NAND.\e[0m=run boot_tftp_write_en8811h_fw ; run bootmenu_confirm_return
++bootmenu_7=\e[31mLoad BL31+U-Boot FIP via TFTP then write to eMMC.\e[0m=run boot_tftp_write_fip ; run bootmenu_confirm_return
++bootmenu_8=\e[31mLoad BL2 preloader via TFTP then write to eMMC.\e[0m=run boot_tftp_write_bl2 ; run bootmenu_confirm_return
++bootmenu_9=Reboot.=reset
++bootmenu_10=Reset all settings to factory defaults.=run reset_factory ; reset
++boot_first=if button reset ; then led $bootled_rec on ; run boot_tftp_recovery ; setenv flag_recover 1 ; run boot_default ; fi ; bootmenu
++boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; setenv replacevol 1 ; run boot_tftp_forever
++boot_production=led $bootled_pwr on ; run emmc_read_production && bootm $loadaddr#$bootconf ; led $bootled_pwr off
++boot_recovery=led $bootled_rec on ; run emmc_read_recovery && bootm $loadaddr#$bootconf ; led $bootled_rec off
++boot_emmc=run boot_production ; run boot_recovery
++boot_tftp_forever=led $bootled_rec on ; while true ; do run boot_tftp_recovery ; sleep 1 ; done
++boot_tftp_production=tftpboot $loadaddr $bootfile_upg && env exists replacevol && iminfo $loadaddr && run emmc_write_production ; if env exists noboot ; then else bootm $loadaddr#$bootconf ; fi
++boot_tftp_recovery=tftpboot $loadaddr $bootfile && env exists replacevol && iminfo $loadaddr && run emmc_write_recovery ; if env exists noboot ; then else bootm $loadaddr#$bootconf ; fi
++boot_tftp_write_fip=tftpboot $loadaddr $bootfile_fip && run emmc_write_fip
++boot_tftp_write_bl2=tftpboot $loadaddr $bootfile_bl2 && run emmc_write_bl2
++boot_tftp_write_en8811h_fw=tftpboot $loadaddr $bootfile_en8811h_fw && run emmc_write_en8811h_fw
++boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr#$bootconf
++mmc_write_vol=imszb $loadaddr image_size && test 0x$image_size -le 0x$part_size && mmc erase 0x$part_addr 0x$image_size && mmc write $loadaddr 0x$part_addr 0x$image_size
++mmc_read_vol=mmc read $loadaddr $part_addr 0x100 && imszb $loadaddr image_size && test 0x$image_size -le 0x$part_size && mmc read $loadaddr 0x$part_addr 0x$image_size && setexpr filesize $image_size * 0x200
++part_default=production
++part_recovery=recovery
++reset_factory=eraseenv && reset
++emmc_read_production=part start mmc 0 $part_default part_addr && part size mmc 0 $part_default part_size && run mmc_read_vol
++emmc_read_recovery=part start mmc 0 $part_recovery part_addr && part size mmc 0 $part_recovery part_size && run mmc_read_vol
++emmc_write_en8811h_fw=mmc partconf 0 1 2 2 && mmc erase 0x0 0x120 && mmc write $fileaddr 0x0 0x120 ; mmc partconf 0 1 1 0
++emmc_write_bl2=mmc partconf 0 1 1 1 && mmc erase 0x0 0x400 && mmc write $fileaddr 0x0 0x400 ; mmc partconf 0 1 1 0
++emmc_write_fip=mmc erase 0x3400 0x2000 && mmc write $fileaddr 0x3400 0x2000 && mmc erase 0x2000 0x800
++emmc_write_production=part start mmc 0 $part_default part_addr && part size mmc 0 $part_default part_size && run mmc_write_vol
++emmc_write_recovery=part start mmc 0 $part_recovery part_addr && part size mmc 0 $part_recovery part_size && run mmc_write_vol
++_init_env=setenv _init_env ; setenv _create_env ; saveenv ; saveenv
++_firstboot=setenv _firstboot ; run _switch_to_menu ; run _init_env ; run boot_first
++_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title
++_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title       \e[33m$ver\e[0m"
+--- /dev/null
++++ b/arch/arm/dts/mt7986a-bpi-r3-mini.dts
+@@ -0,0 +1,238 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++
++/dts-v1/;
++#include "mt7986.dtsi"
++#include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/input/linux-event-codes.h>
++
++/ {
++      #address-cells = <1>;
++      #size-cells = <1>;
++      model = "Bananapi BPi-R3 Mini";
++      compatible = "mediatek,mt7986", "mediatek,mt7986-rfb";
++
++      chosen {
++              stdout-path = &uart0;
++              tick-timer = &timer0;
++      };
++
++      memory@40000000 {
++              device_type = "memory";
++              reg = <0x40000000 0x80000000>;
++      };
++
++      gpio-keys {
++              compatible = "gpio-keys";
++
++              button-reset {
++                      label = "reset";
++                      linux,code = <KEY_RESTART>;
++                      gpios = <&gpio 7 GPIO_ACTIVE_LOW>;
++              };
++      };
++
++      leds {
++              compatible = "gpio-leds";
++
++              status_led: led-0 {
++                      label = "green:status";
++                      gpios = <&gpio 19 GPIO_ACTIVE_HIGH>;
++              };
++
++              led-1 {
++                      label = "blue:wlan2g";
++                      gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
++              };
++
++              led-2 {
++                      label = "blue:wlan5g";
++                      gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
++              };
++      };
++
++      reg_1p8v: regulator-1p8v {
++              compatible = "regulator-fixed";
++              regulator-name = "fixed-1.8V";
++              regulator-min-microvolt = <1800000>;
++              regulator-max-microvolt = <1800000>;
++              regulator-boot-on;
++              regulator-always-on;
++      };
++
++      reg_3p3v: regulator-3p3v {
++              compatible = "regulator-fixed";
++              regulator-name = "fixed-3.3V";
++              regulator-min-microvolt = <3300000>;
++              regulator-max-microvolt = <3300000>;
++              regulator-boot-on;
++              regulator-always-on;
++      };
++};
++
++&eth {
++      status = "okay";
++      pinctrl-names = "default";
++      pinctrl-0 = <&mdio_pins>;
++
++      mediatek,gmac-id = <0>;
++      phy-mode = "2500base-x";
++      phy-handle = <&phy14>;
++
++      phy14: eth-phy@e {
++              compatible = "ethernet-phy-id03a2.a411";
++              reg = <14>;
++
++              airoha,rx-pol-reverse;
++
++              reset-gpios = <&gpio 49 GPIO_ACTIVE_LOW>;
++              reset-assert-us = <10000>;
++              reset-deassert-us = <20000>;
++      };
++};
++
++&mmc0 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&mmc0_pins_default>;
++      bus-width = <8>;
++      max-frequency = <200000000>;
++      cap-mmc-highspeed;
++      cap-mmc-hw-reset;
++      vmmc-supply = <&reg_3p3v>;
++      vqmmc-supply = <&reg_1p8v>;
++      non-removable;
++      status = "okay";
++};
++
++&pinctrl {
++      mdio_pins: mdio-pins {
++              mux {
++                      function = "eth";
++                      groups = "mdc_mdio";
++              };
++
++              conf-en8811-pwr-a {
++                      pins = "GPIO_11";
++                      drive-strength = <MTK_DRIVE_8mA>;
++                      bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
++                      output-low;
++              };
++
++              conf-en8811-pwr-b {
++                      pins = "GPIO_12";
++                      drive-strength = <MTK_DRIVE_8mA>;
++                      bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
++                      output-low;
++              };
++      };
++
++      mmc0_pins_default: mmc0default {
++              mux {
++                      function = "flash";
++                      groups =  "emmc_51";
++              };
++
++              conf-cmd-dat {
++                      pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2",
++                             "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5",
++                             "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD";
++                      input-enable;
++                      drive-strength = <MTK_DRIVE_4mA>;
++                      bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
++              };
++
++              conf-clk {
++                      pins = "EMMC_CK";
++                      drive-strength = <MTK_DRIVE_6mA>;
++                      bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
++              };
++
++              conf-dsl {
++                      pins = "EMMC_DSL";
++                      bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
++              };
++
++              conf-rst {
++                      pins = "EMMC_RSTB";
++                      drive-strength = <MTK_DRIVE_4mA>;
++                      bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
++              };
++      };
++
++      spi_flash_pins: spi0-pins-func-1 {
++              mux {
++                      function = "flash";
++                      groups = "spi0", "spi0_wp_hold";
++              };
++
++              conf-pu {
++                      pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP";
++                      drive-strength = <MTK_DRIVE_8mA>;
++                      bias-pull-up = <MTK_PUPD_SET_R1R0_11>;
++              };
++
++              conf-pd {
++                      pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO";
++                      drive-strength = <MTK_DRIVE_8mA>;
++                      bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
++              };
++      };
++
++      pwm_pins: pwm0-pins-func-1 {
++              mux {
++                      function = "pwm";
++                      groups = "pwm0";
++              };
++      };
++};
++
++&pwm {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pwm_pins>;
++      status = "okay";
++};
++
++&spi0 {
++      #address-cells = <1>;
++      #size-cells = <0>;
++      pinctrl-names = "default";
++      pinctrl-0 = <&spi_flash_pins>;
++      status = "okay";
++      must_tx;
++      enhance_timing;
++      dma_ext;
++      ipm_design;
++      support_quad;
++      tick_dly = <1>;
++      sample_sel = <0>;
++
++      spi_nand@0 {
++              compatible = "spi-nand";
++              reg = <0>;
++              spi-max-frequency = <20000000>;
++
++              partitions {
++                      compatible = "fixed-partitions";
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++
++                      partition@0 {
++                              label = "bl2";
++                              reg = <0x0 0x80000>;
++                      };
++
++                      partition@80000 {
++                              label = "ubi";
++                              reg = <0x80000 0x7f80000>;
++                      };
++              };
++      };
++
++};
++
++&uart0 {
++      status = "okay";
++};
++
++&watchdog {
++      status = "disabled";
++};