]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
net: phy: Add airoha AN8801 ethernet phy driver
authorJulien Stephan <jstephan@baylibre.com>
Wed, 29 Apr 2026 13:58:59 +0000 (15:58 +0200)
committerDavid Lechner <dlechner@baylibre.com>
Wed, 10 Jun 2026 20:25:21 +0000 (15:25 -0500)
Add Airoha AN8801 Ethernet PHY driver (air_an8801.c).
Implement CL22/CL45 MDIO access, LED control, and RGMII delay
configuration. Provide probe, initialization, LED setup, and status
handling. Expose DTS properties for clock delays. Register driver with
PHY framework and trigger on startup.

Signed-off-by: Yanqing Wang <ot_yanqing.wang@mediatek.com>
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
Reviewed-by: Kevin-KW Huang <kevin-kw.huang@airoha.com>
Link: https://patch.msgid.link/20260429-add-ethernet-support-for-genio-520-720-v4-6-be54e17239b7@baylibre.com
Signed-off-by: David Lechner <dlechner@baylibre.com>
MAINTAINERS
drivers/net/phy/airoha/Kconfig
drivers/net/phy/airoha/Makefile
drivers/net/phy/airoha/air_an8801.c [new file with mode: 0644]

index dcaf1e08354a2e3c00f9dc64c37d9896ecda8b70..474b2af11bdd85b7da11da6b41163dd7bdb7e31f 100644 (file)
@@ -60,6 +60,7 @@ F:    lib/acpi/
 
 AIROHA PHY
 M:     Tommy Shih <tommy.shih@airoha.com>
+M:     Kevin-KW Huang <kevin-kw.huang@airoha.com>
 S:     Maintained
 F:     drivers/net/phy/airoha/
 
index b48426bf0fa18bf1021596300e5ad0d0218dd6c5..2d58d674200fd4e12060c87130b4dd7fd3184424 100644 (file)
@@ -2,6 +2,13 @@
 menuconfig PHY_AIROHA
        bool "Airoha Ethernet PHYs support"
 
+config PHY_AIROHA_AN8801
+       bool "Airoha Ethernet AN8801 support"
+       depends on PHY_AIROHA
+       select PHY_AIROHA_PHYLIB
+       help
+         Currently support AIROHA AN8801 1G PHY.
+
 config PHY_AIROHA_EN8811
        bool "Airoha Ethernet EN8811H support"
        depends on PHY_AIROHA
index 59051caecefb88752a854830cec3e04221d18cbd..25e44004cfd69614adb1a8ca83f3c401b27c0767 100644 (file)
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
+obj-$(CONFIG_PHY_AIROHA_AN8801) += air_an8801.o
 obj-$(CONFIG_PHY_AIROHA_EN8811) += air_en8811.o
 obj-$(CONFIG_PHY_AIROHA_PHYLIB) += air_phy_lib.o
