]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
net: phy: air_phy_lib: Factorize BuckPBus register
authorJulien Stephan <jstephan@baylibre.com>
Wed, 29 Apr 2026 13:58:58 +0000 (15:58 +0200)
committerDavid Lechner <dlechner@baylibre.com>
Wed, 10 Jun 2026 20:25:21 +0000 (15:25 -0500)
In preparation of Airoha AN8801R PHY support, move the BuckPBus
register accessors and definitions, present in air_en8811h driver,
into the Airoha PHY shared code (air_phy_lib), so they will be usable
by the new driver without duplicating them.
Also, update air_en8811h driver to use the new function names.

Adapted from [1].

[1]: https://lore.kernel.org/all/20260326-add-airoha-an8801-support-v2-2-1a42d6b6050f@collabora.com/

Signed-off-by: Julien Stephan <jstephan@baylibre.com>
Link: https://patch.msgid.link/20260429-add-ethernet-support-for-genio-520-720-v4-5-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_en8811.c
drivers/net/phy/airoha/air_phy_lib.c [new file with mode: 0644]
drivers/net/phy/airoha/air_phy_lib.h [new file with mode: 0644]

index 4253334e3553c644e9df80a9d722911b54617dce..dcaf1e08354a2e3c00f9dc64c37d9896ecda8b70 100644 (file)
@@ -61,7 +61,7 @@ F:    lib/acpi/
 AIROHA PHY
 M:     Tommy Shih <tommy.shih@airoha.com>
 S:     Maintained
-F:     drivers/net/phy/airoha/air_en8811.c
+F:     drivers/net/phy/airoha/
 
 ALIST
 M:     Simon Glass <sjg@chromium.org>
index 4139df343adbfb5a3fa9257f8be137fccf8221b5..b48426bf0fa18bf1021596300e5ad0d0218dd6c5 100644 (file)
@@ -7,7 +7,13 @@ config PHY_AIROHA_EN8811
        depends on PHY_AIROHA
        depends on SUPPORTS_FW_LOADER
        select FW_LOADER
+       select PHY_AIROHA_PHYLIB
        select PHY_COMMON_PROPS
        help
          AIROHA EN8811H supported.
          AIROHA AN8811HB supported.
+
+config PHY_AIROHA_PHYLIB
+       bool
+       help
+         Airoha Ethernet PHY common library
index 84d23b19ab0b5b3c3448f3fa6ac04e64957593f3..59051caecefb88752a854830cec3e04221d18cbd 100644 (file)
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_PHY_AIROHA_EN8811) += air_en8811.o
+obj-$(CONFIG_PHY_AIROHA_PHYLIB) += air_phy_lib.o
index 32f06dd6dfa3cce1d1de36e27657d377dfd7bd76..7a07be2e95608bb49f9365182bee44cc19741b28 100644 (file)
@@ -25,6 +25,8 @@
 #include <u-boot/crc.h>
 #include <linux/phy/phy-common-props.h>
 
+#include "air_phy_lib.h"
+
 /* MII Registers */
 #define AIR_AUX_CTRL_STATUS            0x1d
 #define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2)
 #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8
 #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc
 
-#define AIR_EXT_PAGE_ACCESS            0x1f
-#define AIR_PHY_PAGE_STANDARD          0x0000
-#define AIR_PHY_PAGE_EXTENDED_4                0x0004
-
 #define AIR_PBUS_MODE_ADDR_HIGH                0x1c
 /* MII Registers Page 4 */
 #define AIR_BPBUS_MODE                 0x10
@@ -310,166 +308,6 @@ static int air_pbus_reg_write(struct phy_device *phydev,
        return ret;
 }
 
