From nobody Sun Feb 8 21:41:45 2026 Received: from dilbert.mork.no (dilbert.mork.no [65.108.154.246]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9FC00330657; Fri, 23 Jan 2026 07:59:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=65.108.154.246 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769155163; cv=none; b=MKdpHePBSf5+o6XFldC6r4WXjLAXcwloaIf/QL+w22ESX3eAewo3Qqp8yR5R7puaHtCxQyYGgQ3v+1IbPSzD+PVttfI5K7tJXeqbvpdyTVS9zWJZIU2yXMVFPrb16Bu9b6LhOv/PKGZ/MEYbFEKqwzaGZHQSSen/PXpylPOdsaI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769155163; c=relaxed/simple; bh=fTCqFHZj/OY/3Ab1Lck/ssuVgcSHr/dHEJUd9vrOZOk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=hbtiD/qeiks4dVgpM8rCHS9dTprN+XW5vJ1W/Ygs5ppTzq2zCo89sb8hocvl02NTFUQRfbsPUs+vnAIqm3ZGa4EXuLBmMw4C1zOSbf+yKLqsvE2AhqH3ZF2SntamKHxo8CxrtI9+JWEbwU80kI9vNFPNwbCTrQWGQcOKbF+RL0s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mork.no; spf=pass smtp.mailfrom=miraculix.mork.no; dkim=pass (1024-bit key) header.d=mork.no header.i=@mork.no header.b=F9IuqsUw; arc=none smtp.client-ip=65.108.154.246 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mork.no Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=miraculix.mork.no Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mork.no header.i=@mork.no header.b="F9IuqsUw" Authentication-Results: dilbert.mork.no; dkim=pass (1024-bit key; secure) header.d=mork.no header.i=@mork.no header.a=rsa-sha256 header.s=b header.b=F9IuqsUw; dkim-atps=neutral Received: from canardo.dyn.mork.no ([IPv6:2a01:799:10e2:d900:0:0:0:1]) (authenticated bits=0) by dilbert.mork.no (8.18.1/8.18.1) with ESMTPSA id 60N7wfus1303108 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=OK); Fri, 23 Jan 2026 07:58:42 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mork.no; s=b; t=1769155121; bh=35Bf9EwSz+MEQ6C6HQ7TLaDA2eaYXECWAxshTaANixY=; h=From:To:Cc:Subject:Date:Message-ID:References:From; b=F9IuqsUwGjeYZqaguR6oT/RoguyChfaiJblIkP9E+TxE8zO16RQ6XMjaQsCznraSy SPZpIUMGGpmnYxlnCplDY5Q/xY7eTOGQfiLi+teTU/FQeJvto4KsHnILxYhgtc0VVf JIIhnQv8w3AaKdYK3+SEx26TtR2P1Ov+/k5Chqqo= Received: from miraculix.mork.no ([IPv6:2a01:799:10e2:d90a:6f50:7559:681d:630c]) (authenticated bits=0) by canardo.dyn.mork.no (8.18.1/8.18.1) with ESMTPSA id 60N7webn3568538 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=OK); Fri, 23 Jan 2026 08:58:41 +0100 Received: (nullmailer pid 1162098 invoked by uid 1000); Fri, 23 Jan 2026 07:58:40 -0000 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= To: netdev@vger.kernel.org Cc: "Lucien.Jheng" , Daniel Golle , Vladimir Oltean , Andrew Lunn , Heiner Kallweit , Russell King , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , linux-kernel@vger.kernel.org, =?UTF-8?q?Bj=C3=B8rn=20Mork?= Subject: [PATCH net-next v2 1/3] net: phy: air_en8811h: factor out shareable code Date: Fri, 23 Jan 2026 08:58:15 +0100 Message-ID: <20260123075817.1162068-2-bjorn@mork.no> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260123075817.1162068-1-bjorn@mork.no> References: <20260123075817.1162068-1-bjorn@mork.no> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Virus-Scanned: clamav-milter 1.4.3 at canardo.mork.no X-Virus-Status: Clean Create reusable helpers in preparation for the AN8811HB support. Signed-off-by: Bj=C3=B8rn Mork --- drivers/net/phy/air_en8811h.c | 123 +++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 46 deletions(-) diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index e890bb2c0aa8..e617108fe469 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -448,6 +448,11 @@ static int en8811h_wait_mcu_ready(struct phy_device *p= hydev) { int ret, reg_value; =20 + ret =3D air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); + if (ret) + return ret; + /* Because of mdio-lock, may have to wait for multiple loads */ ret =3D phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, EN8811H_PHY_FW_STATUS, reg_value, @@ -461,9 +466,18 @@ static int en8811h_wait_mcu_ready(struct phy_device *p= hydev) return 0; } =20 -static int en8811h_load_firmware(struct phy_device *phydev) +static void en8811h_print_fw_version(struct phy_device *phydev) { struct en8811h_priv *priv =3D phydev->priv; + + air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, + &priv->firmware_version); + phydev_info(phydev, "MD32 firmware version: %08x\n", + priv->firmware_version); +} + +static int en8811h_load_firmware(struct phy_device *phydev) +{ struct device *dev =3D &phydev->mdio.dev; const struct firmware *fw1, *fw2; int ret; @@ -500,17 +514,11 @@ static int en8811h_load_firmware(struct phy_device *p= hydev) if (ret < 0) goto en8811h_load_firmware_out; =20 - ret =3D air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_FINISH); + ret =3D en8811h_wait_mcu_ready(phydev); if (ret < 0) goto en8811h_load_firmware_out; =20 - ret =3D en8811h_wait_mcu_ready(phydev); - - air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, - &priv->firmware_version); - phydev_info(phydev, "MD32 firmware version: %08x\n", - priv->firmware_version); + en8811h_print_fw_version(phydev); =20 en8811h_load_firmware_out: release_firmware(fw2); @@ -533,11 +541,6 @@ static int en8811h_restart_mcu(struct phy_device *phyd= ev) if (ret < 0) return ret; =20 - ret =3D air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_FINISH); - if (ret < 0) - return ret; - return en8811h_wait_mcu_ready(phydev); } =20 @@ -919,6 +922,23 @@ static int en8811h_clk_provider_setup(struct device *d= ev, struct clk_hw *hw) return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); } =20 +static int en8811h_leds_setup(struct phy_device *phydev) +{ + struct en8811h_priv *priv =3D phydev->priv; + int ret; + + priv->led[0].rules =3D AIR_DEFAULT_TRIGGER_LED0; + priv->led[1].rules =3D AIR_DEFAULT_TRIGGER_LED1; + priv->led[2].rules =3D AIR_DEFAULT_TRIGGER_LED2; + + ret =3D air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, + AIR_LED_MODE_DISABLE); + if (ret < 0) + phydev_err(phydev, "Failed to disable leds: %d\n", ret); + + return ret; +} + static int en8811h_probe(struct phy_device *phydev) { struct en8811h_priv *priv; @@ -937,19 +957,12 @@ static int en8811h_probe(struct phy_device *phydev) /* mcu has just restarted after firmware load */ priv->mcu_needs_restart =3D false; =20 - priv->led[0].rules =3D AIR_DEFAULT_TRIGGER_LED0; - priv->led[1].rules =3D AIR_DEFAULT_TRIGGER_LED1; - priv->led[2].rules =3D AIR_DEFAULT_TRIGGER_LED2; - /* MDIO_DEVS1/2 empty, so set mmds_present bits here */ phydev->c45_ids.mmds_present |=3D MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; =20 - ret =3D air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, - AIR_LED_MODE_DISABLE); - if (ret < 0) { - phydev_err(phydev, "Failed to disable leds: %d\n", ret); + ret =3D en8811h_leds_setup(phydev); + if (ret < 0) return ret; - } =20 priv->phydev =3D phydev; /* Co-Clock Output */ @@ -1090,11 +1103,9 @@ static int en8811h_config_aneg(struct phy_device *ph= ydev) return __genphy_config_aneg(phydev, changed); } =20 -static int en8811h_read_status(struct phy_device *phydev) +static int en8811h_get_lpa(struct phy_device *phydev) { - struct en8811h_priv *priv =3D phydev->priv; - u32 pbus_value; - int ret, val; + int ret; =20 ret =3D genphy_update_link(phydev); if (ret) @@ -1112,23 +1123,12 @@ static int en8811h_read_status(struct phy_device *p= hydev) if (ret < 0) return ret; =20 - ret =3D genphy_read_lpa(phydev); - if (ret < 0) - return ret; - - /* Get link partner 2.5GBASE-T ability from vendor register */ - ret =3D air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, &pbus_value); - if (ret < 0) - return ret; - linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, - phydev->lp_advertising, - pbus_value & EN8811H_2P5G_LPA_2P5G); - - if (phydev->autoneg_complete) - phy_resolve_aneg_pause(phydev); + return genphy_read_lpa(phydev); +} =20 - if (!phydev->link) - return 0; +static int en8811h_get_speed(struct phy_device *phydev) +{ + int val; =20 /* Get real speed from vendor register */ val =3D phy_read(phydev, AIR_AUX_CTRL_STATUS); @@ -1146,6 +1146,40 @@ static int en8811h_read_status(struct phy_device *ph= ydev) break; } =20 + /* Only supports full duplex */ + phydev->duplex =3D DUPLEX_FULL; + + return 0; +} + +static int en8811h_read_status(struct phy_device *phydev) +{ + struct en8811h_priv *priv =3D phydev->priv; + u32 pbus_value; + int ret; + + ret =3D en8811h_get_lpa(phydev); + if (ret < 0) + return ret; + + /* Get link partner 2.5GBASE-T ability from vendor register */ + ret =3D air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, &pbus_value); + if (ret < 0) + return ret; + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->lp_advertising, + pbus_value & EN8811H_2P5G_LPA_2P5G); + + if (phydev->autoneg_complete) + phy_resolve_aneg_pause(phydev); + + if (!phydev->link) + return 0; + + ret =3D en8811h_get_speed(phydev); + if (ret < 0) + return ret; + /* Firmware before version 24011202 has no vendor register 2P5G_LPA. * Assume link partner advertised it if connected at 2500Mbps. */ @@ -1155,9 +1189,6 @@ static int en8811h_read_status(struct phy_device *phy= dev) phydev->speed =3D=3D SPEED_2500); } =20 - /* Only supports full duplex */ - phydev->duplex =3D DUPLEX_FULL; - return 0; } =20 --=20 2.47.3 From nobody Sun Feb 8 21:41:45 2026 Received: from dilbert.mork.no (dilbert.mork.no [65.108.154.246]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 69A383195FD; Fri, 23 Jan 2026 07:59:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=65.108.154.246 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769155164; cv=none; b=lxQBhhwYMlNxNrdqtZlplJPhuYRt2ZGQsN84TVkAB2Nw16qYATwXexE7blj/f1MZiGanQmcb7PrNC8CNzuqyrniD6zQMCjgSz3Q9ojQM5DgBc2QuOhr45sV0iic+lbdSDW3tS0BJ53JRmNpZgSPkzIly0uADcOGH7heYvfGZtjs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769155164; c=relaxed/simple; bh=MpUpXyyJ378PU0OAcGQT1HQt+Wb0/oGIqwEBEHNq5M4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=t98/B7Nmnvr3Qyv9OTgP/nYs3vHiNNFO3WGJpuh51FCv/rgGIeHT8cvrBvC9rXhNfs2VrWc/m3eBIlkn+4fuT8r4hz0r6vU05Pi+epLNsMcWzqZX32nHdt4CB/tR4IXC9L2AqYElWdy1Wh/p+crBUG0MqXrCiMVpVNmIvrz/OoA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mork.no; spf=pass smtp.mailfrom=miraculix.mork.no; dkim=pass (1024-bit key) header.d=mork.no header.i=@mork.no header.b=dNlZaqoF; arc=none smtp.client-ip=65.108.154.246 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mork.no Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=miraculix.mork.no Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mork.no header.i=@mork.no header.b="dNlZaqoF" Authentication-Results: dilbert.mork.no; dkim=pass (1024-bit key; secure) header.d=mork.no header.i=@mork.no header.a=rsa-sha256 header.s=b header.b=dNlZaqoF; dkim-atps=neutral Received: from canardo.dyn.mork.no ([IPv6:2a01:799:10e2:d900:0:0:0:1]) (authenticated bits=0) by dilbert.mork.no (8.18.1/8.18.1) with ESMTPSA id 60N7whtO1303111 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=OK); Fri, 23 Jan 2026 07:58:45 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mork.no; s=b; t=1769155123; bh=TMjM6ATA5Np5wf3j8cBF7fNTzFqRkoT+pXytRmTdUIM=; h=From:To:Cc:Subject:Date:Message-ID:References:From; b=dNlZaqoFbudtazDU5kJ2SPDvqQxtZMvLhb0462WJI5/K2jFQqtEou1D8hV6U9BUJq x083Tc554PySSOobt662Ul/hUmL8Hq+c5ZX+4ilJTTbtSfSPYw/JGXI3i7vdV5A/3a QvW9uZlccTeAc2rk4FmYcAqctqTJD/CeKm74ziu0= Received: from miraculix.mork.no ([IPv6:2a01:799:10e2:d90a:6f50:7559:681d:630c]) (authenticated bits=0) by canardo.dyn.mork.no (8.18.1/8.18.1) with ESMTPSA id 60N7wh613568547 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=OK); Fri, 23 Jan 2026 08:58:43 +0100 Received: (nullmailer pid 1162102 invoked by uid 1000); Fri, 23 Jan 2026 07:58:43 -0000 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= To: netdev@vger.kernel.org Cc: "Lucien.Jheng" , Daniel Golle , Vladimir Oltean , Andrew Lunn , Heiner Kallweit , Russell King , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , linux-kernel@vger.kernel.org, =?UTF-8?q?Bj=C3=B8rn=20Mork?= Subject: [PATCH net-next v2 2/3] net: phy: air_en8811h: add Airoha AN8811HB support Date: Fri, 23 Jan 2026 08:58:16 +0100 Message-ID: <20260123075817.1162068-3-bjorn@mork.no> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260123075817.1162068-1-bjorn@mork.no> References: <20260123075817.1162068-1-bjorn@mork.no> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Virus-Scanned: clamav-milter 1.4.3 at canardo.mork.no X-Virus-Status: Clean The Airoha AN8811HB is mostly compatible with the EN8811H, adding 10Base-T support and reducing power consumption. This driver is based on the air_an8811hb v0.0.4 out-of-tree driver written by "Lucien.Jheng " Firmware is available in linux-firmware. The driver has been tested with firmware version 25110702 Signed-off-by: Bj=C3=B8rn Mork --- Cc: Lucien.Jheng Cc: Daniel Golle Cc: Vladimir Oltean --- drivers/net/phy/air_en8811h.c | 281 +++++++++++++++++++++++++++++++++- 1 file changed, 277 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index e617108fe469..ed6a094a4185 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -1,14 +1,15 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Driver for the Airoha EN8811H 2.5 Gigabit PHY. + * Driver for the Airoha EN8811H and AN8811HB 2.5 Gigabit PHYs. * - * Limitations of the EN8811H: + * Limitations: * - Only full duplex supported * - Forced speed (AN off) is not supported by hardware (100Mbps) * * Source originated from airoha's en8811h.c and en8811h.h v1.2.1 + * with AN8811HB bits from air_an8811hb.c v0.0.4 * - * Copyright (C) 2023 Airoha Technology Corp. + * Copyright (C) 2023, 2026 Airoha Technology Corp. */ =20 #include @@ -21,9 +22,12 @@ #include =20 #define EN8811H_PHY_ID 0x03a2a411 +#define AN8811HB_PHY_ID 0xc0ff04a0 =20 #define EN8811H_MD32_DM "airoha/EthMD32.dm.bin" #define EN8811H_MD32_DSP "airoha/EthMD32.DSP.bin" +#define AN8811HB_MD32_DM "airoha/an8811hb/EthMD32_CRC.DM.bin" +#define AN8811HB_MD32_DSP "airoha/an8811hb/EthMD32_CRC.DSP.bin" =20 #define AIR_FW_ADDR_DM 0x00000000 #define AIR_FW_ADDR_DSP 0x00100000 @@ -31,6 +35,7 @@ /* MII Registers */ #define AIR_AUX_CTRL_STATUS 0x1d #define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2) +#define AIR_AUX_CTRL_STATUS_SPEED_10 0x0 #define AIR_AUX_CTRL_STATUS_SPEED_100 0x4 #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc @@ -56,6 +61,7 @@ #define EN8811H_PHY_FW_STATUS 0x8009 #define EN8811H_PHY_READY 0x02 =20 +#define AIR_PHY_MCU_CMD_0 0x800b #define AIR_PHY_MCU_CMD_1 0x800c #define AIR_PHY_MCU_CMD_1_MODE1 0x0 #define AIR_PHY_MCU_CMD_2 0x800d @@ -65,6 +71,10 @@ #define AIR_PHY_MCU_CMD_3_DOCMD 0x1100 #define AIR_PHY_MCU_CMD_4 0x800f #define AIR_PHY_MCU_CMD_4_MODE1 0x0002 +#define AIR_PHY_MCU_CMD_4_CABLE_PAIR_A 0x00d7 +#define AIR_PHY_MCU_CMD_4_CABLE_PAIR_B 0x00d8 +#define AIR_PHY_MCU_CMD_4_CABLE_PAIR_C 0x00d9 +#define AIR_PHY_MCU_CMD_4_CABLE_PAIR_D 0x00da #define AIR_PHY_MCU_CMD_4_INTCLR 0x00e4 =20 /* Registers on MDIO_MMD_VEND2 */ @@ -106,6 +116,9 @@ #define AIR_PHY_LED_BLINK_2500RX BIT(11) =20 /* Registers on BUCKPBUS */ +#define AIR_PHY_CONTROL 0x3a9c +#define AIR_PHY_CONTROL_INTERNAL BIT(11) + #define EN8811H_2P5G_LPA 0x3b30 #define EN8811H_2P5G_LPA_2P5G BIT(0) =20 @@ -129,6 +142,34 @@ #define EN8811H_FW_CTRL_2 0x800000 #define EN8811H_FW_CTRL_2_LOADING BIT(11) =20 +#define AN8811HB_CRC_PM_SET1 0xf020c +#define AN8811HB_CRC_PM_MON2 0xf0218 +#define AN8811HB_CRC_PM_MON3 0xf021c +#define AN8811HB_CRC_DM_SET1 0xf0224 +#define AN8811HB_CRC_DM_MON2 0xf0230 +#define AN8811HB_CRC_DM_MON3 0xf0234 +#define AN8811HB_CRC_RD_EN BIT(0) +#define AN8811HB_CRC_ST (BIT(0) | BIT(1)) +#define AN8811HB_CRC_CHECK_PASS BIT(0) + +#define AN8811HB_TX_POLARITY 0x5ce004 +#define AN8811HB_TX_POLARITY_NORMAL BIT(7) +#define AN8811HB_RX_POLARITY 0x5ce61c +#define AN8811HB_RX_POLARITY_NORMAL BIT(7) + +#define AN8811HB_GPIO_OUTPUT 0x5cf8b8 +#define AN8811HB_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5)) + +#define AN8811HB_HWTRAP1 0x5cf910 +#define AN8811HB_HWTRAP2 0x5cf914 +#define AN8811HB_HWTRAP2_CKO BIT(28) + +#define AN8811HB_CLK_DRV 0x5cf9e4 +#define AN8811HB_CLK_DRV_CKO_MASK GENMASK(14, 12) +#define AN8811HB_CLK_DRV_CKOPWD BIT(12) +#define AN8811HB_CLK_DRV_CKO_LDPWD BIT(13) +#define AN8811HB_CLK_DRV_CKO_LPPWD BIT(14) + /* Led definitions */ #define EN8811H_LED_COUNT 3 =20 @@ -466,6 +507,39 @@ static int en8811h_wait_mcu_ready(struct phy_device *p= hydev) return 0; } =20 +static int an8811hb_check_crc(struct phy_device *phydev, u32 set1, + u32 mon2, u32 mon3) +{ + u32 pbus_value; + int retry =3D 25; + int ret; + + /* Configure CRC */ + ret =3D air_buckpbus_reg_modify(phydev, set1, + AN8811HB_CRC_RD_EN, + AN8811HB_CRC_RD_EN); + if (ret < 0) + return ret; + air_buckpbus_reg_read(phydev, set1, &pbus_value); + + do { + mdelay(300); + air_buckpbus_reg_read(phydev, mon2, &pbus_value); + + if (pbus_value & AN8811HB_CRC_ST) { + air_buckpbus_reg_read(phydev, mon3, &pbus_value); + phydev_dbg(phydev, "CRC Check %s!\n", + pbus_value & AN8811HB_CRC_CHECK_PASS ? + "PASS" : "FAIL"); + return air_buckpbus_reg_modify(phydev, set1, + AN8811HB_CRC_RD_EN, 0); + } + } while (--retry); + + phydev_err(phydev, "CRC Check is not ready (%u)\n", pbus_value); + return -ENODEV; +} + static void en8811h_print_fw_version(struct phy_device *phydev) { struct en8811h_priv *priv =3D phydev->priv; @@ -476,6 +550,63 @@ static void en8811h_print_fw_version(struct phy_device= *phydev) priv->firmware_version); } =20 +static int an8811hb_load_firmware(struct phy_device *phydev) +{ + struct device *dev =3D &phydev->mdio.dev; + const struct firmware *fw1, *fw2; + int ret; + + ret =3D request_firmware_direct(&fw1, AN8811HB_MD32_DM, dev); + if (ret < 0) + return ret; + + ret =3D request_firmware_direct(&fw2, AN8811HB_MD32_DSP, dev); + if (ret < 0) + goto an8811hb_load_firmware_rel1; + + ret =3D air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret =3D air_write_buf(phydev, AIR_FW_ADDR_DM, fw1); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret =3D an8811hb_check_crc(phydev, AN8811HB_CRC_DM_SET1, + AN8811HB_CRC_DM_MON2, + AN8811HB_CRC_DM_MON3); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret =3D air_write_buf(phydev, AIR_FW_ADDR_DSP, fw2); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret =3D an8811hb_check_crc(phydev, AN8811HB_CRC_PM_SET1, + AN8811HB_CRC_PM_MON2, + AN8811HB_CRC_PM_MON3); + if (ret < 0) + goto an8811hb_load_firmware_out; + + ret =3D en8811h_wait_mcu_ready(phydev); + if (ret < 0) + goto an8811hb_load_firmware_out; + + en8811h_print_fw_version(phydev); + +an8811hb_load_firmware_out: + release_firmware(fw2); + +an8811hb_load_firmware_rel1: + release_firmware(fw1); + + if (ret < 0) + phydev_err(phydev, "Load firmware failed: %d\n", ret); + + return ret; +} + static int en8811h_load_firmware(struct phy_device *phydev) { struct device *dev =3D &phydev->mdio.dev; @@ -939,6 +1070,41 @@ static int en8811h_leds_setup(struct phy_device *phyd= ev) return ret; } =20 +static int an8811hb_probe(struct phy_device *phydev) +{ + struct en8811h_priv *priv; + int ret; + + priv =3D devm_kzalloc(&phydev->mdio.dev, sizeof(struct en8811h_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + phydev->priv =3D priv; + + ret =3D an8811hb_load_firmware(phydev); + if (ret < 0) + return ret; + + /* mcu has just restarted after firmware load */ + priv->mcu_needs_restart =3D false; + + /* MDIO_DEVS1/2 empty, so set mmds_present bits here */ + phydev->c45_ids.mmds_present |=3D MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; + + ret =3D en8811h_leds_setup(phydev); + if (ret < 0) + return ret; + + /* Configure led gpio pins as output */ + ret =3D air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT, + AN8811HB_GPIO_OUTPUT_345, + AN8811HB_GPIO_OUTPUT_345); + if (ret < 0) + return ret; + + return 0; +} + static int en8811h_probe(struct phy_device *phydev) { struct en8811h_priv *priv; @@ -980,6 +1146,37 @@ static int en8811h_probe(struct phy_device *phydev) return 0; } =20 +static int an8811hb_config_serdes_polarity(struct phy_device *phydev) +{ + struct device *dev =3D &phydev->mdio.dev; + u32 pbus_value =3D 0; + unsigned int pol; + int ret; + + ret =3D phy_get_manual_rx_polarity(dev_fwnode(dev), + phy_modes(phydev->interface), &pol); + if (ret) + return ret; + if (pol =3D=3D PHY_POL_NORMAL) + pbus_value |=3D AN8811HB_RX_POLARITY_NORMAL; + ret =3D air_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY, + AN8811HB_RX_POLARITY_NORMAL, + pbus_value); + if (ret < 0) + return ret; + + ret =3D phy_get_manual_tx_polarity(dev_fwnode(dev), + phy_modes(phydev->interface), &pol); + if (ret) + return ret; + pbus_value =3D 0; + if (pol =3D=3D PHY_POL_NORMAL) + pbus_value |=3D AN8811HB_TX_POLARITY_NORMAL; + return air_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY, + AN8811HB_TX_POLARITY_NORMAL, + pbus_value); +} + static int en8811h_config_serdes_polarity(struct phy_device *phydev) { struct device *dev =3D &phydev->mdio.dev; @@ -1016,6 +1213,33 @@ static int en8811h_config_serdes_polarity(struct phy= _device *phydev) EN8811H_POLARITY_TX_NORMAL, pbus_value); } =20 +static int an8811hb_config_init(struct phy_device *phydev) +{ + struct en8811h_priv *priv =3D phydev->priv; + int ret; + + /* If restart happened in .probe(), no need to restart now */ + if (priv->mcu_needs_restart) { + ret =3D en8811h_restart_mcu(phydev); + if (ret < 0) + return ret; + } else { + /* Next calls to .config_init() mcu needs to restart */ + priv->mcu_needs_restart =3D true; + } + + ret =3D an8811hb_config_serdes_polarity(phydev); + if (ret < 0) + return ret; + + ret =3D air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR, + AIR_LED_MODE_USER_DEFINE); + if (ret < 0) + phydev_err(phydev, "Failed to initialize leds: %d\n", ret); + + return ret; +} + static int en8811h_config_init(struct phy_device *phydev) { struct en8811h_priv *priv =3D phydev->priv; @@ -1144,6 +1368,9 @@ static int en8811h_get_speed(struct phy_device *phyde= v) case AIR_AUX_CTRL_STATUS_SPEED_100: phydev->speed =3D SPEED_100; break; + case AIR_AUX_CTRL_STATUS_SPEED_10: + phydev->speed =3D SPEED_10; + break; } =20 /* Only supports full duplex */ @@ -1152,6 +1379,30 @@ static int en8811h_get_speed(struct phy_device *phyd= ev) return 0; } =20 +static int an8811hb_read_status(struct phy_device *phydev) +{ + int ret, val; + + ret =3D en8811h_get_lpa(phydev); + if (ret < 0) + return ret; + + if (phydev->autoneg =3D=3D AUTONEG_ENABLE && phydev->autoneg_complete) { + val =3D phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT); + if (val < 0) + return val; + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->lp_advertising, + val & MDIO_AN_10GBT_STAT_LP2_5G); + phy_resolve_aneg_pause(phydev); + } + + if (!phydev->link) + return 0; + + return en8811h_get_speed(phydev); +} + static int en8811h_read_status(struct phy_device *phydev) { struct en8811h_priv *priv =3D phydev->priv; @@ -1259,20 +1510,42 @@ static struct phy_driver en8811h_driver[] =3D { .led_brightness_set =3D air_led_brightness_set, .led_hw_control_set =3D air_led_hw_control_set, .led_hw_control_get =3D air_led_hw_control_get, +}, +{ + PHY_ID_MATCH_MODEL(AN8811HB_PHY_ID), + .name =3D "Airoha AN8811HB", + .probe =3D an8811hb_probe, + .get_features =3D en8811h_get_features, + .config_init =3D an8811hb_config_init, + .get_rate_matching =3D en8811h_get_rate_matching, + .config_aneg =3D en8811h_config_aneg, + .read_status =3D an8811hb_read_status, + .config_intr =3D en8811h_clear_intr, + .handle_interrupt =3D en8811h_handle_interrupt, + .led_hw_is_supported =3D en8811h_led_hw_is_supported, + .read_page =3D air_phy_read_page, + .write_page =3D air_phy_write_page, + .led_blink_set =3D air_led_blink_set, + .led_brightness_set =3D air_led_brightness_set, + .led_hw_control_set =3D air_led_hw_control_set, + .led_hw_control_get =3D air_led_hw_control_get, } }; =20 module_phy_driver(en8811h_driver); =20 static const struct mdio_device_id __maybe_unused en8811h_tbl[] =3D { { PHY_ID_MATCH_MODEL(EN8811H_PHY_ID) }, + { PHY_ID_MATCH_MODEL(AN8811HB_PHY_ID) }, { } }; =20 MODULE_DEVICE_TABLE(mdio, en8811h_tbl); MODULE_FIRMWARE(EN8811H_MD32_DM); MODULE_FIRMWARE(EN8811H_MD32_DSP); +MODULE_FIRMWARE(AN8811HB_MD32_DM); +MODULE_FIRMWARE(AN8811HB_MD32_DSP); =20 -MODULE_DESCRIPTION("Airoha EN8811H PHY drivers"); +MODULE_DESCRIPTION("Airoha EN8811H and AN8811HB PHY drivers"); MODULE_AUTHOR("Airoha"); MODULE_AUTHOR("Eric Woudstra "); MODULE_LICENSE("GPL"); --=20 2.47.3 From nobody Sun Feb 8 21:41:45 2026 Received: from dilbert.mork.no (dilbert.mork.no [65.108.154.246]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8953B33F8A1; Fri, 23 Jan 2026 07:59:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=65.108.154.246 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769155165; cv=none; b=jkBL+pGwJ4MeyQMm0a+TYv40LNXpyRGoiAjy/PStEBF7tYsqYu1u7QYenS5pZTnREVpieGixuGExbO6A/0IoNf6qNesZQIWAuKUgiz7+xrpRnRq9u4imGRIBEXlZ4sIzgMtlsk3zS4+9Q/fw/DaOBNHXS8et9MvdXREsOon4REQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769155165; c=relaxed/simple; bh=bHXbzE/+Yo/JNhbG2NsN6vtPGpV6kxPkRR46z0DdXrs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=akS/N11nSIVzsbKU/uQCkHRU/YsKaZR6OHllKrJ8QHn2scbKQosxDPaR/T6kn8br957FHCBp2+2RkrhqjdDitU7+MG/Zc4H4dEftJpt7Yuj/JKj2G61a7FCMlRpjf7b3KmTINRS6VDlfl3HxYkR8gtRxvObfy3tOCmQop9kISeI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mork.no; spf=pass smtp.mailfrom=miraculix.mork.no; dkim=pass (1024-bit key) header.d=mork.no header.i=@mork.no header.b=g65kkIZb; arc=none smtp.client-ip=65.108.154.246 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=mork.no Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=miraculix.mork.no Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=mork.no header.i=@mork.no header.b="g65kkIZb" Authentication-Results: dilbert.mork.no; dkim=pass (1024-bit key; secure) header.d=mork.no header.i=@mork.no header.a=rsa-sha256 header.s=b header.b=g65kkIZb; dkim-atps=neutral Received: from canardo.dyn.mork.no ([IPv6:2a01:799:10e2:d900:0:0:0:1]) (authenticated bits=0) by dilbert.mork.no (8.18.1/8.18.1) with ESMTPSA id 60N7wmTo1303116 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=OK); Fri, 23 Jan 2026 07:58:49 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mork.no; s=b; t=1769155128; bh=uykuFO/98bhDIWNo7fPSo8egYnLMoJznMpVSVmQyKgo=; h=From:To:Cc:Subject:Date:Message-ID:References:From; b=g65kkIZbMbgpgquWZnY38I8LPNlsCGl9G5KddQ0yoxkD0/pdixUmsH0xec91IlVrT /HYMaYo1vpMmpD0H4UlzU/vLThIEMCBQGwhuUzlbP4/71gVwJWyoP8WbcxlvsF3Bvc Ry8UQubBj1n8OHPMqpbVdtYFXJyBusUWMPWprwdI= Received: from miraculix.mork.no ([IPv6:2a01:799:10e2:d90a:6f50:7559:681d:630c]) (authenticated bits=0) by canardo.dyn.mork.no (8.18.1/8.18.1) with ESMTPSA id 60N7wmnN3568556 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=OK); Fri, 23 Jan 2026 08:58:48 +0100 Received: (nullmailer pid 1162109 invoked by uid 1000); Fri, 23 Jan 2026 07:58:48 -0000 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= To: netdev@vger.kernel.org Cc: "Lucien.Jheng" , Daniel Golle , Vladimir Oltean , Andrew Lunn , Heiner Kallweit , Russell King , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , linux-kernel@vger.kernel.org, =?UTF-8?q?Bj=C3=B8rn=20Mork?= Subject: [PATCH net-next v2 3/3] net: phy: air_en8811h: Add clk provider for an8811hb Date: Fri, 23 Jan 2026 08:58:17 +0100 Message-ID: <20260123075817.1162068-4-bjorn@mork.no> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260123075817.1162068-1-bjorn@mork.no> References: <20260123075817.1162068-1-bjorn@mork.no> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Virus-Scanned: clamav-milter 1.4.3 at canardo.mork.no X-Virus-Status: Clean Implement clk provider driver so we can disable the clock output when it isn't needed. This helps to reduce EMF noise Signed-off-by: Bj=C3=B8rn Mork --- drivers/net/phy/air_en8811h.c | 107 ++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index ed6a094a4185..e664cbc2f1ed 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -954,6 +954,105 @@ static int en8811h_led_hw_is_supported(struct phy_dev= ice *phydev, u8 index, return 0; }; =20 +static unsigned long an8811hb_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent) +{ + struct en8811h_priv *priv =3D clk_hw_to_en8811h_priv(hw); + struct phy_device *phydev =3D priv->phydev; + u32 pbus_value; + int ret; + + ret =3D air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value); + if (ret < 0) + return ret; + + return (pbus_value & AN8811HB_HWTRAP2_CKO) ? 50000000 : 25000000; +} + +static int an8811hb_clk_enable(struct clk_hw *hw) +{ + struct en8811h_priv *priv =3D clk_hw_to_en8811h_priv(hw); + struct phy_device *phydev =3D priv->phydev; + + return air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, + AN8811HB_CLK_DRV_CKO_MASK, + AN8811HB_CLK_DRV_CKO_MASK); +} + +static void an8811hb_clk_disable(struct clk_hw *hw) +{ + struct en8811h_priv *priv =3D clk_hw_to_en8811h_priv(hw); + struct phy_device *phydev =3D priv->phydev; + + air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, + AN8811HB_CLK_DRV_CKO_MASK, 0); +} + +static int an8811hb_clk_is_enabled(struct clk_hw *hw) +{ + struct en8811h_priv *priv =3D clk_hw_to_en8811h_priv(hw); + struct phy_device *phydev =3D priv->phydev; + u32 pbus_value; + int ret; + + ret =3D air_buckpbus_reg_read(phydev, AN8811HB_CLK_DRV, &pbus_value); + if (ret < 0) + return ret; + + return (pbus_value & AN8811HB_CLK_DRV_CKO_MASK); +} + +static int an8811hb_clk_save_context(struct clk_hw *hw) +{ + struct en8811h_priv *priv =3D clk_hw_to_en8811h_priv(hw); + + priv->cko_is_enabled =3D an8811hb_clk_is_enabled(hw); + + return 0; +} + +static void an8811hb_clk_restore_context(struct clk_hw *hw) +{ + struct en8811h_priv *priv =3D clk_hw_to_en8811h_priv(hw); + + if (!priv->cko_is_enabled) + an8811hb_clk_disable(hw); +} + +static const struct clk_ops an8811hb_clk_ops =3D { + .recalc_rate =3D an8811hb_clk_recalc_rate, + .enable =3D an8811hb_clk_enable, + .disable =3D an8811hb_clk_disable, + .is_enabled =3D an8811hb_clk_is_enabled, + .save_context =3D an8811hb_clk_save_context, + .restore_context =3D an8811hb_clk_restore_context, +}; + +static int an8811hb_clk_provider_setup(struct device *dev, struct clk_hw *= hw) +{ + struct clk_init_data init; + int ret; + + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; + + init.name =3D devm_kasprintf(dev, GFP_KERNEL, "%s-cko", + fwnode_get_name(dev_fwnode(dev))); + if (!init.name) + return -ENOMEM; + + init.ops =3D &an8811hb_clk_ops; + init.flags =3D 0; + init.num_parents =3D 0; + hw->init =3D &init; + + ret =3D devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); +} + static unsigned long en8811h_clk_recalc_rate(struct clk_hw *hw, unsigned long parent) { @@ -1095,6 +1194,12 @@ static int an8811hb_probe(struct phy_device *phydev) if (ret < 0) return ret; =20 + priv->phydev =3D phydev; + /* Co-Clock Output */ + ret =3D an8811hb_clk_provider_setup(&phydev->mdio.dev, &priv->hw); + if (ret) + return ret; + /* Configure led gpio pins as output */ ret =3D air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT, AN8811HB_GPIO_OUTPUT_345, @@ -1520,6 +1625,8 @@ static struct phy_driver en8811h_driver[] =3D { .get_rate_matching =3D en8811h_get_rate_matching, .config_aneg =3D en8811h_config_aneg, .read_status =3D an8811hb_read_status, + .resume =3D en8811h_resume, + .suspend =3D en8811h_suspend, .config_intr =3D en8811h_clear_intr, .handle_interrupt =3D en8811h_handle_interrupt, .led_hw_is_supported =3D en8811h_led_hw_is_supported, --=20 2.47.3