diff --git a/drivers/net/phy/airoha/air_an8801.c b/drivers/net/phy/airoha/air_an8801.c
new file mode 100644 (file)
index 0000000..9d9958f
--- /dev/null
@@ -0,0 +1,594 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * air_an8801.c - PHY driver for Airoha AN8801.
+ * Copyright (c) 2026 Airoha Technology Corp.
+ * Copyright (C) 2026 BayLibre, SAS.
+ * Author: Kevin-KW Huang <kevin-kw.huang@airoha.com>
+ *         Sita Huang <sita.huang@airoha.com>
+ *         Julien Stephan <jstephan@baylibre.com>
+ */
+
+#include <malloc.h>
+#include <phy.h>
+#include <dm/device_compat.h>
+
+#include "air_phy_lib.h"
+
+#define AN8801R_PHY_ID1                        0xc0ff
+#define AN8801R_PHY_ID2                        0x0421
+#define AN8801R_PHY_ID                 ((u32)((AN8801R_PHY_ID1 << 16) | AN8801R_PHY_ID2))
+
+#define AN8801R_MAX_LED_SIZE           3
+
+/* MII Registers - Airoha Page 4 */
+#define AN8801_PBUS_ACCESS             BIT(28)
+
+/* BPBUS Registers */
+#define AN8801_BPBUS_REG_LED_GPIO      0x54
+#define AN8801_BPBUS_REG_LED_ID_SEL    0x58
+#define  LED_ID_GPIO_SEL(led, gpio)    ((led) << ((gpio) * 3))
+
+#define AN8801_BPBUS_REG_GPIO_MODE     0x70
+
+#define AN8801_BPBUS_REG_LINK_MODE     0x5054
+#define  AN8801_BPBUS_LINK_MODE_1000   BIT(0)
+
+#define AN8801_BPBUS_REG_BYPASS_PTP    0x21c004
+#define   AN8801_BYP_PTP_RGMII_TO_GPHY BIT(0)
+
+#define AN8801_BPBUS_REG_TXDLY_STEP    0x21c024
+#define  RGMII_DELAY_STEP_MASK         GENMASK(2, 0)
+#define  AIR_RGMII_DELAY_NOSTEP                0
+#define  AIR_RGMII_DELAY_STEP_1                1
+#define  AIR_RGMII_DELAY_STEP_2                2
+#define  AIR_RGMII_DELAY_STEP_3                3
+#define  AIR_RGMII_DELAY_STEP_4                4
+#define  AIR_RGMII_DELAY_STEP_5                5
+#define  AIR_RGMII_DELAY_STEP_6                6
+#define  AIR_RGMII_DELAY_STEP_7                7
+#define  RGMII_TXDELAY_FORCE_MODE      BIT(24)
+
+#define AN8801_BPBUS_REG_RXDLY_STEP    0x21c02c
+#define  RGMII_RXDELAY_ALIGN           BIT(4)
+#define  RGMII_RXDELAY_FORCE_MODE      BIT(24)
+
+#define AN8801_BPBUS_REG_EFIFO_CTL(x)  (0x270004 + (0x100 * (x))) /* 0..2 */
+#define   AN8801_EFIFO_ALL_EN          GENMASK(7, 0)
+#define   AN8801_EFIFO_RX_EN           BIT(0)
+#define   AN8801_EFIFO_TX_EN           BIT(1)
+#define   AN8801_EFIFO_RX_CLK_EN       BIT(2)
+#define   AN8801_EFIFO_TX_CLK_EN       BIT(3)
+#define   AN8801_EFIFO_RX_EEE_EN       BIT(4)
+#define   AN8801_EFIFO_TX_EEE_EN       BIT(5)
+#define   AN8801_EFIFO_RX_ODD_NIBBLE_EN        BIT(6)
+#define   AN8801_EFIFO_TX_ODD_NIBBLE_EN        BIT(7)
+
+#define AN8801_BPBUS_REG_HWRST_DE_GLITCH       0xc8
+#define  AN8801_DE_GLITCH_EN                   BIT(2)
+#define  AN8801_11_CYCLE_XTAL_PERIOD_DE_GLITCH GENMASK(1, 0)
+
+#define LED_BCR                                0x21
+#define  LED_BCR_MODE_MASK             GENMASK(1, 0)
+#define  LED_BCR_TIME_TEST             BIT(2)
+#define  LED_BCR_CLK_EN                        BIT(3)
+#define  LED_BCR_EVT_ALL               BIT(4)
+#define  LED_BCR_EXT_CTRL              BIT(15)
+#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                     0x22
+#define LED_ON_DUR_MASK                        GENMASK(15, 0)
+
+#define LED_BLINK_DUR                  0x23
+#define LED_BLINK_DUR_MASK             GENMASK(15, 0)
+
+#define LED_ON_CTRL(i)                 (0x024 + ((i) * 2))
+#define  LED_ON_EVT_MASK               GENMASK(6, 0)
+#define  LED_ON_EVT_LINK_1000M         BIT(0)
+#define  LED_ON_EVT_LINK_100M          BIT(1)
+#define  LED_ON_EVT_LINK_10M           BIT(2)
+#define  LED_ON_EVT_LINK_DN            BIT(3)
+#define  LED_ON_EVT_FDX                        BIT(4)
+#define  LED_ON_EVT_HDX                        BIT(5)
+#define  LED_ON_EVT_FORCE              BIT(6)
+#define  LED_ON_POL                    BIT(14)
+#define  LED_ON_EN                     BIT(15)
+
+#define LED_BLINK_CTRL(i)              (0x025 + ((i) * 2))
+#define  LED_BLINK_EVT_MASK            GENMASK(9, 0)
+#define  LED_BLINK_EVT_1000M_TX                BIT(0)
+#define  LED_BLINK_EVT_1000M_RX                BIT(1)
+#define  LED_BLINK_EVT_100M_TX         BIT(2)
+#define  LED_BLINK_EVT_100M_RX         BIT(3)
+#define  LED_BLINK_EVT_10M_TX          BIT(4)
+#define  LED_BLINK_EVT_10M_RX          BIT(5)
+#define  LED_BLINK_EVT_FORCE           BIT(9)
+
+#define UNIT_LED_BLINK_DURATION                780
+#define LED_BLINK_DURATION(f)          (UNIT_LED_BLINK_DURATION << (f))
+
+/* Link on(1G/100M/10M), no activity */
+#define AIR_LED0_ON \
+       (LED_ON_EVT_LINK_1000M | LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M)
+#define AIR_LED0_BLINK                 0x0
+/* No link on, activity(1G/100M/10M TX/RX) */
+#define AIR_LED1_ON                    0x0
+#define AIR_LED1_BLINK \
+       (LED_BLINK_EVT_1000M_TX | LED_BLINK_EVT_1000M_RX | \
+       LED_BLINK_EVT_100M_TX | LED_BLINK_EVT_100M_RX | \
+       LED_BLINK_EVT_10M_TX | LED_BLINK_EVT_10M_RX)
+/* Link on(100M/10M), activity(100M/10M TX/RX) */
+#define AIR_LED2_ON \
+       (LED_ON_EVT_LINK_100M | LED_ON_EVT_LINK_10M)
+#define AIR_LED2_BLINK \
+       (LED_BLINK_EVT_100M_TX | LED_BLINK_EVT_100M_RX | \
+       LED_BLINK_EVT_10M_TX | LED_BLINK_EVT_10M_RX)
+
+#define INVALID_DATA                   GENMASK(31, 0)
+
+#define AN8801_REG_PHY_INTERNAL0       0x600
+#define AN8801_REG_PHY_INTERNAL1       0x601
+
+#define AN8801_LED_ENABLE              1
+
+enum air_led_gpio_pin {
+       AIR_LED_GPIO1 = 1,
+       AIR_LED_GPIO2,
+       AIR_LED_GPIO3
+};
+
+enum air_led {
+       AIR_LED0 = 0,
+       AIR_LED1,
+       AIR_LED2,
+       AIR_LED3
+};
+
+enum air_led_blink_dut {
+       AIR_LED_BLINK_DUR_32M = 0,
+       AIR_LED_BLINK_DUR_64M,
+       AIR_LED_BLINK_DUR_128M,
+       AIR_LED_BLINK_DUR_256M,
+       AIR_LED_BLINK_DUR_512M,
+       AIR_LED_BLINK_DUR_1024M,
+       AIR_LED_BLINK_DUR_LAST
+};
+
+enum air_led_polarity {
+       AIR_ACTIVE_LOW = 0,
+       AIR_ACTIVE_HIGH,
+};
+
+enum air_led_mode {
+       AIR_LED_MODE_DISABLE = 0,
+       AIR_LED_MODE_USER_DEFINE,
+       AIR_LED_MODE_LAST
+};
+
+struct air_led_cfg {
+       u16 led_en;
+       u16 gpio;
+       u16 led_polarity;
+       u16 led_on_cfg;
+       u16 led_blk_cfg;
+};
+
+struct an8801r_priv {
+       struct air_led_cfg      led_cfg[AN8801R_MAX_LED_SIZE];
+       u32                     led_blink_cfg;
+       u8                      rxdelay_force;
+       u8                      txdelay_force;
+       u16                     rxdelay_step;
+       u8                      rxdelay_align;
+       u16                     txdelay_step;
+};
+
+#define phydev_cfg(phy)                ((struct an8801r_priv *)(phy)->priv)
+
+/*
+ *     GPIO1 <-> LED0,
+ *     GPIO2 <-> LED1,
+ *     GPIO3 <-> LED2,
+ */
+static const struct an8801r_priv an8801r_priv_defaults = {
+       .led_cfg = {
+               /* LED Enable, GPIO, LED Polarity, LED ON, LED Blink */
+               {AN8801_LED_ENABLE, AIR_LED_GPIO1, AIR_ACTIVE_LOW,  AIR_LED0_ON, AIR_LED0_BLINK},
+               {AN8801_LED_ENABLE, AIR_LED_GPIO2, AIR_ACTIVE_HIGH, AIR_LED1_ON, AIR_LED1_BLINK},
+               {AN8801_LED_ENABLE, AIR_LED_GPIO3, AIR_ACTIVE_HIGH, AIR_LED2_ON, AIR_LED2_BLINK},
+       },
+       .led_blink_cfg = AIR_LED_BLINK_DUR_64M,
+       .rxdelay_force = false,
+       .txdelay_force = false,
+       .rxdelay_step = AIR_RGMII_DELAY_NOSTEP,
+       .rxdelay_align = false,
+       .txdelay_step = AIR_RGMII_DELAY_NOSTEP,
+};
+
+static int an8801_buckpbus_reg_rmw(struct phy_device *phydev,
+                                  u32 addr, u32 mask, u32 set)
+{
+       return air_phy_buckpbus_reg_modify(phydev,
+                                         addr | AN8801_PBUS_ACCESS,
+                                         mask, set);
+}
+
+static int an8801_buckpbus_reg_set_bits(struct phy_device *phydev,
+                                       u32 addr, u32 mask)
+{
+       return air_phy_buckpbus_reg_modify(phydev,
+                                         addr | AN8801_PBUS_ACCESS,
+                                         mask, mask);
+}
+
+static int an8801_buckpbus_reg_clear_bits(struct phy_device *phydev,
+                                         u32 addr, u32 mask)
+{
+       return air_phy_buckpbus_reg_modify(phydev,
+                                         addr | AN8801_PBUS_ACCESS,
+                                         mask, 0);
+}
+
+static int an8801_buckpbus_reg_write(struct phy_device *phydev, u32 addr, u32 data)
+{
+       return air_phy_buckpbus_reg_write(phydev, addr | AN8801_PBUS_ACCESS, data);
+}
+
+static int an8801r_led_set_usr_def(struct phy_device *phydev, u8 entity,
+                                  u16 polar, u16 on_evt, u16 blk_evt)
+{
+       int ret;
+
+       if (polar == AIR_ACTIVE_HIGH)
+               on_evt |= LED_ON_POL;
+       else
+               on_evt &= ~LED_ON_POL;
+
+       on_evt |= LED_ON_EN;
+
+       ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(entity), on_evt);
+       if (ret)
+               return ret;
+
+       return phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(entity), blk_evt);
+}
+
+static int an8801r_led_set_blink(struct phy_device *phydev, u16 blink)
+{
+       int ret;
+
+       ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_DUR,
+                           LED_BLINK_DURATION(blink));
+       if (ret)
+               return ret;
+
+       return phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_ON_DUR,
+                            LED_BLINK_DURATION(blink) / 2);
+}
+
+static int an8801r_led_set_mode(struct phy_device *phydev, u8 mode)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_BCR);
+       if (ret < 0)
+               return ret;
+
+       switch (mode) {
+       case AIR_LED_MODE_DISABLE:
+               ret &= ~LED_BCR_EXT_CTRL;
+               ret &= ~LED_BCR_MODE_MASK;
+               ret |= LED_BCR_MODE_DISABLE;
+               break;
+       case AIR_LED_MODE_USER_DEFINE:
+               ret |= LED_BCR_EXT_CTRL | LED_BCR_CLK_EN;
+               break;
+       }
+       return phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_BCR, ret);
+}
+
+static int an8801r_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(entity));
+       if (ret < 0)
+               return ret;
+
+       if (state)
+               ret |= LED_ON_EN;
+       else
+               ret &= ~LED_ON_EN;
+
+       return phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(entity), ret);
+}
+
+static int an8801r_led_init(struct phy_device *phydev)
+{
+       struct an8801r_priv *priv = phydev_cfg(phydev);
+       struct air_led_cfg *led_cfg = priv->led_cfg;
+       u16 led_blink_cfg = priv->led_blink_cfg;
+       int ret, led_id;
+
+       ret = an8801r_led_set_blink(phydev, led_blink_cfg);
+       if (ret)
+               return ret;
+
+       ret = an8801r_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
+       if (ret) {
+               dev_err(phydev->dev, "AN8801R: Fail to set LED mode, ret %d!\n", ret);
+               return ret;
+       }
+
+       for (led_id = AIR_LED0; led_id < AN8801R_MAX_LED_SIZE; led_id++) {
+               ret = an8801r_led_set_state(phydev, led_id, led_cfg[led_id].led_en);
+               if (ret) {
+                       dev_err(phydev->dev, "AN8801R: Fail to set LED%d state, ret %d!\n",
+                               led_id, ret);
+                       return ret;
+               }
+
+               if (!led_cfg[led_id].led_en)
+                       continue;
+
+               ret = an8801_buckpbus_reg_set_bits(phydev, AN8801_BPBUS_REG_LED_GPIO,
+                                                  BIT(led_cfg[led_id].gpio));
+               if (ret)
+                       return ret;
+
+               ret = an8801_buckpbus_reg_set_bits(phydev, AN8801_BPBUS_REG_LED_ID_SEL,
+                                                  LED_ID_GPIO_SEL(led_id,
+                                                                  led_cfg[led_id].gpio));
+               if (ret)
+                       return ret;
+
+               ret = an8801_buckpbus_reg_clear_bits(phydev, AN8801_BPBUS_REG_GPIO_MODE,
+                                                    BIT(led_cfg[led_id].gpio));
+               if (ret)
+                       return ret;
+
+               ret = an8801r_led_set_usr_def(phydev, led_id,
+                                             led_cfg[led_id].led_polarity,
+                                             led_cfg[led_id].led_on_cfg,
+                                             led_cfg[led_id].led_blk_cfg);
+               if (ret) {
+                       dev_err(phydev->dev, "AN8801R: Fail to set LED%d, ret %d!\n",
+                               led_id, ret);
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+static int an8801r_of_init(struct phy_device *phydev)
+{
+       struct an8801r_priv *priv = phydev_cfg(phydev);
+       ofnode node = phy_get_ofnode(phydev);
+       u32 val = 0;
+       int ret;
+
+       if (!ofnode_valid(node))
+               return -EINVAL;
+
+       if (ofnode_has_property(node, "airoha,rxclk-delay")) {
+               ret = ofnode_read_u32(node, "airoha,rxclk-delay", &val);
+               if (ret) {
+                       dev_err(phydev->dev, "airoha,rxclk-delay value is invalid.\n");
+                       return ret;
+               }
+               if (val > AIR_RGMII_DELAY_STEP_7) {
+                       dev_err(phydev->dev, "airoha,rxclk-delay value %u out of range.\n", val);
+                       return -EINVAL;
+               }
+               priv->rxdelay_force = true;
+               priv->rxdelay_step = val;
+               priv->rxdelay_align = ofnode_read_bool(node,
+                                                      "airoha,rxclk-delay-align");
+       }
+
+       if (ofnode_has_property(node, "airoha,txclk-delay")) {
+               ret = ofnode_read_u32(node, "airoha,txclk-delay", &val);
+               if (ret) {
+                       dev_err(phydev->dev, "airoha,txclk-delay value is invalid.\n");
+                       return ret;
+               }
+               if (val > AIR_RGMII_DELAY_STEP_7) {
+                       dev_err(phydev->dev, "airoha,txclk-delay value %u out of range.\n", val);
+                       return -EINVAL;
+               }
+               priv->txdelay_force = true;
+               priv->txdelay_step = val;
+       }
+       return 0;
+}
+
+static int an8801r_rgmii_rxdelay(struct phy_device *phydev, u16 delay, u8 align)
+{
+       u32 reg_val = delay & RGMII_DELAY_STEP_MASK;
+       int ret;
+
+       if (align) {
+               reg_val |= RGMII_RXDELAY_ALIGN;
+               debug("AN8801R: Rxdelay align\n");
+       }
+       reg_val |= RGMII_RXDELAY_FORCE_MODE;
+       ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_RXDLY_STEP, reg_val);
+       if (ret)
+               return ret;
+
+       debug("AN8801R: Force rxdelay = %d(0x%x)\n", delay, reg_val);
+       return 0;
+}
+
+static int an8801r_rgmii_txdelay(struct phy_device *phydev, u16 delay)
+{
+       u32 reg_val = delay & RGMII_DELAY_STEP_MASK;
+       int ret;
+
+       reg_val |= RGMII_TXDELAY_FORCE_MODE;
+       ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_TXDLY_STEP, reg_val);
+       if (ret)
+               return ret;
+
+       debug("AN8801R: Force txdelay = %d(0x%x)\n", delay, reg_val);
+       return 0;
+}
+
+static int an8801r_rgmii_delay_config(struct phy_device *phydev)
+{
+       struct an8801r_priv *priv = phydev_cfg(phydev);
+       int ret;
+
+       switch (phydev->interface) {
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               return an8801r_rgmii_txdelay(phydev, AIR_RGMII_DELAY_STEP_4);
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+               return an8801r_rgmii_rxdelay(phydev, AIR_RGMII_DELAY_NOSTEP, true);
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               ret = an8801r_rgmii_txdelay(phydev, AIR_RGMII_DELAY_STEP_4);
+               if (ret)
+                       return ret;
+               return an8801r_rgmii_rxdelay(phydev, AIR_RGMII_DELAY_NOSTEP, true);
+       case PHY_INTERFACE_MODE_RGMII:
+       default:
+               if (priv->rxdelay_force) {
+                       ret = an8801r_rgmii_rxdelay(phydev, priv->rxdelay_step,
+                                                   priv->rxdelay_align);
+                       if (ret)
+                               return ret;
+               }
+               if (priv->txdelay_force)
+                       return an8801r_rgmii_txdelay(phydev, priv->txdelay_step);
+               return 0;
+       }
+}
+
+static int an8801r_config_init(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = an8801r_of_init(phydev);
+       if (ret < 0)
+               return ret;
+
+       ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_HWRST_DE_GLITCH,
+                                       AN8801_DE_GLITCH_EN |
+                                       AN8801_11_CYCLE_XTAL_PERIOD_DE_GLITCH);
+       if (ret)
+               return ret;
+
+       ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL0, 0x1e);
+       if (ret)
+               return ret;
+
+       ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL1, 0x02);
+       if (ret)
+               return ret;
+
+       ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
+       if (ret)
+               return ret;
+
+       ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_BYPASS_PTP,
+                                       AN8801_BYP_PTP_RGMII_TO_GPHY);
+       if (ret)
+               return ret;
+
+       ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(0),
+                                       AN8801_EFIFO_RX_EN |
+                                       AN8801_EFIFO_TX_EN |
+                                       AN8801_EFIFO_RX_CLK_EN |
+                                       AN8801_EFIFO_TX_CLK_EN |
+                                       AN8801_EFIFO_RX_EEE_EN |
+                                       AN8801_EFIFO_TX_EEE_EN);
+       if (ret)
+               return ret;
+
+       ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(1),
+                                       AN8801_EFIFO_ALL_EN);
+       if (ret)
+               return ret;
+
+       ret = an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(2),
+                                       AN8801_EFIFO_ALL_EN);
+       if (ret)
+               return ret;
+
+       ret = an8801r_rgmii_delay_config(phydev);
+       if (ret)
+               return ret;
+
+       ret = an8801r_led_init(phydev);
+       if (ret) {
+               dev_err(phydev->dev, "AN8801R: LED initialize fail, ret %d!\n", ret);
+               return ret;
+       }
+       return 0;
+}
+
+static int an8801r_phy_probe(struct phy_device *phydev)
+{
+       struct an8801r_priv *priv;
+       u32 phy_id;
+       int ret;
+
+       ret = get_phy_id(phydev->bus, phydev->addr, MDIO_DEVAD_NONE, &phy_id);
+       if (ret)
+               return ret;
+
+       if (phy_id != AN8801R_PHY_ID) {
+               dev_err(phydev->dev,
+                       "AN8801R can't be detected (id=0x%08x).\n", phy_id);
+               return -ENODEV;
+       }
+
+       priv = malloc(sizeof(*priv));
+       if (!priv)
+               return -ENOMEM;
+
+       *priv = an8801r_priv_defaults;
+
+       phydev->priv = priv;
+
+       return 0;
+}
+
+static int an8801r_read_status(struct phy_device *phydev)
+{
+       u32 data;
+
+       if (!phydev->link)
+               return 0;
+
+       debug("AN8801R: SPEED %d\n", phydev->speed);
+       data = phydev->speed == SPEED_1000 ? AN8801_BPBUS_LINK_MODE_1000 : 0;
+
+       return an8801_buckpbus_reg_rmw(phydev, AN8801_BPBUS_REG_LINK_MODE,
+                                      AN8801_BPBUS_LINK_MODE_1000, data);
+}
+
+static int an8801r_startup(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = genphy_startup(phydev);
+       if (ret)
+               return ret;
+
+       return an8801r_read_status(phydev);
+}
+
+U_BOOT_PHY_DRIVER(an8801r) = {
+       .name = "Airoha AN8801R",
+       .uid = AN8801R_PHY_ID,
+       .mask = 0x0ffffff0,
+       .features = PHY_GBIT_FEATURES,
+       .probe = &an8801r_phy_probe,
+       .config = &an8801r_config_init,
+       .read_page = &air_phy_read_page,
+       .write_page = &air_phy_write_page,
+       .startup = &an8801r_startup,
+       .shutdown = &genphy_shutdown,
+};