-static int air_buckpbus_reg_write(struct phy_device *phydev,
-                                 u32 pbus_address, u32 pbus_data)
-{
-       int ret, saved_page;
-
-       saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-       if (saved_page < 0)
-               return saved_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
-                       AIR_BPBUS_MODE_ADDR_FIXED);
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
-                       upper_16_bits(pbus_address));
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
-                       lower_16_bits(pbus_address));
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
-                       upper_16_bits(pbus_data));
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
-                       lower_16_bits(pbus_data));
-       if (ret < 0)
-               goto restore_page;
-
-restore_page:
-       if (ret < 0)
-               dev_err(phydev->dev, "%s 0x%08x failed: %d\n", __func__,
-                       pbus_address, ret);
-
-       return phy_restore_page(phydev, saved_page, ret);
-}
-
-static int air_buckpbus_reg_read(struct phy_device *phydev,
-                                u32 pbus_address, u32 *pbus_data)
-{
-       int pbus_data_low, pbus_data_high;
-       int ret = 0, saved_page;
-
-       saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-       if (saved_page < 0)
-               return saved_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
-                       AIR_BPBUS_MODE_ADDR_FIXED);
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
-                       upper_16_bits(pbus_address));
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
-                       lower_16_bits(pbus_address));
-       if (ret < 0)
-               goto restore_page;
-
-       pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
-       if (pbus_data_high < 0) {
-               ret = pbus_data_high;
-               goto restore_page;
-       }
-
-       pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
-       if (pbus_data_low < 0) {
-               ret = pbus_data_low;
-               goto restore_page;
-       }
-
-       *pbus_data = pbus_data_low | (pbus_data_high << 16);
-
-restore_page:
-       if (ret < 0)
-               dev_err(phydev->dev, "%s 0x%08x failed: %d\n", __func__,
-                       pbus_address, ret);
-
-       return phy_restore_page(phydev, saved_page, ret);
-}
-
-static int air_buckpbus_reg_modify(struct phy_device *phydev,
-                                  u32 pbus_address, u32 mask, u32 set)
-{
-       int pbus_data_low, pbus_data_high;
-       u32 pbus_data_old, pbus_data_new;
-       int ret = 0, saved_page;
-
-       saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
-       if (saved_page < 0)
-               return saved_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
-                       AIR_BPBUS_MODE_ADDR_FIXED);
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
-                       upper_16_bits(pbus_address));
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
-                       lower_16_bits(pbus_address));
-       if (ret < 0)
-               goto restore_page;
-
-       pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
-       if (pbus_data_high < 0) {
-               ret = pbus_data_high;
-               goto restore_page;
-       }
-
-       pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
-       if (pbus_data_low < 0) {
-               ret = pbus_data_low;
-               goto restore_page;
-       }
-
-       pbus_data_old = pbus_data_low | (pbus_data_high << 16);
-       pbus_data_new = (pbus_data_old & ~mask) | set;
-       if (pbus_data_new == pbus_data_old)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
-                       upper_16_bits(pbus_address));
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
-                       lower_16_bits(pbus_address));
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
-                       upper_16_bits(pbus_data_new));
-       if (ret < 0)
-               goto restore_page;
-
-       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
-                       lower_16_bits(pbus_data_new));
-       if (ret < 0)
-               goto restore_page;
-
-restore_page:
-       if (ret < 0)
-               dev_err(phydev->dev, "%s 0x%08x failed: %d\n", __func__,
-                       pbus_address, ret);
-
-       return phy_restore_page(phydev, saved_page, ret);
-}
-
 static int air_write_buf(struct phy_device *phydev, unsigned long address,
                         unsigned long array_size, const unsigned char *buffer)
 {
@@ -540,12 +378,12 @@ static int an8811hb_check_crc(struct phy_device *phydev,
        u32 pbus_value;
 
        /* Configure CRC */
-       ret = air_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN,
-                                     AN8811HB_CRC_RD_EN);
+       ret = air_phy_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN,
+                                         AN8811HB_CRC_RD_EN);
        if (ret < 0)
                return ret;
 
-       ret = air_buckpbus_reg_read(phydev, set1, &pbus_value);
+       ret = air_phy_buckpbus_reg_read(phydev, set1, &pbus_value);
        if (ret < 0)
                return ret;
 
@@ -554,14 +392,14 @@ static int an8811hb_check_crc(struct phy_device *phydev,
        do {
                mdelay(300);
 
-               ret = air_buckpbus_reg_read(phydev, mon2, &pbus_value);
+               ret = air_phy_buckpbus_reg_read(phydev, mon2, &pbus_value);
                if (ret < 0)
                        return ret;
 
                debug("%d: reg 0x%x val 0x%x!\n", __LINE__, mon2, pbus_value);
 
                if (pbus_value & AN8811HB_CRC_ST) {
-                       ret = air_buckpbus_reg_read(phydev, mon3, &pbus_value);
+                       ret = air_phy_buckpbus_reg_read(phydev, mon3, &pbus_value);
                        if (ret < 0)
                                return ret;
 
@@ -585,11 +423,11 @@ static int an8811hb_check_crc(struct phy_device *phydev,
                }
        } while (--retry);
 
-       ret = air_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN, 0);
+       ret = air_phy_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN, 0);
        if (ret < 0)
                return ret;
 
