get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2163306,
    "url": "http://patchwork.ozlabs.org/api/patches/2163306/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/uboot/patch/20251111000506.18366-1-lucienzx159@gmail.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": "<20251111000506.18366-1-lucienzx159@gmail.com>",
    "list_archive_url": null,
    "date": "2025-11-11T00:05:06",
    "name": "[1/1,U-Boot,v6,1/1] net: phy: Add the Airoha EN8811H PHY driver",
    "commit_ref": "ef896333f799f81a073d03aa5244e5f971b7e6c9",
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "5afa197c499060bb112c9d70a3b7b3fc5a5879b6",
    "submitter": {
        "id": 90627,
        "url": "http://patchwork.ozlabs.org/api/people/90627/?format=api",
        "name": "Lucien.Jheng",
        "email": "lucienzx159@gmail.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/20251111000506.18366-1-lucienzx159@gmail.com/mbox/",
    "series": [
        {
            "id": 481611,
            "url": "http://patchwork.ozlabs.org/api/series/481611/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/uboot/list/?series=481611",
            "date": "2025-11-11T00:05:06",
            "name": "[1/1,U-Boot,v6,1/1] net: phy: Add the Airoha EN8811H PHY driver",
            "version": 6,
            "mbox": "http://patchwork.ozlabs.org/series/481611/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2163306/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2163306/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 (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20230601 header.b=msmQboIm;\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=pass (p=none dis=none) header.from=gmail.com",
            "phobos.denx.de;\n spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de",
            "phobos.denx.de;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.b=\"msmQboIm\";\n\tdkim-atps=neutral",
            "phobos.denx.de;\n dmarc=pass (p=none dis=none) header.from=gmail.com",
            "phobos.denx.de;\n spf=pass smtp.mailfrom=lucienzx159@gmail.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 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4d56PL3v03z1xnT\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 11 Nov 2025 11:10:30 +1100 (AEDT)",
            "from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id 202C183AA0;\n\tTue, 11 Nov 2025 01:10:23 +0100 (CET)",
            "by phobos.denx.de (Postfix, from userid 109)\n id 5B03E83AA9; Tue, 11 Nov 2025 01:06:01 +0100 (CET)",
            "from mail-pg1-x529.google.com (mail-pg1-x529.google.com\n [IPv6:2607:f8b0:4864:20::529])\n (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits))\n (No client certificate requested)\n by phobos.denx.de (Postfix) with ESMTPS id 3AA0783A94\n for <u-boot@lists.denx.de>; Tue, 11 Nov 2025 01:05:58 +0100 (CET)",
            "by mail-pg1-x529.google.com with SMTP id\n 41be03b00d2f7-b6ceb3b68eeso2256277a12.2\n for <u-boot@lists.denx.de>; Mon, 10 Nov 2025 16:05:58 -0800 (PST)",
            "from localhost.localdomain (124-218-201-66.cm.dynamic.apol.com.tw.\n [124.218.201.66]) by smtp.gmail.com with ESMTPSA id\n 41be03b00d2f7-ba9024e6e17sm13594196a12.34.2025.11.10.16.05.51\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 10 Nov 2025 16:05:55 -0800 (PST)"
        ],
        "X-Spam-Checker-Version": "SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de",
        "X-Spam-Level": "",
        "X-Spam-Status": "No, score=-1.8 required=5.0 tests=BAYES_00,DKIM_SIGNED,\n DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_ENVFROM_END_DIGIT,\n FREEMAIL_FROM,RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS\n autolearn=ham autolearn_force=no version=3.4.2",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20230601; t=1762819557; x=1763424357; darn=lists.denx.de;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:from:to:cc:subject:date:message-id:reply-to;\n bh=T3lDq7b8tJojqCQkcsiEhMozdKfG0U+exWSGdvQsmdY=;\n b=msmQboImc+AnsF5p3T5qS5tWX4j4nc9T6YFzTmHfHvJqXk42xWKtB2eGrVhlNdqUEl\n bBALsT7RZ0VKNI0LYZkaBxfmwvAsycALJuQBRlg/hFpsetu2si/TYDi+tBF0mhKYZYJK\n qukZYvSUTKcWhm/5hziWhgawd/Gv2JP1k2uytGX5+hpVNaDxyLxZCGhK9CxpKtdRlvsQ\n VCT/x1k0onBKVTgnRS6+61qphT2QzIb+Hk3awbQ+QJAW0/gv6pD0ZBlEAZ6AcEvjkIxj\n 0DV0qxHGCD7MuarHk2vwICs3OdFxgn8/RnqhajISs8pKEcZQsv26tLIMyZa00IEC4bux\n ME9Q==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1762819557; x=1763424357;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n :message-id:reply-to;\n bh=T3lDq7b8tJojqCQkcsiEhMozdKfG0U+exWSGdvQsmdY=;\n b=AKqYtP6i3xLcydugoR2PsF3wSDfZ6Zuze+aAtw3lOYu6iMeaZU/qv6pdvcYOUvS6LO\n wv7kGTZLADYRs7jXQZfyvVnn+pga410PHkPFLT9UyY/Af3YD8L9pyax4/jfcyfMh8jZO\n ZCqTdbvuzwi7Nqbs73JaKxI3wgthL+lqBdhVMXFby6DHNxCRUAb5B3qgQ6AdreQ7+qKS\n b1kpyzEmM1eROS+HLlQsu5gDF9lcHdkNQrLXkwqDsrLKt48cQ50D9/p8O4kiKpwXvQP7\n Kbo4Zlr7ak29vNCvl7sqFkx7y5qT6pyp5ASw6tHZKKGJ31XtXN1ZyiMX9hhq2fRf+bLw\n cIYg==",
        "X-Forwarded-Encrypted": "i=1;\n AJvYcCWYV3Mg9zk0BxSNevPTLctRgybdPVPOIES6/yQAqBAL8uHcomCIrH98Vrn3wXo6M/8ffoPlGw4=@lists.denx.de",
        "X-Gm-Message-State": "AOJu0YyFBs8aMd++zXseFHE+9SFW8kVF3n9gsfTBh7gIXB230ukA2udE\n zUXGcdKkbNk3vKH84M+hQFn7269MUShlyj1Tje8zn4ADViF1O0Ha86yq",
        "X-Gm-Gg": "ASbGncu8aMh7h+PL8chNIMm6cTLJCGptwZ4gLcKlxotReINMa9lOnEEzV2drWWTF/ww\n 0jUarMH0RHUyVu+qZTB1W4vj2p7f36TCugZVBCBrPHtFw2U9OQxRkSc5s91fwgiOrm230DzYuxL\n anT4JoVhun9XEN0CPMFEIWvRLOj/nKnh+tyrtBAoe754h8JHoEheibYTkKFwqyCyE1XYioQ52oU\n syUKuTAQ8ZwAVI7PIzVv1F+cf0Whoo3bGdBoNcwo5e3H1V55ZqobRD17zq4GXfAxTeJmbDwAtEc\n cVWQsyzXKKq1XhnR8tV7xLTWwbYUzyboWbxRg2YNHEtRxnL1sQ2gXfelgmFT6XSUKpSDAzyNwSQ\n m0EGbiQkyCoxWq/st8wdGhS9pzue+WTLJIRbtzXAc+Wwd5v+Wutrxe7WAReqmyxcMCD65ayXTtO\n bwLKjwu8pK2eWwu5S0rjlZpMyI2Kju1D2c9vhPO7/mWQUUjkP/xhUpsSeObyPBYC6yPPfkaO0=",
        "X-Google-Smtp-Source": "\n AGHT+IFRvtiWaP0ekbjO2gd49a8VnrabjAOaY1d8uAa8zWlxHi8gk8bTD+M7wj8RoJEGoJyLL4cbZg==",
        "X-Received": "by 2002:a17:902:f70a:b0:295:5dbe:f629 with SMTP id\n d9443c01a7336-297e540dd62mr100401215ad.8.1762819556425;\n Mon, 10 Nov 2025 16:05:56 -0800 (PST)",
        "From": "\"Lucien.Jheng\" <lucienzx159@gmail.com>",
        "To": "trini@konsulko.com, joe.hershberger@ni.com,\n marek.vasut+renesas@mailbox.org, rfried.dev@gmail.com,\n jerome.forissier@linaro.org, lucienzx159@gmail.com, ansuelsmth@gmail.com,\n weijie.gao@mediatek.com, SkyLake.Huang@mediatek.com,\n romain.gantois@bootlin.com, s-vadapalli@ti.com, u-boot@lists.denx.de",
        "Cc": "lucien.jheng@airoha.com, frank-w@public-files.de, daniel@makrotopia.org,\n ericwouds@gmail.com",
        "Subject": "[PATCH 1/1] [U-Boot, v6,\n 1/1]net: phy: Add the Airoha EN8811H PHY driver",
        "Date": "Tue, 11 Nov 2025 08:05:06 +0800",
        "Message-Id": "<20251111000506.18366-1-lucienzx159@gmail.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-Mailman-Approved-At": "Tue, 11 Nov 2025 01:10:21 +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 the driver for the Airoha EN8811H 2.5 Gigabit PHY. The PHY supports\n100/1000/2500 Mbps with auto negotiation only.\n\nThe driver uses two firmware files, for which updated versions are added to\nlinux-firmware already.\n\nLocating the AIROHA FW within the filesystem at the designated partition\nand path will trigger its automatic loading and writing to the PHY via MDIO.\nIf need board specific loading override,\nplease override the en8811h_read_fw function on board or architecture level.\n\nBased on the Linux upstream AIROHA EN8811H driver code(air_en8811h.c),\nI have modified the relevant process to align with the U-Boot boot sequence.\nand have validated this on Banana Pi BPI-R3 Mini.\n\nSigned-off-by: Lucien.Jheng <lucienzx159@gmail.com>\n---\nChange in PATCH v6:\nair_en8811.c:\n * Use dev_err() instead of printf() for error messages\n * Use dev_info() instead of printf() for error messages\n * Return actual error code in en8811h_wait_mcu_ready()\n * Fix comment formatting for MII Registers Page 4\n\nChange in PATCH v5:\nair_en8811.c:\n * Add read_page & write_page functions for page access.\n * Change fs_loader to fw_loader.\n * Remove unnecessary debug prints.\n\nChange in PATCH v4:\nair_en8811.c:\n * Create the airoha folder to store all future airoha phy drivers.\n * Use `request_firmware_into_buf_via_script` for firmware loading.\n\nChange in PATCH v3:\nair_en8811.c:\n * Add U-Boot environment variable(en8811h_fw_part, en8811h_fw_dm_dir, en8811h_fw_dsp_dir)\n   support for firmware loading.\n\nRegarding the firmware loading mechanism,\nPlease refer to the following script example for guidance:\n1.  `env set en8811h_load_firmware 'env set en8811h_load_firmware_addr 0x46000000 && env set en8811h_load_firmware_size 0x24000 && mw.b ${en8811h_load_firmware_addr} 0 ${en8811h_load_firmware_size} && load mmc 0:3 0x46000000 /boot/EthMD32.dm.bin && if test ${filesize} -eq 4000; then load mmc 0:3 0x46004000 /boot/EthMD32.DSP.bin; else echo \"loading MD32 FW failed\"; env set en8811h_load_firmware_size 0; exit 1;fi'`\n````Test Log````\nNet:   16384 bytes read in 1 ms (15.6 MiB/s)\n131072 bytes read in 9 ms (13.9 MiB/s)\naddr: 0x46000000, size: 0x24000\nFound Airoha Firmware.\nMD32 firmware version: 24112802\n````End of Test Log````\n\n2.  `env set en8811h_load_firmware 'env set en8811h_load_firmware_addr 0x46000000 && env set en8811h_load_firmware_size 0x24000 && mmc partconf 0 1 2 2 && mmc read $en8811h_load_firmware_addr 0x0 0x120 && mmc partconf 0 1 1 0'`\n````Test Log````\nNet:   MMC read: dev # 0, block # 0, count 288 ... 288 blocks read: OK\naddr: 0x46000000, size: 0x24000\nFound Airoha Firmware.\nMD32 firmware version: 24112802\n````End of Test Log````\n\n drivers/net/phy/Kconfig             |   2 +\n drivers/net/phy/Makefile            |   1 +\n drivers/net/phy/airoha/Kconfig      |  11 +\n drivers/net/phy/airoha/Makefile     |   3 +\n drivers/net/phy/airoha/air_en8811.c | 805 ++++++++++++++++++++++++++++\n 5 files changed, 822 insertions(+)\n create mode 100644 drivers/net/phy/airoha/Kconfig\n create mode 100644 drivers/net/phy/airoha/Makefile\n create mode 100644 drivers/net/phy/airoha/air_en8811.c\n\n--\n2.34.1",
    "diff": "diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig\nindex 018be98705a..b254a0f4591 100644\n--- a/drivers/net/phy/Kconfig\n+++ b/drivers/net/phy/Kconfig\n@@ -188,6 +188,8 @@ config PHY_MARVELL_10G\n\n source \"drivers/net/phy/mediatek/Kconfig\"\n\n+source \"drivers/net/phy/airoha/Kconfig\"\n+\n config PHY_MESON_GXL\n \tbool \"Amlogic Meson GXL Internal PHY support\"\n\ndiff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile\nindex a339b8ac29d..83520de7f1f 100644\n--- a/drivers/net/phy/Makefile\n+++ b/drivers/net/phy/Makefile\n@@ -22,6 +22,7 @@ obj-$(CONFIG_PHY_LXT) += lxt.o\n obj-$(CONFIG_PHY_MARVELL) += marvell.o\n obj-$(CONFIG_PHY_MARVELL_10G) += marvell10g.o\n obj-y += mediatek/\n+obj-y += airoha/\n obj-$(CONFIG_PHY_MICREL_KSZ8XXX) += micrel_ksz8xxx.o\n obj-$(CONFIG_PHY_MICREL_KSZ90X1) += micrel_ksz90x1.o\n obj-$(CONFIG_PHY_MESON_GXL) += meson-gxl.o\ndiff --git a/drivers/net/phy/airoha/Kconfig b/drivers/net/phy/airoha/Kconfig\nnew file mode 100644\nindex 00000000000..999564e4848\n--- /dev/null\n+++ b/drivers/net/phy/airoha/Kconfig\n@@ -0,0 +1,11 @@\n+# SPDX-License-Identifier: GPL-2.0-only\n+menuconfig PHY_AIROHA\n+\tbool \"Airoha Ethernet PHYs support\"\n+\n+config PHY_AIROHA_EN8811\n+\tbool \"Airoha Ethernet EN8811H support\"\n+\tdepends on PHY_AIROHA\n+\tselect FW_LOADER\n+\thelp\n+\t  AIROHA EN8811H supported.\n+\ndiff --git a/drivers/net/phy/airoha/Makefile b/drivers/net/phy/airoha/Makefile\nnew file mode 100644\nindex 00000000000..84d23b19ab0\n--- /dev/null\n+++ b/drivers/net/phy/airoha/Makefile\n@@ -0,0 +1,3 @@\n+# SPDX-License-Identifier: GPL-2.0\n+\n+obj-$(CONFIG_PHY_AIROHA_EN8811) += air_en8811.o\ndiff --git a/drivers/net/phy/airoha/air_en8811.c b/drivers/net/phy/airoha/air_en8811.c\nnew file mode 100644\nindex 00000000000..1a628ede82b\n--- /dev/null\n+++ b/drivers/net/phy/airoha/air_en8811.c\n@@ -0,0 +1,805 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/*\n+ * Driver for the Airoha EN8811H 2.5 Gigabit PHY.\n+ *\n+ * Limitations of the EN8811H:\n+ * - Only full duplex supported\n+ * - Forced speed (AN off) is not supported by hardware (100Mbps)\n+ *\n+ * Source originated from linux air_en8811h.c\n+ *\n+ * Copyright (C) 2025 Airoha Technology Corp.\n+ */\n+\n+#include <phy.h>\n+#include <errno.h>\n+#include <log.h>\n+#include <env.h>\n+#include <malloc.h>\n+#include <fw_loader.h>\n+#include <asm/unaligned.h>\n+#include <linux/iopoll.h>\n+#include <linux/bitops.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_100\t0x4\n+#define AIR_AUX_CTRL_STATUS_SPEED_1000\t0x8\n+#define AIR_AUX_CTRL_STATUS_SPEED_2500\t0xc\n+\n+#define AIR_EXT_PAGE_ACCESS\t\t0x1f\n+#define AIR_PHY_PAGE_STANDARD\t\t0x0000\n+#define AIR_PHY_PAGE_EXTENDED_4\t\t0x0004\n+\n+/* MII Registers Page 4 */\n+#define AIR_BPBUS_MODE\t\t\t0x10\n+#define AIR_BPBUS_MODE_ADDR_FIXED\t0x0000\n+#define AIR_BPBUS_MODE_ADDR_INCR\tBIT(15)\n+#define AIR_BPBUS_WR_ADDR_HIGH\t\t0x11\n+#define AIR_BPBUS_WR_ADDR_LOW\t\t0x12\n+#define AIR_BPBUS_WR_DATA_HIGH\t\t0x13\n+#define AIR_BPBUS_WR_DATA_LOW\t\t0x14\n+#define AIR_BPBUS_RD_ADDR_HIGH\t\t0x15\n+#define AIR_BPBUS_RD_ADDR_LOW\t\t0x16\n+#define AIR_BPBUS_RD_DATA_HIGH\t\t0x17\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+\n+/* Registers on MDIO_MMD_VEND2 */\n+#define AIR_PHY_LED_BCR\t\t\t0x021\n+#define AIR_PHY_LED_BCR_MODE_MASK\tGENMASK(1, 0)\n+#define AIR_PHY_LED_BCR_TIME_TEST\tBIT(2)\n+#define AIR_PHY_LED_BCR_CLK_EN\t\tBIT(3)\n+#define AIR_PHY_LED_BCR_EXT_CTRL\tBIT(15)\n+\n+#define AIR_PHY_LED_DUR_ON\t\t0x022\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_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+#define AIR_PHY_LED_ON_LINK10\t\tBIT(2)\n+#define AIR_PHY_LED_ON_LINKDOWN\t\tBIT(3)\n+#define AIR_PHY_LED_ON_FDX\t\tBIT(4) /* Full duplex */\n+#define AIR_PHY_LED_ON_HDX\t\tBIT(5) /* Half duplex */\n+#define AIR_PHY_LED_ON_FORCE_ON\t\tBIT(6)\n+#define AIR_PHY_LED_ON_LINK2500\t\tBIT(8)\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_1000TX\tBIT(0)\n+#define AIR_PHY_LED_BLINK_1000RX\tBIT(1)\n+#define AIR_PHY_LED_BLINK_100TX\t\tBIT(2)\n+#define AIR_PHY_LED_BLINK_100RX\t\tBIT(3)\n+#define AIR_PHY_LED_BLINK_10TX\t\tBIT(4)\n+#define AIR_PHY_LED_BLINK_10RX\t\tBIT(5)\n+#define AIR_PHY_LED_BLINK_COLLISION\tBIT(6)\n+#define AIR_PHY_LED_BLINK_RX_CRC_ERR\tBIT(7)\n+#define AIR_PHY_LED_BLINK_RX_IDLE_ERR\tBIT(8)\n+#define AIR_PHY_LED_BLINK_FORCE_BLINK\tBIT(9)\n+#define AIR_PHY_LED_BLINK_2500TX\tBIT(10)\n+#define AIR_PHY_LED_BLINK_2500RX\tBIT(11)\n+\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+\n+struct led {\n+\tunsigned long rules;\n+\tunsigned long state;\n+};\n+\n+enum {\n+\tAIR_PHY_LED_STATE_FORCE_ON,\n+\tAIR_PHY_LED_STATE_FORCE_BLINK,\n+};\n+\n+enum {\n+\tAIR_PHY_LED_DUR_BLINK_32MS,\n+\tAIR_PHY_LED_DUR_BLINK_64MS,\n+\tAIR_PHY_LED_DUR_BLINK_128MS,\n+\tAIR_PHY_LED_DUR_BLINK_256MS,\n+\tAIR_PHY_LED_DUR_BLINK_512MS,\n+\tAIR_PHY_LED_DUR_BLINK_1024MS,\n+};\n+\n+enum {\n+\tAIR_LED_DISABLE,\n+\tAIR_LED_ENABLE,\n+};\n+\n+enum {\n+\tAIR_ACTIVE_LOW,\n+\tAIR_ACTIVE_HIGH,\n+};\n+\n+enum {\n+\tAIR_LED_MODE_DISABLE,\n+\tAIR_LED_MODE_USER_DEFINE,\n+};\n+\n+/* Trigger specific enum */\n+enum air_led_trigger_netdev_modes {\n+\tAIR_TRIGGER_NETDEV_LINK = 0,\n+\tAIR_TRIGGER_NETDEV_LINK_10,\n+\tAIR_TRIGGER_NETDEV_LINK_100,\n+\tAIR_TRIGGER_NETDEV_LINK_1000,\n+\tAIR_TRIGGER_NETDEV_LINK_2500,\n+\tAIR_TRIGGER_NETDEV_LINK_5000,\n+\tAIR_TRIGGER_NETDEV_LINK_10000,\n+\tAIR_TRIGGER_NETDEV_HALF_DUPLEX,\n+\tAIR_TRIGGER_NETDEV_FULL_DUPLEX,\n+\tAIR_TRIGGER_NETDEV_TX,\n+\tAIR_TRIGGER_NETDEV_RX,\n+\tAIR_TRIGGER_NETDEV_TX_ERR,\n+\tAIR_TRIGGER_NETDEV_RX_ERR,\n+\n+\t/* Keep last */\n+\t__AIR_TRIGGER_NETDEV_MAX,\n+};\n+\n+/* Default LED setup:\n+ * GPIO5 <-> LED0  On: Link detected, blink Rx/Tx\n+ * GPIO4 <-> LED1  On: Link detected at 2500 and 1000 Mbps\n+ * GPIO3 <-> LED2  On: Link detected at 2500 and  100 Mbps\n+ */\n+#define AIR_DEFAULT_TRIGGER_LED0 (BIT(AIR_TRIGGER_NETDEV_LINK)      | \\\n+\t\t\t\t  BIT(AIR_TRIGGER_NETDEV_RX)        | \\\n+\t\t\t\t  BIT(AIR_TRIGGER_NETDEV_TX))\n+#define AIR_DEFAULT_TRIGGER_LED1 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \\\n+\t\t\t\t  BIT(AIR_TRIGGER_NETDEV_LINK_1000))\n+#define AIR_DEFAULT_TRIGGER_LED2 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \\\n+\t\t\t\t  BIT(AIR_TRIGGER_NETDEV_LINK_100))\n+\n+#define AIR_PHY_LED_DUR_UNIT\t781\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+\tbool\t\tmcu_needs_restart;\n+\tstruct led\tled[EN8811H_LED_COUNT];\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+\tint ret, saved_page;\n+\n+\tsaved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);\n+\tif (saved_page < 0)\n+\t\treturn saved_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,\n+\t\t\tAIR_BPBUS_MODE_ADDR_FIXED);\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,\n+\t\t\tupper_16_bits(pbus_address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,\n+\t\t\tlower_16_bits(pbus_address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,\n+\t\t\tupper_16_bits(pbus_data));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,\n+\t\t\tlower_16_bits(pbus_data));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+restore_page:\n+\tif (ret < 0)\n+\t\tdev_err(phydev->dev, \"%s 0x%08x failed: %d\\n\", __func__,\n+\t\t\tpbus_address, ret);\n+\n+\treturn phy_restore_page(phydev, saved_page, ret);\n+}\n+\n+static int air_buckpbus_reg_read(struct phy_device *phydev,\n+\t\t\t\t u32 pbus_address, u32 *pbus_data)\n+{\n+\tint pbus_data_low, pbus_data_high;\n+\tint ret = 0, saved_page;\n+\n+\tsaved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);\n+\tif (saved_page < 0)\n+\t\treturn saved_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,\n+\t\t\tAIR_BPBUS_MODE_ADDR_FIXED);\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,\n+\t\t\tupper_16_bits(pbus_address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,\n+\t\t\tlower_16_bits(pbus_address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tpbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);\n+\tif (pbus_data_high < 0) {\n+\t\tret = pbus_data_high;\n+\t\tgoto restore_page;\n+\t}\n+\n+\tpbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);\n+\tif (pbus_data_low < 0) {\n+\t\tret = pbus_data_low;\n+\t\tgoto restore_page;\n+\t}\n+\n+\t*pbus_data = pbus_data_low | (pbus_data_high << 16);\n+\n+restore_page:\n+\tif (ret < 0)\n+\t\tdev_err(phydev->dev, \"%s 0x%08x failed: %d\\n\", __func__,\n+\t\t\tpbus_address, ret);\n+\n+\treturn phy_restore_page(phydev, saved_page, ret);\n+}\n+\n+static int air_buckpbus_reg_modify(struct phy_device *phydev,\n+\t\t\t\t   u32 pbus_address, u32 mask, u32 set)\n+{\n+\tint pbus_data_low, pbus_data_high;\n+\tu32 pbus_data_old, pbus_data_new;\n+\tint ret = 0, saved_page;\n+\n+\tsaved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);\n+\tif (saved_page < 0)\n+\t\treturn saved_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,\n+\t\t\tAIR_BPBUS_MODE_ADDR_FIXED);\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,\n+\t\t\tupper_16_bits(pbus_address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,\n+\t\t\tlower_16_bits(pbus_address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tpbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);\n+\tif (pbus_data_high < 0) {\n+\t\tret = pbus_data_high;\n+\t\tgoto restore_page;\n+\t}\n+\n+\tpbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);\n+\tif (pbus_data_low < 0) {\n+\t\tret = pbus_data_low;\n+\t\tgoto restore_page;\n+\t}\n+\n+\tpbus_data_old = pbus_data_low | (pbus_data_high << 16);\n+\tpbus_data_new = (pbus_data_old & ~mask) | set;\n+\tif (pbus_data_new == pbus_data_old)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,\n+\t\t\tupper_16_bits(pbus_address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,\n+\t\t\tlower_16_bits(pbus_address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,\n+\t\t\tupper_16_bits(pbus_data_new));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,\n+\t\t\tlower_16_bits(pbus_data_new));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+restore_page:\n+\tif (ret < 0)\n+\t\tdev_err(phydev->dev, \"%s 0x%08x failed: %d\\n\", __func__,\n+\t\t\tpbus_address, ret);\n+\n+\treturn phy_restore_page(phydev, saved_page, ret);\n+}\n+\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+\tu16 val;\n+\n+\tsaved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);\n+\tif (saved_page < 0)\n+\t\treturn saved_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,\n+\t\t\tAIR_BPBUS_MODE_ADDR_INCR);\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,\n+\t\t\tupper_16_bits(address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,\n+\t\t\tlower_16_bits(address));\n+\tif (ret < 0)\n+\t\tgoto restore_page;\n+\n+\tfor (offset = 0; offset < array_size; offset += 4) {\n+\t\tval = get_unaligned_le16(&buffer[offset + 2]);\n+\t\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, val);\n+\t\tif (ret < 0)\n+\t\t\tgoto restore_page;\n+\n+\t\tval = get_unaligned_le16(&buffer[offset]);\n+\t\tret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, val);\n+\t\tif (ret < 0)\n+\t\t\tgoto restore_page;\n+\t}\n+\n+restore_page:\n+\tif (ret < 0)\n+\t\tdev_err(phydev->dev, \"%s 0x%08lx failed: %d\\n\", __func__,\n+\t\t\taddress, ret);\n+\n+\treturn phy_restore_page(phydev, saved_page, ret);\n+}\n+\n+static int en8811h_wait_mcu_ready(struct phy_device *phydev)\n+{\n+\tint ret, reg_value;\n+\n+\t/* Because of mdio-lock, may have to wait for multiple loads */\n+\tret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,\n+\t\t\t\t\tEN8811H_PHY_FW_STATUS, reg_value,\n+\t\t\t\t\treg_value == EN8811H_PHY_READY,\n+\t\t\t\t\t20000, 7500000, true);\n+\tif (ret) {\n+\t\tdev_err(phydev->dev, \"MCU not ready: 0x%x\\n\", reg_value);\n+\t\treturn ret;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+int en8811h_read_fw(void **fw, size_t *fwsize)\n+{\n+\tvoid *buffer;\n+\tint ret;\n+\n+\tbuffer = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_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+\tif (ret) {\n+\t\tfree(buffer);\n+\t\treturn ret;\n+\t}\n+\n+\t*fw = buffer;\n+\n+\tdebug(\"Found Airoha Firmware.\\n\");\n+\n+\treturn 0;\n+}\n+\n+static int en8811h_load_firmware(struct phy_device *phydev)\n+{\n+\tstruct en8811h_priv *priv = phydev->priv;\n+\tsize_t fw_size;\n+\tvoid *buffer;\n+\tint ret;\n+\n+\tret = en8811h_read_fw(&buffer, &fw_size);\n+\tif (ret < 0) {\n+\t\tdev_err(phydev->dev, \"Failed to get firmware data\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (fw_size != EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE) {\n+\t\tdev_err(phydev->dev,\n+\t\t\t\"MD32 firmware size mismatch (0x%zx != 0x%x)\\n\",\n+\t\t\tfw_size, EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);\n+\t\tret = -EINVAL;\n+\t\tgoto en8811h_load_firmware_out;\n+\t}\n+\n+\tret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,\n+\t\t\t\t     EN8811H_FW_CTRL_1_START);\n+\tif (ret < 0)\n+\t\tgoto en8811h_load_firmware_out;\n+\n+\tret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,\n+\t\t\t\t      EN8811H_FW_CTRL_2_LOADING,\n+\t\t\t\t      EN8811H_FW_CTRL_2_LOADING);\n+\tif (ret < 0)\n+\t\tgoto en8811h_load_firmware_out;\n+\n+\tret = air_write_buf(phydev, AIR_FW_ADDR_DM, EN8811H_MD32_DM_SIZE,\n+\t\t\t    (unsigned char *)buffer);\n+\tif (ret < 0)\n+\t\tgoto en8811h_load_firmware_out;\n+\n+\tret = air_write_buf(phydev, AIR_FW_ADDR_DSP, EN8811H_MD32_DSP_SIZE,\n+\t\t\t    (unsigned char *)buffer + EN8811H_MD32_DM_SIZE);\n+\tif (ret < 0)\n+\t\tgoto en8811h_load_firmware_out;\n+\n+\tret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,\n+\t\t\t\t      EN8811H_FW_CTRL_2_LOADING, 0);\n+\tif (ret < 0)\n+\t\tgoto en8811h_load_firmware_out;\n+\n+\tret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,\n+\t\t\t\t     EN8811H_FW_CTRL_1_FINISH);\n+\tif (ret < 0)\n+\t\tgoto en8811h_load_firmware_out;\n+\n+\tret = en8811h_wait_mcu_ready(phydev);\n+\n+\tair_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,\n+\t\t\t      &priv->firmware_version);\n+\tdev_info(phydev->dev, \"MD32 firmware version: %08x\\n\",\n+\t\t priv->firmware_version);\n+\n+en8811h_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+static int en8811h_restart_mcu(struct phy_device *phydev)\n+{\n+\tint ret;\n+\n+\tret = phy_write_mmd(phydev, 0x1e, 0x8009, 0x0);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,\n+\t\t\t\t     EN8811H_FW_CTRL_1_START);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\treturn air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,\n+\t\t\t\t      EN8811H_FW_CTRL_1_FINISH);\n+}\n+\n+static int air_led_hw_control_set(struct phy_device *phydev, u8 index,\n+\t\t\t\t  unsigned long rules)\n+{\n+\tstruct en8811h_priv *priv = phydev->priv;\n+\tu16 on = 0, blink = 0;\n+\tint ret;\n+\n+\tif (index >= EN8811H_LED_COUNT)\n+\t\treturn -EINVAL;\n+\n+\tpriv->led[index].rules = rules;\n+\n+\tif (rules & BIT(AIR_TRIGGER_NETDEV_FULL_DUPLEX))\n+\t\ton |= AIR_PHY_LED_ON_FDX;\n+\n+\tif (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_10) | BIT(AIR_TRIGGER_NETDEV_LINK)))\n+\t\ton |= AIR_PHY_LED_ON_LINK10;\n+\n+\tif (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_100) | BIT(AIR_TRIGGER_NETDEV_LINK)))\n+\t\ton |= AIR_PHY_LED_ON_LINK100;\n+\n+\tif (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_1000) | BIT(AIR_TRIGGER_NETDEV_LINK)))\n+\t\ton |= AIR_PHY_LED_ON_LINK1000;\n+\n+\tif (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | BIT(AIR_TRIGGER_NETDEV_LINK)))\n+\t\ton |= AIR_PHY_LED_ON_LINK2500;\n+\n+\tif (rules & BIT(AIR_TRIGGER_NETDEV_RX)) {\n+\t\tblink |= AIR_PHY_LED_BLINK_10RX   |\n+\t\t\t AIR_PHY_LED_BLINK_100RX  |\n+\t\t\t AIR_PHY_LED_BLINK_1000RX |\n+\t\t\t AIR_PHY_LED_BLINK_2500RX;\n+\t}\n+\n+\tif (rules & BIT(AIR_TRIGGER_NETDEV_TX)) {\n+\t\tblink |= AIR_PHY_LED_BLINK_10TX   |\n+\t\t\t AIR_PHY_LED_BLINK_100TX  |\n+\t\t\t AIR_PHY_LED_BLINK_1000TX |\n+\t\t\t AIR_PHY_LED_BLINK_2500TX;\n+\t}\n+\n+\tif (blink || on) {\n+\t\t/* switch hw-control on, so led-on and led-blink are off */\n+\t\tclear_bit(AIR_PHY_LED_STATE_FORCE_ON,\n+\t\t\t  &priv->led[index].state);\n+\t\tclear_bit(AIR_PHY_LED_STATE_FORCE_BLINK,\n+\t\t\t  &priv->led[index].state);\n+\t} else {\n+\t\tpriv->led[index].rules = 0;\n+\t}\n+\n+\tret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),\n+\t\t\t     AIR_PHY_LED_ON_MASK, on);\n+\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\treturn phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index),\n+\t\t\t     blink);\n+};\n+\n+static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol)\n+{\n+\tint val = 0;\n+\tint err;\n+\n+\tif (index >= EN8811H_LED_COUNT)\n+\t\treturn -EINVAL;\n+\n+\tif (state == AIR_LED_ENABLE)\n+\t\tval |= AIR_PHY_LED_ON_ENABLE;\n+\telse\n+\t\tval &= ~AIR_PHY_LED_ON_ENABLE;\n+\n+\tif (pol == AIR_ACTIVE_HIGH)\n+\t\tval |= AIR_PHY_LED_ON_POLARITY;\n+\telse\n+\t\tval &= ~AIR_PHY_LED_ON_POLARITY;\n+\n+\terr = phy_write_mmd(phydev, 0x1f, AIR_PHY_LED_ON(index), val);\n+\tif (err < 0)\n+\t\treturn err;\n+\n+\treturn 0;\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+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON,\n+\t\t\t    dur >> 1);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tswitch (mode) {\n+\tcase AIR_LED_MODE_DISABLE:\n+\t\tret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,\n+\t\t\t\t     AIR_PHY_LED_BCR_EXT_CTRL |\n+\t\t\t\t     AIR_PHY_LED_BCR_MODE_MASK, 0);\n+\t\tbreak;\n+\tcase AIR_LED_MODE_USER_DEFINE:\n+\t\tret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,\n+\t\t\t\t     AIR_PHY_LED_BCR_EXT_CTRL |\n+\t\t\t\t     AIR_PHY_LED_BCR_CLK_EN,\n+\t\t\t\t     AIR_PHY_LED_BCR_EXT_CTRL |\n+\t\t\t\t     AIR_PHY_LED_BCR_CLK_EN);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t\tbreak;\n+\tdefault:\n+\t\tdev_err(phydev->dev, \"LED mode %d is not supported\\n\", mode);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tfor (i = 0; i < num; ++i) {\n+\t\tret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH);\n+\t\tif (ret < 0) {\n+\t\t\tdev_err(phydev->dev, \"LED%d init failed: %d\\n\", i, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t\tair_led_hw_control_set(phydev, i, priv->led[i].rules);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int en8811h_config(struct phy_device *phydev)\n+{\n+\tstruct en8811h_priv *priv = phydev->priv;\n+\tofnode node = phy_get_ofnode(phydev);\n+\tu32 pbus_value = 0;\n+\tint ret = 0;\n+\n+\t/* If restart happened in .probe(), no need to restart now */\n+\tif (priv->mcu_needs_restart) {\n+\t\tret = en8811h_restart_mcu(phydev);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t} else {\n+\t\tret = en8811h_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 = phy_write_mmd(phydev, 0x1e, 0x800c, 0x0);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tret = phy_write_mmd(phydev, 0x1e, 0x800d, 0x0);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tret = phy_write_mmd(phydev, 0x1e, 0x800e, 0x1101);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\tret = phy_write_mmd(phydev, 0x1e, 0x800f, 0x0002);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\t/* Serdes polarity */\n+\tpbus_value = 0;\n+\tif (ofnode_read_bool(node, \"airoha,pnswap-rx\"))\n+\t\tpbus_value |=  EN8811H_POLARITY_RX_REVERSE;\n+\telse\n+\t\tpbus_value &= ~EN8811H_POLARITY_RX_REVERSE;\n+\tif (ofnode_read_bool(node, \"airoha,pnswap-tx\"))\n+\t\tpbus_value &= ~EN8811H_POLARITY_TX_NORMAL;\n+\telse\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+\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 en8811h_parse_status(struct phy_device *phydev)\n+{\n+\tint ret = 0, reg_value;\n+\n+\tphydev->duplex = DUPLEX_FULL;\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+\tdefault:\n+\t\tdev_err(phydev->dev, \"Auto-neg error, defaulting to 2500M/FD\\n\");\n+\t\tphydev->speed = SPEED_2500;\n+\t\tbreak;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int en8811h_startup(struct phy_device *phydev)\n+{\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+}\n+\n+static int en8811h_probe(struct phy_device *phydev)\n+{\n+\tstruct en8811h_priv *priv;\n+\n+\tpriv = malloc(sizeof(*priv));\n+\tif (!priv)\n+\t\treturn -ENOMEM;\n+\tmemset(priv, 0, sizeof(*priv));\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+\n+\t/* mcu has just restarted after firmware load */\n+\tpriv->mcu_needs_restart = false;\n+\n+\tphydev->priv = priv;\n+\n+\treturn 0;\n+}\n+\n+static int en8811h_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+{\n+\treturn phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);\n+}\n+\n+U_BOOT_PHY_DRIVER(en8811h) = {\n+\t.name = \"Airoha EN8811H\",\n+\t.uid = EN8811H_PHY_ID,\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.startup = &en8811h_startup,\n+\t.shutdown = &genphy_shutdown,\n+};\n",
    "prefixes": [
        "1/1",
        "U-Boot",
        "v6",
        "1/1"
    ]
}