[PATCH 6/9] net: phy: Add airoha AN8801 ethernet phy driver
Julien Stephan
jstephan at baylibre.com
Thu Apr 23 15:25:57 CEST 2026
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. Improve robustness and error
reporting across platforms now.
Signed-off-by: Yanqing Wang <ot_yanqing.wang at mediatek.com>
Signed-off-by: Julien Stephan <jstephan at baylibre.com>
---
MAINTAINERS | 1 +
drivers/net/phy/airoha/Kconfig | 7 +
drivers/net/phy/airoha/Makefile | 1 +
drivers/net/phy/airoha/air_an8801.c | 614 ++++++++++++++++++++++++++++++++++++
4 files changed, 623 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 4bda6b9e8c4..a59342de3e7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -60,6 +60,7 @@ F: lib/acpi/
AIROHA PHY
M: Tommy Shih <tommy.shih at airoha.com>
+M: Kevin-KW Huang <kevin-kw.huang at airoha.com>
S: Maintained
F: drivers/net/phy/airoha/
diff --git a/drivers/net/phy/airoha/Kconfig b/drivers/net/phy/airoha/Kconfig
index 2a2f4f05bb2..cbceb0cfdb4 100644
--- a/drivers/net/phy/airoha/Kconfig
+++ b/drivers/net/phy/airoha/Kconfig
@@ -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_AIRONA_PHYLIB
+ help
+ Currently support AIROHA AN8801 1G PHY.
+
config PHY_AIROHA_EN8811
bool "Airoha Ethernet EN8811H support"
depends on PHY_AIROHA
diff --git a/drivers/net/phy/airoha/Makefile b/drivers/net/phy/airoha/Makefile
index cbf123986f2..3144f1dc54f 100644
--- a/drivers/net/phy/airoha/Makefile
+++ b/drivers/net/phy/airoha/Makefile
@@ -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_AIRONA_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
index 00000000000..53657babf57
--- /dev/null
+++ b/drivers/net/phy/airoha/air_an8801.c
@@ -0,0 +1,614 @@
+// 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 at airoha.com>
+ * Sita Huang <sita.huang at airoha.com>
+ * Julien Stephan <jstephan at 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 LINK_UP 1
+#define LINK_DOWN 0
+
+#define MAX_LED_SIZE 3
+
+#define MAX_RETRY 5
+
+#define LED_ENABLE 1
+#define LED_DISABLE 0
+
+/* 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)
+
+/* Invalid data */
+#define INVALID_DATA 0xffffffff
+
+#define AN8801_REG_PHY_INTERNAL0 0x600
+#define AN8801_REG_PHY_INTERNAL1 0x601
+
+enum AIR_LED_GPIO_PIN_T {
+ AIR_LED_GPIO1 = 1,
+ AIR_LED_GPIO2,
+ AIR_LED_GPIO3
+};
+
+enum AIR_LED_T {
+ AIR_LED0 = 0,
+ AIR_LED1,
+ AIR_LED2,
+ AIR_LED3
+};
+
+enum AIR_LED_BLINK_DUT_T {
+ 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_T {
+ AIR_LED_MODE_DISABLE = 0,
+ AIR_LED_MODE_USER_DEFINE,
+ AIR_LED_MODE_LAST
+};
+
+struct air_led_cfg_t {
+ u16 en;
+ u16 gpio;
+ u16 pol;
+ u16 on_cfg;
+ u16 blk_cfg;
+};
+
+struct an8801r_priv {
+ struct air_led_cfg_t led_cfg[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)
+
+/*
+ * For reference only
+ * GPIO1 <-> LED0,
+ * GPIO2 <-> LED1,
+ * GPIO3 <-> LED2,
+ */
+/* User-defined.B */
+static const struct air_led_cfg_t led_cfg_dlt[MAX_LED_SIZE] = {
+ /* LED Enable, GPIO, LED Polarity, LED ON, LED Blink */
+ /* LED0 */
+ {LED_ENABLE, AIR_LED_GPIO1, AIR_ACTIVE_LOW, AIR_LED0_ON, AIR_LED0_BLINK},
+ /* LED1 */
+ {LED_ENABLE, AIR_LED_GPIO2, AIR_ACTIVE_HIGH, AIR_LED1_ON, AIR_LED1_BLINK},
+ /* LED2 */
+ {LED_ENABLE, AIR_LED_GPIO3, AIR_ACTIVE_HIGH, AIR_LED2_ON, AIR_LED2_BLINK},
+};
+
+static const u16 led_blink_cfg_dlt = AIR_LED_BLINK_DUR_64M;
+/* RGMII delay */
+static const u8 rxdelay_force;
+static const u8 txdelay_force;
+static const u16 rxdelay_step = AIR_RGMII_DELAY_NOSTEP;
+static const u8 rxdelay_align;
+static const u16 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) >> 1);
+}
+
+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_t *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 < MAX_LED_SIZE; led_id++) {
+ ret = an8801r_led_set_state(phydev, led_id, led_cfg[led_id].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].en != LED_ENABLE)
+ 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].pol,
+ led_cfg[led_id].on_cfg,
+ led_cfg[led_id].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;
+
+ if (!ofnode_valid(node))
+ return -EINVAL;
+
+ if (ofnode_get_property(node, "airoha,rxclk-delay", NULL)) {
+ if (ofnode_read_u32(node, "airoha,rxclk-delay", &val)) {
+ dev_err(phydev->dev, "airoha,rxclk-delay value is invalid.\n");
+ return -EINVAL;
+ }
+ 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_get_property(node, "airoha,txclk-delay", NULL)) {
+ if (ofnode_read_u32(node, "airoha,txclk-delay", &val)) {
+ dev_err(phydev->dev, "airoha,txclk-delay value is invalid.\n");
+ return -EINVAL;
+ }
+ 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 = 0;
+
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ ret = an8801r_rgmii_txdelay(phydev, AIR_RGMII_DELAY_STEP_4);
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ ret = an8801r_rgmii_rxdelay(phydev, AIR_RGMII_DELAY_NOSTEP, true);
+ break;
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ ret = an8801r_rgmii_txdelay(phydev, AIR_RGMII_DELAY_STEP_4);
+ if (ret)
+ break;
+ ret = an8801r_rgmii_rxdelay(phydev, AIR_RGMII_DELAY_NOSTEP, true);
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ default:
+ if (priv->rxdelay_force) {
+ ret = an8801r_rgmii_rxdelay(phydev, priv->rxdelay_step,
+ priv->rxdelay_align);
+ if (ret)
+ break;
+ }
+ if (priv->txdelay_force)
+ ret = an8801r_rgmii_txdelay(phydev, priv->txdelay_step);
+ break;
+ }
+ return ret;
+}
+
+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, led_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 = calloc(1, sizeof(struct an8801r_priv));
+ if (!priv)
+ return -ENOMEM;
+
+ for (led_id = AIR_LED0; led_id < MAX_LED_SIZE; led_id++)
+ priv->led_cfg[led_id] = led_cfg_dlt[led_id];
+
+ priv->led_blink_cfg = led_blink_cfg_dlt;
+ priv->rxdelay_force = rxdelay_force;
+ priv->txdelay_force = txdelay_force;
+ priv->rxdelay_step = rxdelay_step;
+ priv->rxdelay_align = rxdelay_align;
+ priv->txdelay_step = txdelay_step;
+
+ phydev->priv = priv;
+ return 0;
+}
+
+static int an8801r_read_status(struct phy_device *phydev)
+{
+ u32 data;
+
+ if (phydev->link != LINK_UP)
+ 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,
+};
--
2.52.0
More information about the U-Boot
mailing list