-       ret = air_buckpbus_reg_read(phydev, set1, &pbus_value);
+       ret = air_phy_buckpbus_reg_read(phydev, set1, &pbus_value);
        if (ret < 0)
                return ret;
 
@@ -647,9 +485,9 @@ static int an8811hb_surge_protect_cfg(struct phy_device *phydev)
                return ret;
        }
 
-       ret = air_buckpbus_reg_modify(phydev, AIR_PHY_CONTROL,
-                                     AIR_PHY_CONTROL_SURGE_5R,
-                                     AIR_PHY_CONTROL_SURGE_5R);
+       ret = air_phy_buckpbus_reg_modify(phydev, AIR_PHY_CONTROL,
+                                         AIR_PHY_CONTROL_SURGE_5R,
+                                         AIR_PHY_CONTROL_SURGE_5R);
        if (ret < 0)
                return ret;
 
@@ -707,14 +545,14 @@ static int en8811h_load_firmware(struct phy_device *phydev)
                goto en8811h_load_firmware_out;
        }
 
-       ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-                                    EN8811H_FW_CTRL_1_START);
+       ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+                                        EN8811H_FW_CTRL_1_START);
        if (ret < 0)
                goto en8811h_load_firmware_out;
 
-       ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
-                                     EN8811H_FW_CTRL_2_LOADING,
-                                     EN8811H_FW_CTRL_2_LOADING);
+       ret = air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
+                                         EN8811H_FW_CTRL_2_LOADING,
+                                         EN8811H_FW_CTRL_2_LOADING);
        if (ret < 0)
                goto en8811h_load_firmware_out;
 
@@ -728,13 +566,13 @@ static int en8811h_load_firmware(struct phy_device *phydev)
        if (ret < 0)
                goto en8811h_load_firmware_out;
 
-       ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
-                                     EN8811H_FW_CTRL_2_LOADING, 0);
+       ret = air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
+                                         EN8811H_FW_CTRL_2_LOADING, 0);
        if (ret < 0)
                goto en8811h_load_firmware_out;
 
-       ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-                                    EN8811H_FW_CTRL_1_FINISH);
+       ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+                                        EN8811H_FW_CTRL_1_FINISH);
        if (ret < 0)
                goto en8811h_load_firmware_out;
 
@@ -742,8 +580,8 @@ static int en8811h_load_firmware(struct phy_device *phydev)
        if (ret < 0)
                goto en8811h_load_firmware_out;
 
-       air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
-                             &priv->firmware_version);
+       air_phy_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
+                                 &priv->firmware_version);
 
        dev_info(phydev->dev, "MD32 firmware version: %08x\n",
                 priv->firmware_version);
@@ -779,8 +617,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
        if (ret < 0)
                goto an8811hb_load_firmware_out;
 
-       ret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
-                                    AIR_PHY_FW_CTRL_1_START);
+       ret = air_phy_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
+                                        AIR_PHY_FW_CTRL_1_START);
        if (ret < 0)
                goto an8811hb_load_firmware_out;
 
@@ -804,8 +642,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
        if (ret < 0)
                goto an8811hb_load_firmware_out;
 
-       ret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
-                                    AIR_PHY_FW_CTRL_1_FINISH);
+       ret = air_phy_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
+                                        AIR_PHY_FW_CTRL_1_FINISH);
        if (ret < 0)
                goto an8811hb_load_firmware_out;
 
@@ -818,7 +656,7 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
        do {
                mdelay(300);
 
-               ret = air_buckpbus_reg_read(phydev, AIR_PHY_FW_CTRL_1, &reg_val);
+               ret = air_phy_buckpbus_reg_read(phydev, AIR_PHY_FW_CTRL_1, &reg_val);
                if (ret < 0)
                        goto an8811hb_load_firmware_out;
 
@@ -828,8 +666,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
                debug("%d: reg 0x%x val 0x%x!\n", __LINE__, AIR_PHY_FW_CTRL_1,
                      reg_val);
 
-               ret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
-                                            AIR_PHY_FW_CTRL_1_FINISH);
+               ret = air_phy_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,
+                                                AIR_PHY_FW_CTRL_1_FINISH);
                if (ret < 0)
                        goto an8811hb_load_firmware_out;
 
@@ -839,8 +677,8 @@ static int an8811hb_load_firmware(struct phy_device *phydev)
        if (ret < 0)
                goto an8811hb_load_firmware_out;
 
