get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2201579/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2201579,
    "url": "http://patchwork.ozlabs.org/api/patches/2201579/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/uboot/patch/20260226085208.429014-1-tommy.shih@airoha.com/",
    "project": {
        "id": 18,
        "url": "http://patchwork.ozlabs.org/api/projects/18/?format=api",
        "name": "U-Boot",
        "link_name": "uboot",
        "list_id": "u-boot.lists.denx.de",
        "list_email": "u-boot@lists.denx.de",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260226085208.429014-1-tommy.shih@airoha.com>",
    "list_archive_url": null,
    "date": "2026-02-26T08:52:00",
    "name": "[v2] net: phy: air_en8811: add support for Airoha AN8811HB PHY",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "701c3b444f4ef097a3eb90ab970986899a67fd18",
    "submitter": {
        "id": 92702,
        "url": "http://patchwork.ozlabs.org/api/people/92702/?format=api",
        "name": "Tommy Shih (施易廷)",
        "email": "tommy.shih@airoha.com"
    },
    "delegate": {
        "id": 157425,
        "url": "http://patchwork.ozlabs.org/api/users/157425/?format=api",
        "username": "jforissier",
        "first_name": "Jerome",
        "last_name": "Forissier",
        "email": "jerome.forissier@linaro.org"
    },
    "mbox": "http://patchwork.ozlabs.org/project/uboot/patch/20260226085208.429014-1-tommy.shih@airoha.com/mbox/",
    "series": [
        {
            "id": 493595,
            "url": "http://patchwork.ozlabs.org/api/series/493595/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/uboot/list/?series=493595",
            "date": "2026-02-26T08:52:00",
            "name": "[v2] net: phy: air_en8811: add support for Airoha AN8811HB PHY",
            "version": 2,
            "mbox": "http://patchwork.ozlabs.org/series/493595/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2201579/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2201579/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<u-boot-bounces@lists.denx.de>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=mediatek.com header.i=@mediatek.com header.a=rsa-sha256\n header.s=dk header.b=HI0jM1Vh;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de\n (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de;\n envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org)",
            "phobos.denx.de;\n dmarc=fail (p=quarantine dis=none) header.from=airoha.com",
            "phobos.denx.de;\n spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de",
            "phobos.denx.de;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=mediatek.com header.i=@mediatek.com\n header.b=\"HI0jM1Vh\";\n\tdkim-atps=neutral",
            "phobos.denx.de; dmarc=fail (p=quarantine dis=none)\n header.from=airoha.com",
            "phobos.denx.de;\n spf=pass smtp.mailfrom=ar701363@airoha.com"
        ],
        "Received": [
            "from phobos.denx.de (phobos.denx.de\n [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fMCNh5g2Mz1xy2\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 27 Feb 2026 00:44:04 +1100 (AEDT)",
            "from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id 791DC83E63;\n\tThu, 26 Feb 2026 14:43:24 +0100 (CET)",
            "by phobos.denx.de (Postfix, from userid 109)\n id 98B5583D1A; Thu, 26 Feb 2026 09:52:22 +0100 (CET)",
            "from mailgw01.mediatek.com (unknown [60.244.123.138])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))\n (No client certificate requested)\n by phobos.denx.de (Postfix) with ESMTPS id D540083AA9\n for <u-boot@lists.denx.de>; Thu, 26 Feb 2026 09:52:16 +0100 (CET)",
            "from mtkmbs09n1.mediatek.inc [(172.21.101.35)] by\n mailgw01.mediatek.com (envelope-from <ar701363@airoha.com>)\n (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 256/256)\n with ESMTP id 969779687; Thu, 26 Feb 2026 16:52:13 +0800",
            "from mtkmbs13n1.mediatek.inc (172.21.101.193) by\n mtkmbs13n1.mediatek.inc (172.21.101.193) with Microsoft SMTP Server\n (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n 15.2.2562.29; Thu, 26 Feb 2026 16:52:11 +0800",
            "from mediatek.com (172.18.153.125) by mtkmbs13n1.mediatek.inc\n (172.21.101.73) with Microsoft SMTP Server id 15.2.2562.29 via Frontend\n Transport; Thu, 26 Feb 2026 16:52:11 +0800",
            "by arhtenbupstream01.localdomain (Postfix, from userid 1003)\n id AC21D1408D4; Thu, 26 Feb 2026 16:52:11 +0800 (CST)"
        ],
        "X-Spam-Checker-Version": "SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de",
        "X-Spam-Level": "",
        "X-Spam-Status": "No, score=0.2 required=5.0 tests=BAYES_00,DKIM_SIGNED,\n DKIM_VALID,RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,\n RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED,\n RDNS_NONE,SPF_HELO_PASS,SPF_PASS,UNPARSEABLE_RELAY autolearn=no\n autolearn_force=no version=3.4.2",
        "X-UUID": [
            "7173e7a612f011f1bcd7499a721e883d-20260226",
            "7173e7a612f011f1bcd7499a721e883d-20260226"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;\n d=mediatek.com;\n s=dk;\n h=Content-Type:Content-Transfer-Encoding:MIME-Version:Message-ID:Date:Subject:CC:To:From;\n bh=aoBiAiV0SOK6j4sCgY5g5CW19OhtF0w6xPTiXXlvFsE=;\n b=HI0jM1VhyZfT20zGJFrxiT+BaL7xhMlG/70VGJZw/yB1paNihgGQsUNhRcymxefNOU1vI6YLGjFkk2h0T9WlUwLSHgY3CT4QhjZjSj244hx4Zi7Jwm642X09PLFlMmNF4vBTWN6AQbSQhuD15ewYn8Jnmh3Aah4XMJbjQ5y5XLY=;",
        "X-CID-P-RULE": "Release_Ham",
        "X-CID-O-INFO": "VERSION:1.3.11, REQID:e242c97d-21da-4678-987b-565641b1508d,\n IP:0,\n U\n RL:0,TC:0,Content:-25,EDM:-20,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACT\n ION:release,TS:-45",
        "X-CID-META": "VersionHash:89c9d04, CLOUDID:c8e004ea-ef90-4382-9c6f-55f2a0689a6b,\n B\n ulkID:nil,BulkQuantity:0,Recheck:0,SF:102|836|888|898,TC:-5,Content:0|15|5\n 0,EDM:1,IP:nil,URL:99|1,File:130,RT:0,Bulk:nil,QS:nil,BEC:-1,COL:0,OSI:0,O\n SA:0,AV:0,LES:1,SPR:NO,DKR:0,DKP:0,BRR:0,BRE:0,ARC:0",
        "X-CID-BVR": "2,SSN|SDN",
        "X-CID-BAS": "2,SSN|SDN,0,_",
        "X-CID-FACTOR": "TF_CID_SPAM_ULS,TF_CID_SPAM_SNR",
        "X-CID-RHF": "D41D8CD98F00B204E9800998ECF8427E",
        "From": "Tommy Shih <tommy.shih@airoha.com>",
        "To": "Christian Marangi <ansuelsmth@gmail.com>, Jerome Forissier\n <jerome.forissier@arm.com>, Joe Hershberger <joe.hershberger@ni.com>, \"Lucien\n . Jheng\" <lucienzx159@gmail.com>, Marek Vasut\n <marek.vasut+renesas@mailbox.org>, Ramon Fried <rfried.dev@gmail.com>, Tom\n Rini <trini@konsulko.com>, <daniel@makrotopia.org>,\n <frank-w@public-files.de>, <u-boot@lists.denx.de>",
        "CC": "Tommy Shih <tommy.shih@airoha.com>, Lucien Jheng <lucien.jheng@airoha.com>",
        "Subject": "[PATCH v2] net: phy: air_en8811: add support for Airoha AN8811HB PHY",
        "Date": "Thu, 26 Feb 2026 16:52:00 +0800",
        "Message-ID": "<20260226085208.429014-1-tommy.shih@airoha.com>",
        "X-Mailer": "git-send-email 2.43.0",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-Mailman-Approved-At": "Thu, 26 Feb 2026 14:43:22 +0100",
        "X-BeenThere": "u-boot@lists.denx.de",
        "X-Mailman-Version": "2.1.39",
        "Precedence": "list",
        "List-Id": "U-Boot discussion <u-boot.lists.denx.de>",
        "List-Unsubscribe": "<https://lists.denx.de/options/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=unsubscribe>",
        "List-Archive": "<https://lists.denx.de/pipermail/u-boot/>",
        "List-Post": "<mailto:u-boot@lists.denx.de>",
        "List-Help": "<mailto:u-boot-request@lists.denx.de?subject=help>",
        "List-Subscribe": "<https://lists.denx.de/listinfo/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=subscribe>",
        "Errors-To": "u-boot-bounces@lists.denx.de",
        "Sender": "\"U-Boot\" <u-boot-bounces@lists.denx.de>",
        "X-Virus-Scanned": "clamav-milter 0.103.8 at phobos.denx.de",
        "X-Virus-Status": "Clean"
    },
    "content": "Add support for the Airoha AN8811HB 2.5 Gigabit PHY to the existing\nen8811h driver. This PHY supports 10/100/1000/2500 Mbps speeds.\n\nUpdate the driver to recognize the AN8811HB PHY ID and handle its\nspecific firmware loading requirements. The firmware loading mechanism\nremains consistent with the existing implementation.\n\nThis driver is based on:\n  - Linux upstream PHY subsystem (v7.0-rc1)\n  - air_an8811hb v0.0.4 out-of-tree uboot driver written by\n    \"Lucien.Jheng <lucien.jheng@airoha.com>\"\n\nTested on MT7987 RFB board.\n\nLink: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?id=6f1769ec5892ac41d82e820d94dcdc68e904aa99\nLink: https://patchwork.kernel.org/project/netdevbpf/patch/20260122071601.1057083-3-bjorn@mork.no/\nSigned-off-by: Tommy Shih <tommy.shih@airoha.com>\nReviewed-by: Lucien.Jheng <lucienzx159@gmail.com>\n\n---\n\nChanges in v2:\n- Remove addition headers which are not used\n\n drivers/net/phy/airoha/Kconfig      |   2 +-\n drivers/net/phy/airoha/air_en8811.c | 689 ++++++++++++++++++++++++++--\n 2 files changed, 647 insertions(+), 44 deletions(-)",
    "diff": "diff --git a/drivers/net/phy/airoha/Kconfig b/drivers/net/phy/airoha/Kconfig\nindex 999564e4848..fcace9a24ac 100644\n--- a/drivers/net/phy/airoha/Kconfig\n+++ b/drivers/net/phy/airoha/Kconfig\n@@ -8,4 +8,4 @@ config PHY_AIROHA_EN8811\n \tselect FW_LOADER\n \thelp\n \t  AIROHA EN8811H supported.\n-\n+\t  AIROHA AN8811HB supported.\ndiff --git a/drivers/net/phy/airoha/air_en8811.c b/drivers/net/phy/airoha/air_en8811.c\nindex 1a628ede82b..0b974472732 100644\n--- a/drivers/net/phy/airoha/air_en8811.c\n+++ b/drivers/net/phy/airoha/air_en8811.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: GPL-2.0+\n /*\n- * Driver for the Airoha EN8811H 2.5 Gigabit PHY.\n+ * Driver for the Airoha EN8811H and AN8811HB 2.5 Gigabit PHY.\n  *\n  * Limitations of the EN8811H:\n  * - Only full duplex supported\n@@ -8,9 +8,8 @@\n  *\n  * Source originated from linux air_en8811h.c\n  *\n- * Copyright (C) 2025 Airoha Technology Corp.\n+ * Copyright (C) 2025, 2026 Airoha Technology Corp.\n  */\n-\n #include <phy.h>\n #include <errno.h>\n #include <log.h>\n@@ -20,27 +19,15 @@\n #include <asm/unaligned.h>\n #include <linux/iopoll.h>\n #include <linux/bitops.h>\n+#include <linux/bitfield.h>\n #include <linux/compat.h>\n #include <dm/device_compat.h>\n #include <u-boot/crc.h>\n \n-#define EN8811H_PHY_ID\t\t0x03a2a411\n-\n-#define AIR_FW_ADDR_DM\t\t0x00000000\n-#define AIR_FW_ADDR_DSP\t\t0x00100000\n-\n-#define EN8811H_MD32_DM_SIZE\t0x4000\n-#define EN8811H_MD32_DSP_SIZE\t0x20000\n-\n-#define EN8811H_FW_CTRL_1\t\t0x0f0018\n-#define EN8811H_FW_CTRL_1_START\t\t0x0\n-#define EN8811H_FW_CTRL_1_FINISH\t0x1\n-#define EN8811H_FW_CTRL_2\t\t0x800000\n-#define EN8811H_FW_CTRL_2_LOADING\tBIT(11)\n-\n /* MII Registers */\n #define AIR_AUX_CTRL_STATUS\t\t0x1d\n #define AIR_AUX_CTRL_STATUS_SPEED_MASK\tGENMASK(4, 2)\n+#define AIR_AUX_CTRL_STATUS_SPEED_10\t0x0\n #define AIR_AUX_CTRL_STATUS_SPEED_100\t0x4\n #define AIR_AUX_CTRL_STATUS_SPEED_1000\t0x8\n #define AIR_AUX_CTRL_STATUS_SPEED_2500\t0xc\n@@ -49,6 +36,7 @@\n #define AIR_PHY_PAGE_STANDARD\t\t0x0000\n #define AIR_PHY_PAGE_EXTENDED_4\t\t0x0004\n \n+#define AIR_PBUS_MODE_ADDR_HIGH\t\t0x1c\n /* MII Registers Page 4 */\n #define AIR_BPBUS_MODE\t\t\t0x10\n #define AIR_BPBUS_MODE_ADDR_FIXED\t0x0000\n@@ -63,8 +51,16 @@\n #define AIR_BPBUS_RD_DATA_LOW\t\t0x18\n \n /* Registers on MDIO_MMD_VEND1 */\n-#define EN8811H_PHY_FW_STATUS\t\t0x8009\n-#define EN8811H_PHY_READY\t\t0x02\n+#define AIR_PHY_MCU_CMD_1\t\t0x800c\n+#define AIR_PHY_MCU_CMD_1_MODE1\t\t0x0\n+#define AIR_PHY_MCU_CMD_2\t\t0x800d\n+#define AIR_PHY_MCU_CMD_2_MODE1\t\t0x0\n+#define AIR_PHY_MCU_CMD_3\t\t0x800e\n+#define AIR_PHY_MCU_CMD_3_MODE1\t\t0x1101\n+#define AIR_PHY_MCU_CMD_3_DOCMD\t\t0x1100\n+#define AIR_PHY_MCU_CMD_4\t\t0x800f\n+#define AIR_PHY_MCU_CMD_4_MODE1\t\t0x0002\n+#define AIR_PHY_MCU_CMD_4_INTCLR\t0x00e4\n \n /* Registers on MDIO_MMD_VEND2 */\n #define AIR_PHY_LED_BCR\t\t\t0x021\n@@ -77,7 +73,7 @@\n \n #define AIR_PHY_LED_DUR_BLINK\t\t0x023\n \n-#define AIR_PHY_LED_ON(i)\t       (0x024 + ((i) * 2))\n+#define AIR_PHY_LED_ON(i)\t\t(0x024 + ((i) * 2))\n #define AIR_PHY_LED_ON_MASK\t\t(GENMASK(6, 0) | BIT(8))\n #define AIR_PHY_LED_ON_LINK1000\t\tBIT(0)\n #define AIR_PHY_LED_ON_LINK100\t\tBIT(1)\n@@ -90,7 +86,7 @@\n #define AIR_PHY_LED_ON_POLARITY\t\tBIT(14)\n #define AIR_PHY_LED_ON_ENABLE\t\tBIT(15)\n \n-#define AIR_PHY_LED_BLINK(i)\t       (0x025 + ((i) * 2))\n+#define AIR_PHY_LED_BLINK(i)\t\t(0x025 + ((i) * 2))\n #define AIR_PHY_LED_BLINK_1000TX\tBIT(0)\n #define AIR_PHY_LED_BLINK_1000RX\tBIT(1)\n #define AIR_PHY_LED_BLINK_100TX\t\tBIT(2)\n@@ -104,21 +100,101 @@\n #define AIR_PHY_LED_BLINK_2500TX\tBIT(10)\n #define AIR_PHY_LED_BLINK_2500RX\tBIT(11)\n \n+/* Registers on BUCKPBUS */\n+#define AIR_PHY_CONTROL\t\t\t0x3a9c\n+#define AIR_PHY_CONTROL_SURGE_5R\tBIT(3)\n+#define AIR_PHY_CONTROL_INTERNAL\tBIT(11)\n+\n+/* Led definitions */\n+#define EN8811H_LED_COUNT\t\t3\n+\n+/* Firmware registers */\n+#define AIR_FW_ADDR_DM\t\t\t0x00000000\n+#define AIR_FW_ADDR_DSP\t\t\t0x00100000\n+#define EN8811H_FW_CTRL_1\t\t0x0f0018\n+#define EN8811H_FW_CTRL_1_START\t\t0x0\n+#define EN8811H_FW_CTRL_1_FINISH\t0x1\n+#define EN8811H_FW_CTRL_2\t\t0x800000\n+#define EN8811H_FW_CTRL_2_LOADING\tBIT(11)\n+#define EN8811H_PHY_FW_STATUS\t\t0x8009\n+#define EN8811H_PHY_READY\t\t0x02\n+#define AIR_PHY_FW_STATUS\t\t0x8009\n+#define AIR_PHY_READY\t\t\t0x02\n+\n+#define AIR_PHY_FW_CTRL_1\t\t0x0f0018\n+#define AIR_PHY_FW_CTRL_1_START\t\t0x0\n+#define AIR_PHY_FW_CTRL_1_FINISH\t0x1\n+\n+/* EN8811H */\n+#define EN8811H_PHY_ID\t\t\t0x03a2a411\n+#define EN8811H_MD32_DM_SIZE\t\t0x4000\n+#define EN8811H_MD32_DSP_SIZE\t\t0x20000\n #define EN8811H_FW_VERSION\t\t0x3b3c\n \n #define EN8811H_POLARITY\t\t0xca0f8\n #define EN8811H_POLARITY_TX_NORMAL\tBIT(0)\n #define EN8811H_POLARITY_RX_REVERSE\tBIT(1)\n-\n #define EN8811H_CLK_CGM\t\t\t0xcf958\n #define EN8811H_CLK_CGM_CKO\t\tBIT(26)\n #define EN8811H_HWTRAP1\t\t\t0xcf914\n #define EN8811H_HWTRAP1_CKO\t\tBIT(12)\n \n-#define clear_bit(bit, bitmap)\t__clear_bit(bit, bitmap)\n-\n-/* Led definitions */\n-#define EN8811H_LED_COUNT\t3\n+/* AN8811HB */\n+#define AN8811HB_PHY_ID\t\t\t0xc0ff04a0\n+#define AIR_MD32_DM_SIZE\t\t0x8000\n+#define AIR_MD32_DSP_SIZE\t\t0x20000\n+#define AIR_PHY_MD32FW_VERSION\t\t0x3b3c\n+\n+#define AN8811HB_GPIO_OUTPUT\t\t0x5cf8b8\n+#define AN8811HB_GPIO_OUTPUT_MASK\tGENMASK(15, 0)\n+#define AN8811HB_GPIO_OUTPUT_345\t(BIT(3) | BIT(4) | BIT(5))\n+#define AN8811HB_GPIO_OUTPUT_0115\t(BIT(0) | BIT(1) | BIT(15))\n+#define AN8811HB_GPIO_SEL_1\t\t0x5cf8bc\n+#define AN8811HB_GPIO_SEL_1_0_MASK\tGENMASK(2, 0)\n+#define AN8811HB_GPIO_SEL_1_1_MASK\tGENMASK(6, 4)\n+#define AN8811HB_GPIO_SEL_1_0\t\tFIELD_PREP(AN8811HB_GPIO_SEL_1_0_MASK, 1)\n+#define AN8811HB_GPIO_SEL_1_1\t\tFIELD_PREP(AN8811HB_GPIO_SEL_1_1_MASK, 0)\n+#define AN8811HB_GPIO_SEL_2\t\t0x5cf8c0\n+#define AN8811HB_GPIO_SEL_2_15_MASK\tGENMASK(30, 28)\n+#define AN8811HB_GPIO_SEL_2_15\t\tFIELD_PREP(AN8811HB_GPIO_SEL_2_15_MASK, 2)\n+\n+#define AN8811HB_CRC_PM_SET1\t\t0xf020c\n+#define AN8811HB_CRC_PM_MON2\t\t0xf0218\n+#define AN8811HB_CRC_PM_MON3\t\t0xf021c\n+#define AN8811HB_CRC_DM_SET1\t\t0xf0224\n+#define AN8811HB_CRC_DM_MON2\t\t0xf0230\n+#define AN8811HB_CRC_DM_MON3\t\t0xf0234\n+#define AN8811HB_CRC_RD_EN\t\tBIT(0)\n+#define AN8811HB_CRC_ST\t\t\t(BIT(0) | BIT(1))\n+#define AN8811HB_CRC_CHECK_PASS\t\tBIT(0)\n+\n+#define AN8811HB_TX_POLARITY\t\t0x5ce004\n+#define AN8811HB_TX_POLARITY_NORMAL\tBIT(7)\n+#define AN8811HB_RX_POLARITY\t\t0x5ce61c\n+#define AN8811HB_RX_POLARITY_NORMAL\tBIT(7)\n+\n+#define AN8811HB_HWTRAP1\t\t0x5cf910\n+#define AN8811HB_HWTRAP2\t\t0x5cf914\n+#define AN8811HB_HWTRAP2_CKO\t\tBIT(28)\n+#define AN8811HB_HWTRAP2_PKG\t\t(BIT(12) | BIT(13) | BIT(14))\n+#define AN8811HB_PRO_ID\t\t\t0x5cf920\n+#define AN8811HB_PRO_ID_VERSION\t\tGENMASK(3, 0)\n+\n+#define AN8811HB_CLK_DRV\t\t0x5cf9e4\n+#define AN8811HB_CLK_DRV_CKO_MASK\tGENMASK(14, 12)\n+#define AN8811HB_CLK_DRV_CKOPWD\t\tBIT(12)\n+#define AN8811HB_CLK_DRV_CKO_LDPWD\tBIT(13)\n+#define AN8811HB_CLK_DRV_CKO_LPPWD\tBIT(14)\n+\n+#define AN8811HB_MCU_SW_RST\t\t0x5cf9f8\n+#define AN8811HB_MCU_SW_RST_HOLD\tBIT(16)\n+#define AN8811HB_MCU_SW_RST_RUN\t\t(BIT(16) | BIT(0))\n+#define AN8811HB_MCU_SW_START\t\t0x5cf9fc\n+#define AN8811HB_MCU_SW_START_EN\tBIT(16)\n+\n+#define clear_bit(bit, bitmap)\t\t__clear_bit(bit, bitmap)\n+\n+#define SCRIPT_NAME(name)\t\t#name \"_load_firmware\"\n \n struct led {\n \tunsigned long rules;\n@@ -191,11 +267,48 @@ enum air_led_trigger_netdev_modes {\n #define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS)\n \n struct en8811h_priv {\n-\tint firmware_version;\n+\tu32\t\tfirmware_version;\n \tbool\t\tmcu_needs_restart;\n \tstruct led\tled[EN8811H_LED_COUNT];\n+\tu32\t\tpro_id;\n+\tu32\t\tpkg_sel;\n+\tu32\t\tmem_size;\n+\tconst char\t*script_name;\n };\n \n+static int air_pbus_reg_write(struct phy_device *phydev,\n+\t\t\t      u32 pbus_reg, u32 pbus_data)\n+{\n+\tint pbus_addr = (phydev->addr) + 8;\n+\tstruct mii_dev *bus = phydev->bus;\n+\tint ret;\n+\n+\tret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE,\n+\t\t\t AIR_EXT_PAGE_ACCESS,\n+\t\t\t (pbus_reg >> 16));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE,\n+\t\t\t AIR_PBUS_MODE_ADDR_HIGH,\n+\t\t\t ((pbus_reg & GENMASK(15, 6)) >> 6));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE,\n+\t\t\t ((pbus_reg & GENMASK(5, 2)) >> 2),\n+\t\t\t (pbus_data & GENMASK(15, 0)));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = bus->write(bus, pbus_addr, MDIO_DEVAD_NONE, 0x10,\n+\t\t\t ((pbus_data & GENMASK(31, 16)) >> 16));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\treturn ret;\n+}\n+\n static int air_buckpbus_reg_write(struct phy_device *phydev,\n \t\t\t\t  u32 pbus_address, u32 pbus_data)\n {\n@@ -359,8 +472,8 @@ restore_page:\n static int air_write_buf(struct phy_device *phydev, unsigned long address,\n \t\t\t unsigned long array_size, const unsigned char *buffer)\n {\n-\tunsigned int offset;\n \tint ret, saved_page;\n+\tu32 offset;\n \tu16 val;\n \n \tsaved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);\n@@ -419,18 +532,144 @@ static int en8811h_wait_mcu_ready(struct phy_device *phydev)\n \treturn ret;\n }\n \n-int en8811h_read_fw(void **fw, size_t *fwsize)\n+static int an8811hb_check_crc(struct phy_device *phydev,\n+\t\t\t      u32 set1, u32 mon2, u32 mon3)\n+{\n+\tint ret, retry = 10;\n+\tu32 pbus_value;\n+\n+\t/* Configure CRC */\n+\tret = air_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN,\n+\t\t\t\t      AN8811HB_CRC_RD_EN);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = air_buckpbus_reg_read(phydev, set1, &pbus_value);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tdebug(\"%d: reg 0x%x val 0x%x!\\n\", __LINE__, set1, pbus_value);\n+\n+\tdo {\n+\t\tmdelay(300);\n+\n+\t\tret = air_buckpbus_reg_read(phydev, mon2, &pbus_value);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tdebug(\"%d: reg 0x%x val 0x%x!\\n\", __LINE__, mon2, pbus_value);\n+\n+\t\tif (pbus_value & AN8811HB_CRC_ST) {\n+\t\t\tret = air_buckpbus_reg_read(phydev, mon3, &pbus_value);\n+\t\t\tif (ret < 0)\n+\t\t\t\treturn ret;\n+\n+\t\t\tdebug(\"%d: reg 0x%x val 0x%x!\\n\", __LINE__, mon3,\n+\t\t\t      pbus_value);\n+\n+\t\t\tif (pbus_value & AN8811HB_CRC_CHECK_PASS)\n+\t\t\t\tdebug(\"CRC Check PASS!\\n\");\n+\t\t\telse\n+\t\t\t\tdev_err(phydev->dev, \"CRC Check FAIL!(0x%lx)\\n\",\n+\t\t\t\t\tpbus_value & AN8811HB_CRC_CHECK_PASS);\n+\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (!retry) {\n+\t\t\tdev_err(phydev->dev,\n+\t\t\t\t\"CRC Check is not ready.(Status %u)\\n\",\n+\t\t\t\tpbus_value);\n+\t\t\treturn -ENODEV;\n+\t\t}\n+\t} while (--retry);\n+\n+\tret = air_buckpbus_reg_modify(phydev, set1, AN8811HB_CRC_RD_EN, 0);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = air_buckpbus_reg_read(phydev, set1, &pbus_value);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tdebug(\"%d: reg 0x%x val 0x%x!\\n\", __LINE__, set1, pbus_value);\n+\n+\treturn ret;\n+}\n+\n+static int an8811hb_mcu_assert(struct phy_device *phydev)\n+{\n+\tint ret;\n+\n+\tret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_RST,\n+\t\t\t\t AN8811HB_MCU_SW_RST_HOLD);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_START, 0);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tdebug(\"MCU asserted\\n\");\n+\tmdelay(50);\n+\n+\treturn ret;\n+}\n+\n+static int an8811hb_mcu_deassert(struct phy_device *phydev)\n+{\n+\tint ret;\n+\n+\tret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_START,\n+\t\t\t\t AN8811HB_MCU_SW_START_EN);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = air_pbus_reg_write(phydev, AN8811HB_MCU_SW_RST,\n+\t\t\t\t AN8811HB_MCU_SW_RST_RUN);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tdebug(\"MCU deasserted\\n\");\n+\tmdelay(50);\n+\n+\treturn ret;\n+}\n+\n+static int an8811hb_surge_protect_cfg(struct phy_device *phydev)\n+{\n+\tofnode node = phy_get_ofnode(phydev);\n+\tint ret = 0;\n+\n+\tif (!ofnode_read_bool(node, \"airoha,surge-5r\")) {\n+\t\tdebug(\"Surge Protection mode - 0R\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tret = air_buckpbus_reg_modify(phydev, AIR_PHY_CONTROL,\n+\t\t\t\t      AIR_PHY_CONTROL_SURGE_5R,\n+\t\t\t\t      AIR_PHY_CONTROL_SURGE_5R);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tdebug(\"Surge Protection mode - 5R\\n\");\n+\n+\treturn ret;\n+}\n+\n+static int en8811h_read_fw(void **fw, size_t *fwsize, struct en8811h_priv *priv)\n {\n+\tconst char *script_name = priv->script_name;\n+\tu32 mem_size = priv->mem_size;\n \tvoid *buffer;\n \tint ret;\n \n-\tbuffer = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);\n+\tbuffer = malloc(mem_size);\n \tif (!buffer)\n \t\treturn -ENOMEM;\n \n-\tret = request_firmware_into_buf_via_script(buffer,\n-\t\t\t\t\t\t   EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE,\n-\t\t\t\t\t\t   \"en8811h_load_firmware\", fwsize);\n+\tret = request_firmware_into_buf_via_script(buffer, mem_size,\n+\t\t\t\t\t\t   script_name, fwsize);\n \tif (ret) {\n \t\tfree(buffer);\n \t\treturn ret;\n@@ -450,7 +689,10 @@ static int en8811h_load_firmware(struct phy_device *phydev)\n \tvoid *buffer;\n \tint ret;\n \n-\tret = en8811h_read_fw(&buffer, &fw_size);\n+\tpriv->script_name = SCRIPT_NAME(en8811h);\n+\tpriv->mem_size = EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE;\n+\n+\tret = en8811h_read_fw(&buffer, &fw_size, priv);\n \tif (ret < 0) {\n \t\tdev_err(phydev->dev, \"Failed to get firmware data\\n\");\n \t\treturn -EINVAL;\n@@ -496,9 +738,12 @@ static int en8811h_load_firmware(struct phy_device *phydev)\n \t\tgoto en8811h_load_firmware_out;\n \n \tret = en8811h_wait_mcu_ready(phydev);\n+\tif (ret < 0)\n+\t\tgoto en8811h_load_firmware_out;\n \n \tair_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,\n \t\t\t      &priv->firmware_version);\n+\n \tdev_info(phydev->dev, \"MD32 firmware version: %08x\\n\",\n \t\t priv->firmware_version);\n \n@@ -510,6 +755,130 @@ en8811h_load_firmware_out:\n \treturn ret;\n }\n \n+static int an8811hb_load_firmware(struct phy_device *phydev)\n+{\n+\tstruct en8811h_priv *priv = phydev->priv;\n+\tint ret, retry = 10;\n+\tsize_t fw_size;\n+\tvoid *buffer;\n+\tu32 reg_val;\n+\n+\tret = an8811hb_mcu_assert(phydev);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = an8811hb_mcu_deassert(phydev);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tpriv->script_name = SCRIPT_NAME(an8811hb);\n+\tpriv->mem_size = AIR_MD32_DM_SIZE + AIR_MD32_DSP_SIZE;\n+\n+\tret = en8811h_read_fw(&buffer, &fw_size, priv);\n+\tif (ret < 0)\n+\t\tgoto an8811hb_load_firmware_out;\n+\n+\tret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,\n+\t\t\t\t     AIR_PHY_FW_CTRL_1_START);\n+\tif (ret < 0)\n+\t\tgoto an8811hb_load_firmware_out;\n+\n+\tret = air_write_buf(phydev, AIR_FW_ADDR_DM, AIR_MD32_DM_SIZE,\n+\t\t\t    (unsigned char *)buffer);\n+\tif (ret < 0)\n+\t\tgoto an8811hb_load_firmware_out;\n+\n+\tret = an8811hb_check_crc(phydev, AN8811HB_CRC_DM_SET1,\n+\t\t\t\t AN8811HB_CRC_DM_MON2, AN8811HB_CRC_DM_MON3);\n+\tif (ret < 0)\n+\t\tgoto an8811hb_load_firmware_out;\n+\n+\tret = air_write_buf(phydev, AIR_FW_ADDR_DSP, AIR_MD32_DSP_SIZE,\n+\t\t\t    (unsigned char *)buffer + AIR_MD32_DM_SIZE);\n+\tif (ret < 0)\n+\t\tgoto an8811hb_load_firmware_out;\n+\n+\tret = an8811hb_check_crc(phydev, AN8811HB_CRC_PM_SET1,\n+\t\t\t\t AN8811HB_CRC_PM_MON2, AN8811HB_CRC_PM_MON3);\n+\tif (ret < 0)\n+\t\tgoto an8811hb_load_firmware_out;\n+\n+\tret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,\n+\t\t\t\t     AIR_PHY_FW_CTRL_1_FINISH);\n+\tif (ret < 0)\n+\t\tgoto an8811hb_load_firmware_out;\n+\n+\tret = an8811hb_surge_protect_cfg(phydev);\n+\tif (ret < 0) {\n+\t\tdev_err(phydev->dev, \"an8811hb_surge_protect_cfg fail. (ret=%d)\\n\", ret);\n+\t\tgoto an8811hb_load_firmware_out;\n+\t}\n+\n+\tdo {\n+\t\tmdelay(300);\n+\n+\t\tret = air_buckpbus_reg_read(phydev, AIR_PHY_FW_CTRL_1, &reg_val);\n+\t\tif (ret < 0)\n+\t\t\tgoto an8811hb_load_firmware_out;\n+\n+\t\tif (reg_val == AIR_PHY_FW_CTRL_1_FINISH)\n+\t\t\tbreak;\n+\n+\t\tdebug(\"%d: reg 0x%x val 0x%x!\\n\", __LINE__, AIR_PHY_FW_CTRL_1,\n+\t\t      reg_val);\n+\n+\t\tret = air_buckpbus_reg_write(phydev, AIR_PHY_FW_CTRL_1,\n+\t\t\t\t\t     AIR_PHY_FW_CTRL_1_FINISH);\n+\t\tif (ret < 0)\n+\t\t\tgoto an8811hb_load_firmware_out;\n+\n+\t} while (--retry);\n+\n+\tret = en8811h_wait_mcu_ready(phydev);\n+\tif (ret < 0)\n+\t\tgoto an8811hb_load_firmware_out;\n+\n+\tair_buckpbus_reg_read(phydev, AIR_PHY_MD32FW_VERSION,\n+\t\t\t      &priv->firmware_version);\n+\n+\tdebug(\"MD32 firmware version: %08x\\n\", priv->firmware_version);\n+\n+an8811hb_load_firmware_out:\n+\tfree(buffer);\n+\tif (ret < 0)\n+\t\tdev_err(phydev->dev, \"Firmware loading failed: %d\\n\", ret);\n+\n+\treturn ret;\n+}\n+\n+int an8811hb_cko_cfg(struct phy_device *phydev)\n+{\n+\tofnode node = phy_get_ofnode(phydev);\n+\tu32 pbus_value;\n+\tint ret = 0;\n+\n+\tif (!ofnode_read_bool(node, \"airoha,phy-output-clock\")) {\n+\t\tret = air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV,\n+\t\t\t\t\t      AN8811HB_CLK_DRV_CKO_MASK,\n+\t\t\t\t\t      AN8811HB_CLK_DRV_CKOPWD    |\n+\t\t\t\t\t      AN8811HB_CLK_DRV_CKO_LDPWD |\n+\t\t\t\t\t      AN8811HB_CLK_DRV_CKO_LPPWD);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tdebug(\"CKO Output mode - Disabled\\n\");\n+\t} else {\n+\t\tret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tdebug(\"CKO Output %dMHz - Enabled\\n\",\n+\t\t      (pbus_value & AN8811HB_HWTRAP2_CKO) ? 50 : 25);\n+\t}\n+\n+\treturn ret;\n+}\n+\n static int en8811h_restart_mcu(struct phy_device *phydev)\n {\n \tint ret;\n@@ -613,13 +982,30 @@ static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol)\n \treturn 0;\n }\n \n+/**\n+ * air_leds_init - Initialize and configure LEDs for a phy device.\n+ *\n+ * @phydev: Pointer to the phy_device structure.\n+ * @num: Number of LEDs to initialize.\n+ * @dur: Duration for LED blink in milliseconds. It sets the duration\n+ *       for both the ON and OFF periods (OFF period will be half of `dur`).\n+ * @mode: LED operation mode. Supported modes are:\n+ *           - AIR_LED_MODE_DISABLE: Disables LED control.\n+ *           - AIR_LED_MODE_USER_DEFINE: Enables user-defined LED control.\n+ *\n+ * Initializes and configures LEDs on a phy device with a specified blink duration\n+ * and mode. Supports disabling or enabling user-defined control.\n+ * Return:\n+ * On success, returns 0. On error, it returns a negative value that denotes\n+ * the error code.\n+ */\n+\n static int air_leds_init(struct phy_device *phydev, int num, u16 dur, int mode)\n {\n \tstruct en8811h_priv *priv = phydev->priv;\n \tint ret, i;\n \n-\tret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK,\n-\t\t\t    dur);\n+\tret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK, dur);\n \tif (ret < 0)\n \t\treturn ret;\n \n@@ -707,10 +1093,121 @@ static int en8811h_config(struct phy_device *phydev)\n \t\tpbus_value |=  EN8811H_POLARITY_TX_NORMAL;\n \tret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,\n \t\t\t\t      EN8811H_POLARITY_RX_REVERSE |\n-\t\t\t\t      EN8811H_POLARITY_TX_NORMAL, pbus_value);\n+\t\t\t\t      EN8811H_POLARITY_TX_NORMAL,\n+\t\t\t\t      pbus_value);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,\n+\t\t\t    AIR_LED_MODE_USER_DEFINE);\n+\tif (ret < 0) {\n+\t\tdev_err(phydev->dev, \"Failed to disable leds: %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int an8811hb_config(struct phy_device *phydev)\n+{\n+\tstruct en8811h_priv *priv = phydev->priv;\n+\tu32 pbus_value = 0;\n+\tofnode node;\n+\tint ret = 0;\n+\n+\tnode = phy_get_ofnode(phydev);\n+\tif (!ofnode_valid(node))\n+\t\treturn 0;\n+\n+\t/* If restart happened in .probe(), no need to restart now */\n+\tif (priv->mcu_needs_restart) {\n+\t\tret = an8811hb_mcu_assert(phydev);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tret = an8811hb_mcu_deassert(phydev);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tret = en8811h_restart_mcu(phydev);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t} else {\n+\t\tret = an8811hb_load_firmware(phydev);\n+\t\tif (ret) {\n+\t\t\tdev_err(phydev->dev, \"Load firmware fail.\\n\");\n+\t\t\treturn ret;\n+\t\t}\n+\t\t/* Next calls to .config() mcu needs to restart */\n+\t\tpriv->mcu_needs_restart = true;\n+\t}\n+\n+\tret = air_buckpbus_reg_read(phydev, AN8811HB_PRO_ID, &pbus_value);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tpriv->pro_id = (pbus_value & AN8811HB_PRO_ID_VERSION) + 1;\n+\n+\tret = air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tpriv->pkg_sel = (pbus_value & AN8811HB_HWTRAP2_PKG) >> 12;\n+\tdebug(\"%s(%d) Version: E%d\\n\",\n+\t      priv->pkg_sel ? \"AN8811HBCN\" : \"AN8811HBN\", priv->pkg_sel,\n+\t      priv->pro_id);\n+\n+\t/* Serdes polarity */\n+\tpbus_value = 0;\n+\tif (ofnode_read_bool(node, \"airoha,pnswap-rx\"))\n+\t\tpbus_value &= ~AN8811HB_RX_POLARITY_NORMAL;\n+\telse\n+\t\tpbus_value |= AN8811HB_RX_POLARITY_NORMAL;\n+\n+\tdebug(\"1 pbus_value 0x%x\\n\", pbus_value);\n+\tret = air_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY,\n+\t\t\t\t      AN8811HB_RX_POLARITY_NORMAL, pbus_value);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tpbus_value = 0;\n+\tif (ofnode_read_bool(node, \"airoha,pnswap-tx\"))\n+\t\tpbus_value &= ~AN8811HB_TX_POLARITY_NORMAL;\n+\telse\n+\t\tpbus_value |= AN8811HB_TX_POLARITY_NORMAL;\n+\n+\tdebug(\"2 pbus_value 0x%x\\n\", pbus_value);\n+\tret = air_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY,\n+\t\t\t\t      AN8811HB_TX_POLARITY_NORMAL, pbus_value);\n \tif (ret < 0)\n \t\treturn ret;\n \n+\t/* Configure led gpio pins as output */\n+\tif (priv->pkg_sel) {\n+\t\tret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,\n+\t\t\t\t\t      AN8811HB_GPIO_OUTPUT_MASK,\n+\t\t\t\t\t      AN8811HB_GPIO_OUTPUT_0115);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\tret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_1,\n+\t\t\t\t\t      AN8811HB_GPIO_SEL_1_0_MASK |\n+\t\t\t\t\t      AN8811HB_GPIO_SEL_1_1_MASK,\n+\t\t\t\t\t      AN8811HB_GPIO_SEL_1_0 |\n+\t\t\t\t\t      AN8811HB_GPIO_SEL_1_1);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\n+\t\tret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_SEL_2,\n+\t\t\t\t\t      AN8811HB_GPIO_SEL_2_15_MASK,\n+\t\t\t\t\t      AN8811HB_GPIO_SEL_2_15);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t} else {\n+\t\tret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,\n+\t\t\t\t\t      AN8811HB_GPIO_OUTPUT_345,\n+\t\t\t\t\t      AN8811HB_GPIO_OUTPUT_345);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t}\n+\n \tret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,\n \t\t\t    AIR_LED_MODE_USER_DEFINE);\n \tif (ret < 0) {\n@@ -718,9 +1215,91 @@ static int en8811h_config(struct phy_device *phydev)\n \t\treturn ret;\n \t}\n \n+\t/* Co-Clock Output */\n+\tret = an8811hb_cko_cfg(phydev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tprintf(\"AN8811HB initialize OK !\\n\");\n+\n+\treturn 0;\n+}\n+\n+static int an8811hb_update_duplex(struct phy_device *phydev)\n+{\n+\tint lpa;\n+\n+\tif (phydev->autoneg == AUTONEG_ENABLE) {\n+\t\tlpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);\n+\t\tif (lpa < 0)\n+\t\t\treturn lpa;\n+\n+\t\tswitch (phydev->speed) {\n+\t\tcase SPEED_2500:\n+\t\tcase SPEED_1000:\n+\t\t\tphydev->duplex = DUPLEX_FULL;\n+\t\t\tbreak;\n+\t\tcase SPEED_100:\n+\t\t\tphydev->duplex = (lpa & LPA_100FULL) ? DUPLEX_FULL :\n+\t\t\t\t\t\t\t       DUPLEX_HALF;\n+\t\t\tbreak;\n+\t\tcase SPEED_10:\n+\t\t\tphydev->duplex = (lpa & LPA_10FULL) ? DUPLEX_FULL :\n+\t\t\t\t\t\t\t      DUPLEX_HALF;\n+\t\t\tbreak;\n+\t\t}\n+\t} else {\n+\t\tint bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);\n+\n+\t\tif (phydev->speed == SPEED_2500)\n+\t\t\tphydev->duplex = DUPLEX_FULL;\n+\t\telse\n+\t\t\tphydev->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL :\n+\t\t\t\t\t\t\t\t  DUPLEX_HALF;\n+\t}\n+\n \treturn 0;\n }\n \n+static int an8811hb_parse_status(struct phy_device *phydev)\n+{\n+\tint ret = 0, reg_value;\n+\n+\treg_value = phy_read(phydev, MDIO_DEVAD_NONE, AIR_AUX_CTRL_STATUS);\n+\tif (reg_value < 0)\n+\t\treturn reg_value;\n+\n+\tswitch (reg_value & AIR_AUX_CTRL_STATUS_SPEED_MASK) {\n+\tcase AIR_AUX_CTRL_STATUS_SPEED_2500:\n+\t\tphydev->speed = SPEED_2500;\n+\t\tbreak;\n+\tcase AIR_AUX_CTRL_STATUS_SPEED_1000:\n+\t\tphydev->speed = SPEED_1000;\n+\t\tbreak;\n+\tcase AIR_AUX_CTRL_STATUS_SPEED_100:\n+\t\tphydev->speed = SPEED_100;\n+\t\tbreak;\n+\tcase AIR_AUX_CTRL_STATUS_SPEED_10:\n+\t\tphydev->speed = SPEED_10;\n+\t\tbreak;\n+\tdefault:\n+\t\tdev_err(phydev->dev,\n+\t\t\t\"Auto-neg error, defaulting to 2500M/FD\\n\");\n+\t\tphydev->speed = SPEED_2500;\n+\t\tphydev->duplex = DUPLEX_FULL;\n+\t\treturn 0;\n+\t}\n+\n+\t/* Update duplex mode based on speed and negotiation status */\n+\tret = an8811hb_update_duplex(phydev);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tdebug(\"Speed: %d, %s duplex\\n\", phydev->speed,\n+\t      (phydev->duplex) ? \"full\" : \"half\");\n+\treturn ret;\n+}\n+\n static int en8811h_parse_status(struct phy_device *phydev)\n {\n \tint ret = 0, reg_value;\n@@ -742,7 +1321,8 @@ static int en8811h_parse_status(struct phy_device *phydev)\n \t\tphydev->speed = SPEED_100;\n \t\tbreak;\n \tdefault:\n-\t\tdev_err(phydev->dev, \"Auto-neg error, defaulting to 2500M/FD\\n\");\n+\t\tdev_err(phydev->dev,\n+\t\t\t\"Auto-neg error, defaulting to 2500M/FD\\n\");\n \t\tphydev->speed = SPEED_2500;\n \t\tbreak;\n \t}\n@@ -752,24 +1332,35 @@ static int en8811h_parse_status(struct phy_device *phydev)\n \n static int en8811h_startup(struct phy_device *phydev)\n {\n+\tu32 phy_id = phydev->phy_id;\n \tint ret = 0;\n \n \tret = genphy_update_link(phydev);\n \tif (ret)\n \t\treturn ret;\n \n-\treturn en8811h_parse_status(phydev);\n+\tif (phy_id == EN8811H_PHY_ID)\n+\t\tret = en8811h_parse_status(phydev);\n+\telse if (phy_id == AN8811HB_PHY_ID)\n+\t\tret = an8811hb_parse_status(phydev);\n+\n+\treturn ret;\n }\n \n static int en8811h_probe(struct phy_device *phydev)\n {\n \tstruct en8811h_priv *priv;\n+\tint phy_id;\n \n \tpriv = malloc(sizeof(*priv));\n \tif (!priv)\n \t\treturn -ENOMEM;\n \tmemset(priv, 0, sizeof(*priv));\n \n+\tdebug(\"%s driver is probed.\\n\", phydev->drv->name);\n+\tget_phy_id(phydev->bus, phydev->addr, MDIO_DEVAD_NONE, &phy_id);\n+\tdebug(\"phy id is 0x%x.\\n\", phy_id);\n+\n \tpriv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0;\n \tpriv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1;\n \tpriv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2;\n@@ -782,12 +1373,12 @@ static int en8811h_probe(struct phy_device *phydev)\n \treturn 0;\n }\n \n-static int en8811h_read_page(struct phy_device *phydev)\n+static int air_phy_read_page(struct phy_device *phydev)\n {\n \treturn phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS);\n }\n \n-static int en8811h_write_page(struct phy_device *phydev, int page)\n+static int air_phy_write_page(struct phy_device *phydev, int page)\n {\n \treturn phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);\n }\n@@ -798,8 +1389,20 @@ U_BOOT_PHY_DRIVER(en8811h) = {\n \t.mask = 0x0ffffff0,\n \t.config = &en8811h_config,\n \t.probe = &en8811h_probe,\n-\t.read_page = &en8811h_read_page,\n-\t.write_page = &en8811h_write_page,\n+\t.read_page = &air_phy_read_page,\n+\t.write_page = &air_phy_write_page,\n+\t.startup = &en8811h_startup,\n+\t.shutdown = &genphy_shutdown,\n+};\n+\n+U_BOOT_PHY_DRIVER(an8811hb) = {\n+\t.name = \"Airoha AN8811HB\",\n+\t.uid = AN8811HB_PHY_ID,\n+\t.mask = 0x0ffffff0,\n+\t.config = &an8811hb_config,\n+\t.probe = &en8811h_probe,\n+\t.read_page = &air_phy_read_page,\n+\t.write_page = &air_phy_write_page,\n \t.startup = &en8811h_startup,\n \t.shutdown = &genphy_shutdown,\n };\n",
    "prefixes": [
        "v2"
    ]
}