-       air_buckpbus_reg_read(phydev, AIR_PHY_MD32FW_VERSION,
-                             &priv->firmware_version);
+       air_phy_buckpbus_reg_read(phydev, AIR_PHY_MD32FW_VERSION,
+                                 &priv->firmware_version);
 
        debug("MD32 firmware version: %08x\n", priv->firmware_version);
 
@@ -859,17 +697,17 @@ int an8811hb_cko_cfg(struct phy_device *phydev)
        int ret = 0;
 
        if (!ofnode_read_bool(node, "airoha,phy-output-clock")) {
-               ret = air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
-                                             AN8811HB_CLK_DRV_CKO_MASK,
-                                             AN8811HB_CLK_DRV_CKOPWD    |
-                                             AN8811HB_CLK_DRV_CKO_LDPWD |
-                                             AN8811HB_CLK_DRV_CKO_LPPWD);
+               ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,
+                                                 AN8811HB_CLK_DRV_CKO_MASK,
+                                                 AN8811HB_CLK_DRV_CKOPWD    |
+                                                 AN8811HB_CLK_DRV_CKO_LDPWD |
+                                                 AN8811HB_CLK_DRV_CKO_LPPWD);
                if (ret < 0)
                        return ret;
 
                debug("CKO Output mode - Disabled\n");
        } else {
-               ret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);
+               ret = air_phy_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);
                if (ret < 0)
                        return ret;
 
@@ -888,13 +726,13 @@ static int en8811h_restart_mcu(struct phy_device *phydev)
        if (ret < 0)
                return ret;
 
-       ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-                                    EN8811H_FW_CTRL_1_START);
+       ret = air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+                                        EN8811H_FW_CTRL_1_START);
        if (ret < 0)
                return ret;
 
-       return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
-                                     EN8811H_FW_CTRL_1_FINISH);
+       return air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+                                         EN8811H_FW_CTRL_1_FINISH);
 }
 
 static int air_led_hw_control_set(struct phy_device *phydev, u8 index,
@@ -1083,9 +921,10 @@ static int en8811h_config_serdes_polarity(struct phy_device *phydev)
        if (pol == PHY_POL_NORMAL)
                pbus_value |= EN8811H_POLARITY_TX_NORMAL;
 
-       return air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
-                                      EN8811H_POLARITY_RX_REVERSE |
-                                      EN8811H_POLARITY_TX_NORMAL, pbus_value);
+       return air_phy_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
+                                          EN8811H_POLARITY_RX_REVERSE |
+                                          EN8811H_POLARITY_TX_NORMAL,
+                                          pbus_value);
 }
 
 static int en8811h_config(struct phy_device *phydev)
@@ -1170,12 +1009,12 @@ static int an8811hb_config(struct phy_device *phydev)
                priv->mcu_needs_restart = true;
        }
 
-       ret = air_buckpbus_reg_read(phydev, AN8811HB_PRO_ID, &pbus_value);
+       ret = air_phy_buckpbus_reg_read(phydev, AN8811HB_PRO_ID, &pbus_value);
        if (ret < 0)
                return ret;
        priv->pro_id = (pbus_value & AN8811HB_PRO_ID_VERSION) + 1;
 
-       ret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);
+       ret = air_phy_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);
        if (ret < 0)
                return ret;
        priv->pkg_sel = (pbus_value & AN8811HB_HWTRAP2_PKG) >> 12;
@@ -1191,8 +1030,8 @@ static int an8811hb_config(struct phy_device *phydev)
                pbus_value |= AN8811HB_RX_POLARITY_NORMAL;
 
        debug("1 pbus_value 0x%x\n", pbus_value);
-       ret = air_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY,
-                                     AN8811HB_RX_POLARITY_NORMAL, pbus_value);
+       ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY,
+                                         AN8811HB_RX_POLARITY_NORMAL, pbus_value);
        if (ret < 0)
                return ret;
 
@@ -1203,35 +1042,35 @@ static int an8811hb_config(struct phy_device *phydev)
                pbus_value |= AN8811HB_TX_POLARITY_NORMAL;
 
        debug("2 pbus_value 0x%x\n", pbus_value);
-       ret = air_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY,
-                                     AN8811HB_TX_POLARITY_NORMAL, pbus_value);
+       ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY,
+                                         AN8811HB_TX_POLARITY_NORMAL, pbus_value);
        if (ret < 0)
                return ret;
 
        /* Configure led gpio pins as output */
        if (priv->pkg_sel) {
-               ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
-                                             AN8811HB_GPIO_OUTPUT_MASK,
-                                             AN8811HB_GPIO_OUTPUT_0115);
+               ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
+                                                 AN8811HB_GPIO_OUTPUT_MASK,
+                                                 AN8811HB_GPIO_OUTPUT_0115);
                if (ret < 0)
                        return ret;
-               ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_1,
-                                             AN8811HB_GPIO_SEL_1_0_MASK |
-                                             AN8811HB_GPIO_SEL_1_1_MASK,
-                                             AN8811HB_GPIO_SEL_1_0 |
-                                             AN8811HB_GPIO_SEL_1_1);
+               ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_1,
+                                                 AN8811HB_GPIO_SEL_1_0_MASK |
+                                                 AN8811HB_GPIO_SEL_1_1_MASK,
+                                                 AN8811HB_GPIO_SEL_1_0 |
+                                                 AN8811HB_GPIO_SEL_1_1);
                if (ret < 0)
                        return ret;
 
-               ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_2,
-                                             AN8811HB_GPIO_SEL_2_15_MASK,
-                                             AN8811HB_GPIO_SEL_2_15);
+               ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_2,
+                                                 AN8811HB_GPIO_SEL_2_15_MASK,
+                                                 AN8811HB_GPIO_SEL_2_15);
                if (ret < 0)
                        return ret;
        } else {
-               ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
-                                             AN8811HB_GPIO_OUTPUT_345,
-                                             AN8811HB_GPIO_OUTPUT_345);
+               ret = air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
+                                                 AN8811HB_GPIO_OUTPUT_345,
+                                                 AN8811HB_GPIO_OUTPUT_345);
                if (ret < 0)
                        return ret;
        }
@@ -1401,16 +1240,6 @@ static int en8811h_probe(struct phy_device *phydev)
        return 0;
 }
 
-static int air_phy_read_page(struct phy_device *phydev)
-{
-       return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS);
-}
-
-static int air_phy_write_page(struct phy_device *phydev, int page)
-{
-       return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);
-}
-
 U_BOOT_PHY_DRIVER(en8811h) = {
        .name = "Airoha EN8811H",
        .uid = EN8811H_PHY_ID,
diff --git a/drivers/net/phy/airoha/air_phy_lib.c b/drivers/net/phy/airoha/air_phy_lib.c
new file mode 100644 (file)
index 0000000..61c3bf8
--- /dev/null
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Airoha Ethernet PHY common library
+ *
+ * Copyright (C) 2026 Airoha Technology Corp.
+ * Copyright (C) 2026 Collabora Ltd.
+ *                    Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ *
+ * Adapated from https://lore.kernel.org/all/20260326-add-airoha-an8801-support-v2-2-1a42d6b6050f@collabora.com/
+ */
+
+#include <dm/device_compat.h>
+#include <linux/compat.h>
+#include <phy.h>
+
+#include "air_phy_lib.h"
+
+#define AIR_EXT_PAGE_ACCESS    0x1f
+
+static int __air_buckpbus_reg_read(struct phy_device *phydev,
+                                  u32 pbus_address, u32 *pbus_data)
+{
+       int pbus_data_low, pbus_data_high;
+       int ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+                       AIR_BPBUS_MODE_ADDR_FIXED);
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
+                       upper_16_bits(pbus_address));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
+                       lower_16_bits(pbus_address));
+       if (ret < 0)
+               return ret;
+
+       pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE,
+                                 AIR_BPBUS_RD_DATA_HIGH);
+       if (pbus_data_high < 0)
+               return pbus_data_high;
+
+       pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE,
+                                AIR_BPBUS_RD_DATA_LOW);
+       if (pbus_data_low < 0)
+               return pbus_data_low;
+
+       *pbus_data = pbus_data_low | (pbus_data_high << 16);
+       return 0;
+}
+
+static int __air_buckpbus_reg_write(struct phy_device *phydev,
+                                   u32 pbus_address, u32 pbus_data)
+{
+       int ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+                       AIR_BPBUS_MODE_ADDR_FIXED);
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
+                       upper_16_bits(pbus_address));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
+                       lower_16_bits(pbus_address));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
+                       upper_16_bits(pbus_data));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
+                       lower_16_bits(pbus_data));
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int __air_buckpbus_reg_modify(struct phy_device *phydev,
+                                    u32 pbus_address, u32 mask, u32 set)
+{
+       int pbus_data_low, pbus_data_high;
+       u32 pbus_data_old, pbus_data_new;
+       int ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+                       AIR_BPBUS_MODE_ADDR_FIXED);
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
+                       upper_16_bits(pbus_address));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
+                       lower_16_bits(pbus_address));
+       if (ret < 0)
+               return ret;
+
+       pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE,
+                                 AIR_BPBUS_RD_DATA_HIGH);
+       if (pbus_data_high < 0)
+               return pbus_data_high;
+
+       pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE,
+                                AIR_BPBUS_RD_DATA_LOW);
+       if (pbus_data_low < 0)
+               return pbus_data_low;
+
+       pbus_data_old = pbus_data_low | (pbus_data_high << 16);
+       pbus_data_new = (pbus_data_old & ~mask) | set;
+       if (pbus_data_new == pbus_data_old)
+               return 0;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
+                       upper_16_bits(pbus_address));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
+                       lower_16_bits(pbus_address));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
+                       upper_16_bits(pbus_data_new));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
+                       lower_16_bits(pbus_data_new));
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address,
+                             u32 *pbus_data)
+{
+       int saved_page;
+       int ret = 0;
+
+       saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+
+       if (saved_page >= 0) {
+               ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data);
+               if (ret < 0)
+                       dev_err(phydev->dev, "%s 0x%08x failed: %d\n", __func__,
+                               pbus_address, ret);
+       }
+
+       return phy_restore_page(phydev, saved_page, ret);
+}
+
+int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address,
+                              u32 pbus_data)
+{
+       int saved_page;
+       int ret = 0;
+
+       saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+
+       if (saved_page >= 0) {
+               ret = __air_buckpbus_reg_write(phydev, pbus_address,
+                                              pbus_data);
+               if (ret < 0)
+                       dev_err(phydev->dev, "%s 0x%08x failed: %d\n", __func__,
+                               pbus_address, ret);
+       }
+
+       return phy_restore_page(phydev, saved_page, ret);
+}
+
+int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_address,
+                               u32 mask, u32 set)
+{
+       int saved_page;
+       int ret = 0;
+
+       saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+
+       if (saved_page >= 0) {
+               ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask,
+                                               set);
+               if (ret < 0)
+                       dev_err(phydev->dev, "%s 0x%08x failed: %d\n", __func__,
+                               pbus_address, ret);
+       }
+
+       return phy_restore_page(phydev, saved_page, ret);
+}
+
+int air_phy_read_page(struct phy_device *phydev)
+{
+       return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS);
+}
+
+int air_phy_write_page(struct phy_device *phydev, int page)
+{
+       return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);
+}
+
+MODULE_DESCRIPTION("Airoha PHY Library");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Louis-Alexis Eyraud");
diff --git a/drivers/net/phy/airoha/air_phy_lib.h b/drivers/net/phy/airoha/air_phy_lib.h
new file mode 100644 (file)
index 0000000..845d2f7
--- /dev/null
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2026 Airoha Technology Corp.
+ * Copyright (C) 2026 Collabora Ltd.
+ *                    Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
+ */
+
+#ifndef __AIR_PHY_LIB_H
+#define __AIR_PHY_LIB_H
+
+#define AIR_EXT_PAGE_ACCESS    0x1f
+
+#define AIR_PHY_PAGE_STANDARD          0x0000
+#define AIR_PHY_PAGE_EXTENDED_1                0x0001
+#define AIR_PHY_PAGE_EXTENDED_4                0x0004
+
+/* MII Registers Page 4*/
+#define AIR_BPBUS_MODE                 0x10
+#define   AIR_BPBUS_MODE_ADDR_FIXED    0x0000
+#define   AIR_BPBUS_MODE_ADDR_INCR     BIT(15)
+#define AIR_BPBUS_WR_ADDR_HIGH         0x11
+#define AIR_BPBUS_WR_ADDR_LOW          0x12
+#define AIR_BPBUS_WR_DATA_HIGH         0x13
+#define AIR_BPBUS_WR_DATA_LOW          0x14
+#define AIR_BPBUS_RD_ADDR_HIGH         0x15
+#define AIR_BPBUS_RD_ADDR_LOW          0x16
+#define AIR_BPBUS_RD_DATA_HIGH         0x17
+#define AIR_BPBUS_RD_DATA_LOW          0x18
+
+int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_address,
+                               u32 mask, u32 set);
+int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address,
+                             u32 *pbus_data);
+int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address,
+                              u32 pbus_data);
+int air_phy_read_page(struct phy_device *phydev);
+int air_phy_write_page(struct phy_device *phydev, int page);
+
+#endif /* __AIR_PHY_LIB_H */