From nobody Thu Apr 2 22:17:53 2026 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (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 22CF1397683; Thu, 26 Mar 2026 12:05:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774526734; cv=none; b=mT4ML8wixSUskfL+31cWP8kMz3jF5fC9Rtja6KlGpEzPUdPbsCey5KRCQsqcyvrOdhxYCPTcsa2W2L0opOQZwATBTORtk/aGwDsEsLQ+/BPh2gyZhuFz2UaBo9OA7/BHcoZ6ch9h6/XRYzfqvIqChyEvKfSRUMUB9xfzCeem/lA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774526734; c=relaxed/simple; bh=Zgf20bu0xGzHqUFQHCNtBUZtwRdwhJSQCkmmV2fc+ys=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=dfOYDGhECupUaaxL8m/NJ5ZuJOAgo6afMXKJ7iezFrFQ7aF/qxAQhOHDXDIV138Hsv5Ex1E6J6OzGftVkWOGqQsokn0DtPMzfejiQuHqyB7zAoe+K65u6eQX3tIg12eLbLOlYmY1xMWpowIAPDECnn5eBdTd6sIza6giyWs5tKM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=ZGbUBS5J; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="ZGbUBS5J" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1774526731; bh=Zgf20bu0xGzHqUFQHCNtBUZtwRdwhJSQCkmmV2fc+ys=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ZGbUBS5JiTuFi7VBT2/pW5362F8o0aMKire94QYUJY/NQgk9COoAQp47Y3DmioUVy UcXlgGWd+Rx4IJFjKE05TarlhL2GtHYMeUrr1TcESsDvQK/lD3/PZgZ8CFsVg4Fppy inDIUapqFS6xkKIllL1AfE7dl9LswO9OeHYczFztAE+vnlpBzGQxu54WEX0Xw8F57V Fdl8XE0K4cC6LjevWUloVYuKwJQX/1jVDyqKRzmHF5Hb5XsqeFEre1oNJVTdDyijcZ aK9666e06IIgHK5XYla7IN0ZcCcTgniJLIvdiV32qCWfy7Y/8sGQmif135m5iVB6wk r5suKlMf6K4gw== Received: from yukiji.home (lfbn-idf1-1-2269-27.w92-151.abo.wanadoo.fr [92.151.67.27]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: laeyraud) by bali.collaboradmins.com (Postfix) with ESMTPSA id DDD6B17E6082; Thu, 26 Mar 2026 13:05:30 +0100 (CET) From: Louis-Alexis Eyraud Date: Thu, 26 Mar 2026 13:04:12 +0100 Subject: [PATCH net-next v2 1/4] dt-bindings: net: Add support for Airoha AN8801/R GbE PHY 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 Message-Id: <20260326-add-airoha-an8801-support-v2-1-1a42d6b6050f@collabora.com> References: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com> In-Reply-To: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com> To: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , AngeloGioacchino Del Regno , Andrew Lunn , Heiner Kallweit , Russell King Cc: kevin-kw.huang@airoha.com, macpaul.lin@mediatek.com, matthias.bgg@gmail.com, kernel@collabora.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org, Louis-Alexis Eyraud X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1774526729; l=3223; i=louisalexis.eyraud@collabora.com; s=20250113; h=from:subject:message-id; bh=FA5AQZRyCjhwT8o7QRGwdmO6VLOKQrbpn6fhImI/4Og=; b=cA3GQztJxtw0FPttw+KwoD4r9hKjNYsOOFsNSP9sD/iJLAaWXOrkWzaLWrZmTzB3qAu/BJXCo 9oB3+Sy2ceSCnyWXAXLLwcafvIXQf8m1C/n9PGK8lmDhmQHri7VX5jV X-Developer-Key: i=louisalexis.eyraud@collabora.com; a=ed25519; pk=CHFBDB2Kqh4EHc6JIqFn69GhxJJAzc0Zr4e8QxtumuM= From: AngeloGioacchino Del Regno Add a new binding to support the Airoha AN8801R Series Gigabit Ethernet PHY. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Rob Herring (Arm) Signed-off-by: Louis-Alexis Eyraud --- .../devicetree/bindings/net/airoha,an8801.yaml | 85 ++++++++++++++++++= ++++ 1 file changed, 85 insertions(+) diff --git a/Documentation/devicetree/bindings/net/airoha,an8801.yaml b/Doc= umentation/devicetree/bindings/net/airoha,an8801.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b90b21b5505367309b5df8ece54= ea38664f6b50f --- /dev/null +++ b/Documentation/devicetree/bindings/net/airoha,an8801.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/airoha,an8801.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha AN8801R Series PHY + +maintainers: + - AngeloGioacchino Del Regno + +description: + The Airoha AN8801R is a low power single-port Ethernet PHY Transceiver + with Single-port serdes interface for 1000Base-X/RGMII; this chip is + compliant with 10Base-T, 100Base-TX and 1000Base-T IEEE 802.3(u,ab) + and supports Energy Efficient Ethernet (802.3az), Full Duplex Control + Flow (802.3x), auto-negotiation, crossover detect and autocorrection, + Wake-on-LAN with Magic Packet, and Jumbo Frame up to 9 Kilobytes. + This PHY also supports up to three user-configurable LEDs, which are + usually used for LAN Activity, 100M, 1000M indication. + +allOf: + - $ref: ethernet-phy.yaml# + +properties: + compatible: + enum: + - ethernet-phy-idc0ff.0421 + + reg: + maxItems: 1 + + leds: true + + wakeup-source: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable Wake-on-LAN support + +required: + - reg + +unevaluatedProperties: false + +examples: + - | + #include + + mdio { + #address-cells =3D <1>; + #size-cells =3D <0>; + + ethernet-phy@0 { + compatible =3D "ethernet-phy-idc0ff.0421"; + reg =3D <0>; + + leds { + #address-cells =3D <1>; + #size-cells =3D <0>; + + led@0 { + reg =3D <0>; + color =3D ; + function =3D LED_FUNCTION_LAN; + default-state =3D "keep"; + }; + + led@1 { + reg =3D <1>; + color =3D ; + function =3D LED_FUNCTION_LAN; + function-enumerator =3D <1>; + default-state =3D "keep"; + }; + + led@2 { + reg =3D <2>; + color =3D ; + function =3D LED_FUNCTION_LAN; + function-enumerator =3D <2>; + default-state =3D "keep"; + }; + }; + }; + }; --=20 2.53.0 From nobody Thu Apr 2 22:17:53 2026 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (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 2A92C3EF0AF; Thu, 26 Mar 2026 12:05:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774526735; cv=none; b=VkOosLs1tpfcxk+67Gu8IpXU+GQpqXGe+lYkMVN5//0KwueCD49zoZaQvno4Y7otTlV0oVg8yCYdCnwR6EAHbgaBhidVcDLsoxnatUFIF8+M1/tFMM2GnUaOPrJUK9+BNWAkLDaJHz5tOOrO/wiVUecjAq/OjZpy/vO01xC1D+o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774526735; c=relaxed/simple; bh=NihN73nX4Vo6b33p6sv55lawetyANmkKtj9GSk6TAPU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=uyvPRtdtRwsVJC0NxguNiS6+azAmOmusAm/huSNdkIiCZnaaCEb3m2vpfApcugX07H5LYJDUda/8IL/IwcCRAeCAEzpt8szh3aj32qQqM2OG+1DHQnoILqQeU4Z0JMJ77F1Y3cJIDJX6nAofW/A1V0dCIueo0HJIu5cYtH/8/p4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=JIKVgHPp; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="JIKVgHPp" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1774526732; bh=NihN73nX4Vo6b33p6sv55lawetyANmkKtj9GSk6TAPU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=JIKVgHPpvBaQswcqC7BG0fi+ByVeAo4SNGX7S3ABsYlx4aI1C3oONp1ZTocwS66BM HwyoorwLvXFKS1S2wKCJK2o4lU7rHd85r9ehGMiM7+i+FB1aYG/MebFJJNk6zf4IcI evHL+GjUs0EAOOBl4r7vfKv97VH4hL1xZkrSCTXKmfGyQfeyt9m94NqNjcV+v/VYlG e4K8K7gMxvyFrEOJqmR5yxlFnmfzxJr85XXvm5hKxGleM/Wr7aVk8l8ub4IELG3VHe +O1yTbC4pEqR0yqz1GsMuRTqNYNbbKv8SLxQf9x4oWJmsHhvHCYG07/mwHJXx34SdM m7+y+VhP6+NOg== Received: from yukiji.home (lfbn-idf1-1-2269-27.w92-151.abo.wanadoo.fr [92.151.67.27]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: laeyraud) by bali.collaboradmins.com (Postfix) with ESMTPSA id D878517E60BF; Thu, 26 Mar 2026 13:05:31 +0100 (CET) From: Louis-Alexis Eyraud Date: Thu, 26 Mar 2026 13:04:13 +0100 Subject: [PATCH net-next v2 2/4] net: phy: Add Airoha phy library for shared code 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 Message-Id: <20260326-add-airoha-an8801-support-v2-2-1a42d6b6050f@collabora.com> References: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com> In-Reply-To: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com> To: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , AngeloGioacchino Del Regno , Andrew Lunn , Heiner Kallweit , Russell King Cc: kevin-kw.huang@airoha.com, macpaul.lin@mediatek.com, matthias.bgg@gmail.com, kernel@collabora.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org, Louis-Alexis Eyraud X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1774526729; l=4772; i=louisalexis.eyraud@collabora.com; s=20250113; h=from:subject:message-id; bh=NihN73nX4Vo6b33p6sv55lawetyANmkKtj9GSk6TAPU=; b=vTFeCSNE/LeYsMxRsqGBiAJjhzyloUxyF6nTfkp2mFJLedPhcK5la7iyrFSJOfJ/+6un9NoXP hDLh2FYhYcgCbdiUKBAYcCbnAN+ZFZ53bivo4UQsKSsf74fGph0+ACj X-Developer-Key: i=louisalexis.eyraud@collabora.com; a=ed25519; pk=CHFBDB2Kqh4EHc6JIqFn69GhxJJAzc0Zr4e8QxtumuM= In preparation of Airoha AN8801R PHY support, split out the interface functions that will be common between the already present air_en8811h driver and the new one, and put them into a new library named air_phy_lib. Signed-off-by: Louis-Alexis Eyraud --- drivers/net/phy/Kconfig | 6 ++++++ drivers/net/phy/Makefile | 1 + drivers/net/phy/air_en8811h.c | 13 ++----------- drivers/net/phy/air_phy_lib.c | 30 ++++++++++++++++++++++++++++++ drivers/net/phy/air_phy_lib.h | 14 ++++++++++++++ 5 files changed, 53 insertions(+), 11 deletions(-) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index b5ee338b620d53980fbec9e83ab0de3d96ab4cc9..b6b1cde7e51fa1e470bfe210c87= 64a193449acb5 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -92,10 +92,16 @@ config AS21XXX_PHY =20 config AIR_EN8811H_PHY tristate "Airoha EN8811H 2.5 Gigabit PHY" + select AIR_NET_PHYLIB select PHY_COMMON_PROPS help Currently supports the Airoha EN8811H PHY. =20 +config AIR_NET_PHYLIB + tristate + help + Airoha Ethernet PHY common library + config AMD_PHY tristate "AMD and Altima PHYs" help diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 05e4878af27abeae3dfd9ab18fd29f8bf788b2a4..7cf1fa9e12cb6073a9e68c0ffa6= 284b361b80487 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -30,6 +30,7 @@ obj-y +=3D $(sfp-obj-y) $(sfp-obj-m) obj-$(CONFIG_ADIN_PHY) +=3D adin.o obj-$(CONFIG_ADIN1100_PHY) +=3D adin1100.o obj-$(CONFIG_AIR_EN8811H_PHY) +=3D air_en8811h.o +obj-$(CONFIG_AIR_NET_PHYLIB) +=3D air_phy_lib.o obj-$(CONFIG_AMD_PHY) +=3D amd.o obj-$(CONFIG_AMCC_QT2025_PHY) +=3D qt2025.o obj-$(CONFIG_AQUANTIA_PHY) +=3D aquantia/ diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index 29ae73e65caaa9cdebe2253b5349aa6c7478dc85..be7c3426182a26fe3799b875827= 750e7772caadd 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -21,6 +21,8 @@ #include #include =20 +#include "air_phy_lib.h" + #define EN8811H_PHY_ID 0x03a2a411 #define AN8811HB_PHY_ID 0xc0ff04a0 =20 @@ -40,7 +42,6 @@ #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc =20 -#define AIR_EXT_PAGE_ACCESS 0x1f #define AIR_PHY_PAGE_STANDARD 0x0000 #define AIR_PHY_PAGE_EXTENDED_4 0x0004 =20 @@ -244,16 +245,6 @@ static const unsigned long en8811h_led_trig =3D BIT(TR= IGGER_NETDEV_FULL_DUPLEX) | BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX); =20 -static int air_phy_read_page(struct phy_device *phydev) -{ - return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); -} - -static int air_phy_write_page(struct phy_device *phydev, int page) -{ - return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page); -} - static int __air_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address, u32 pbus_data) { diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c new file mode 100644 index 0000000000000000000000000000000000000000..04c4719a073f98ef75eabd54c48= 51f6a16391350 --- /dev/null +++ b/drivers/net/phy/air_phy_lib.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Airoha Ethernet PHY common library + * + * Copyright (C) 2026 Airoha Technology Corp. + * Copyright (C) 2026 Collabora Ltd. + * Louis-Alexis Eyraud + */ + +#include + +#include "air_phy_lib.h" + +#define AIR_EXT_PAGE_ACCESS 0x1f + +int air_phy_read_page(struct phy_device *phydev) +{ + return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); +} +EXPORT_SYMBOL_GPL(air_phy_read_page); + +int air_phy_write_page(struct phy_device *phydev, int page) +{ + return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page); +} +EXPORT_SYMBOL_GPL(air_phy_write_page); + +MODULE_DESCRIPTION("Airoha PHY Library"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Louis-Alexis Eyraud"); diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h new file mode 100644 index 0000000000000000000000000000000000000000..dd501b175a3a38e57744f79571e= b1bc4ef46fdf5 --- /dev/null +++ b/drivers/net/phy/air_phy_lib.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2026 Airoha Technology Corp. + * Copyright (C) 2026 Collabora Ltd. + * Louis-Alexis Eyraud + */ + +#ifndef __AIR_PHY_LIB_H +#define __AIR_PHY_LIB_H + +int air_phy_read_page(struct phy_device *phydev); +int air_phy_write_page(struct phy_device *phydev, int page); + +#endif /* __AIR_PHY_LIB_H */ --=20 2.53.0 From nobody Thu Apr 2 22:17:53 2026 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (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 28F5A3921DC; Thu, 26 Mar 2026 12:05:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774526737; cv=none; b=gt42SpdlFcNnOrsG1tPZSIca0/ddbCNmMKpU3zgaTb+swtDyaUc6JTaBKSq70fbS4KYTX86P7NR7ECljfgrbnTDqrfRI6MW+B7J7EMidMAc88xzPUUMopqqztM/fI48fT1FME4F1pN+seNliYlTtg4Z/VPCxeZwgeIfqDe9n9cE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774526737; c=relaxed/simple; bh=yoTbL7Lk5kFWSSwVOfmBihcqssZrWTTbS3RQVaXEF9w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MFDcInFpsAzYcTWyutWwGayUROgZ/2gWgbJxT9hygFQ57CaSv9CDAwNos3axeUlA/ZQMGTB8C80tqJH7SYRt1elXEDnZJrCB6yMo/wI37VZ0Qzf0i2A2AOX+BoY3m3vqP0a7k7a5quanqI48PcSEvBW2fHK3jhVd4f6PUZmjwX4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=eTweHEKY; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="eTweHEKY" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1774526733; bh=yoTbL7Lk5kFWSSwVOfmBihcqssZrWTTbS3RQVaXEF9w=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=eTweHEKY81VrQKGjekC2kt4ULWrzTSZOiZiL5ALS++CuKmfThoDfarITrzsLR2qmc u0it9XRxiljH7N3KoBOQO0dkP1ZepEGnQ5giJcQHDEenOfV1zJM3Mj+PMQHJNd21Q8 YA+qPSLORhYPSn+2RkuARgkcU4kXsE7vgB/dUC0Zr3QUIyULdDdt5a+Qf/6F4vGWZX fN53Ooolh6GlwXNATiO3hal/gT96pVG6fSZyRoYeRjHrcZOAuaYLY5EFs/nrj08Ewp VmLX4uMcYwtVYw1E3zmODtczOWfYCzqwje01xRr6hLoquadR8tnKAk57rp5cVwFe+g N4q7XhYYPilHQ== Received: from yukiji.home (lfbn-idf1-1-2269-27.w92-151.abo.wanadoo.fr [92.151.67.27]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: laeyraud) by bali.collaboradmins.com (Postfix) with ESMTPSA id D06C117E60C6; Thu, 26 Mar 2026 13:05:32 +0100 (CET) From: Louis-Alexis Eyraud Date: Thu, 26 Mar 2026 13:04:14 +0100 Subject: [PATCH net-next v2 3/4] net: phy: air_phy_lib: Factorize BuckPBus register accessors 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 Message-Id: <20260326-add-airoha-an8801-support-v2-3-1a42d6b6050f@collabora.com> References: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com> In-Reply-To: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com> To: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , AngeloGioacchino Del Regno , Andrew Lunn , Heiner Kallweit , Russell King Cc: kevin-kw.huang@airoha.com, macpaul.lin@mediatek.com, matthias.bgg@gmail.com, kernel@collabora.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org, Louis-Alexis Eyraud X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1774526729; l=22764; i=louisalexis.eyraud@collabora.com; s=20250113; h=from:subject:message-id; bh=yoTbL7Lk5kFWSSwVOfmBihcqssZrWTTbS3RQVaXEF9w=; b=gL5ij06YyPZvu6hv8zHXZbZ/48SqYkqzfXeWmBdBeNp+kR8XWMz58XMmgc7+e07BtuZAF+9pK ij+WnOgqQKpBUditHsP0MvCHG1+aVTRDdmeV0vNoKZdqEpUSiLx5S9T X-Developer-Key: i=louisalexis.eyraud@collabora.com; a=ed25519; pk=CHFBDB2Kqh4EHc6JIqFn69GhxJJAzc0Zr4e8QxtumuM= In preparation of Airoha AN8801R PHY support, move the BuckPBus register accessors and definitions, present in air_en8811h driver, into the Airoha PHY shared code (air_phy_lib), so they will be usable by the new driver without duplicating them. Also, update air_en8811h driver to use the new function names. Signed-off-by: Louis-Alexis Eyraud --- drivers/net/phy/air_en8811h.c | 303 ++++++++------------------------------= ---- drivers/net/phy/air_phy_lib.c | 180 +++++++++++++++++++++++++ drivers/net/phy/air_phy_lib.h | 23 ++++ 3 files changed, 259 insertions(+), 247 deletions(-) diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c index be7c3426182a26fe3799b875827750e7772caadd..a42898ae41358fe86072a55528a= 0ecff0eb5ec19 100644 --- a/drivers/net/phy/air_en8811h.c +++ b/drivers/net/phy/air_en8811h.c @@ -42,22 +42,6 @@ #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8 #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc =20 -#define AIR_PHY_PAGE_STANDARD 0x0000 -#define AIR_PHY_PAGE_EXTENDED_4 0x0004 - -/* MII Registers Page 4*/ -#define AIR_BPBUS_MODE 0x10 -#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 -#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) -#define AIR_BPBUS_WR_ADDR_HIGH 0x11 -#define AIR_BPBUS_WR_ADDR_LOW 0x12 -#define AIR_BPBUS_WR_DATA_HIGH 0x13 -#define AIR_BPBUS_WR_DATA_LOW 0x14 -#define AIR_BPBUS_RD_ADDR_HIGH 0x15 -#define AIR_BPBUS_RD_ADDR_LOW 0x16 -#define AIR_BPBUS_RD_DATA_HIGH 0x17 -#define AIR_BPBUS_RD_DATA_LOW 0x18 - /* Registers on MDIO_MMD_VEND1 */ #define EN8811H_PHY_FW_STATUS 0x8009 #define EN8811H_PHY_READY 0x02 @@ -245,183 +229,6 @@ static const unsigned long en8811h_led_trig =3D BIT(T= RIGGER_NETDEV_FULL_DUPLEX) | BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX); =20 -static int __air_buckpbus_reg_write(struct phy_device *phydev, - u32 pbus_address, u32 pbus_data) -{ - int ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, - upper_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, - lower_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, - upper_16_bits(pbus_data)); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, - lower_16_bits(pbus_data)); - if (ret < 0) - return ret; - - return 0; -} - -static int air_buckpbus_reg_write(struct phy_device *phydev, - u32 pbus_address, u32 pbus_data) -{ - int saved_page; - int ret =3D 0; - - saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); - - if (saved_page >=3D 0) { - ret =3D __air_buckpbus_reg_write(phydev, pbus_address, - pbus_data); - if (ret < 0) - phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, - pbus_address, ret); - } - - return phy_restore_page(phydev, saved_page, ret); -} - -static int __air_buckpbus_reg_read(struct phy_device *phydev, - u32 pbus_address, u32 *pbus_data) -{ - int pbus_data_low, pbus_data_high; - int ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, - upper_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, - lower_16_bits(pbus_address)); - if (ret < 0) - return ret; - - pbus_data_high =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); - if (pbus_data_high < 0) - return pbus_data_high; - - pbus_data_low =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); - if (pbus_data_low < 0) - return pbus_data_low; - - *pbus_data =3D pbus_data_low | (pbus_data_high << 16); - return 0; -} - -static int air_buckpbus_reg_read(struct phy_device *phydev, - u32 pbus_address, u32 *pbus_data) -{ - int saved_page; - int ret =3D 0; - - saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); - - if (saved_page >=3D 0) { - ret =3D __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); - if (ret < 0) - phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, - pbus_address, ret); - } - - return phy_restore_page(phydev, saved_page, ret); -} - -static int __air_buckpbus_reg_modify(struct phy_device *phydev, - u32 pbus_address, u32 mask, u32 set) -{ - int pbus_data_low, pbus_data_high; - u32 pbus_data_old, pbus_data_new; - int ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, - upper_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, - lower_16_bits(pbus_address)); - if (ret < 0) - return ret; - - pbus_data_high =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); - if (pbus_data_high < 0) - return pbus_data_high; - - pbus_data_low =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); - if (pbus_data_low < 0) - return pbus_data_low; - - pbus_data_old =3D pbus_data_low | (pbus_data_high << 16); - pbus_data_new =3D (pbus_data_old & ~mask) | set; - if (pbus_data_new =3D=3D pbus_data_old) - return 0; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, - upper_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, - lower_16_bits(pbus_address)); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, - upper_16_bits(pbus_data_new)); - if (ret < 0) - return ret; - - ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, - lower_16_bits(pbus_data_new)); - if (ret < 0) - return ret; - - return 0; -} - -static int air_buckpbus_reg_modify(struct phy_device *phydev, - u32 pbus_address, u32 mask, u32 set) -{ - int saved_page; - int ret =3D 0; - - saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); - - if (saved_page >=3D 0) { - ret =3D __air_buckpbus_reg_modify(phydev, pbus_address, mask, - set); - if (ret < 0) - phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, - pbus_address, ret); - } - - return phy_restore_page(phydev, saved_page, ret); -} - static int __air_write_buf(struct phy_device *phydev, u32 address, const struct firmware *fw) { @@ -480,8 +287,8 @@ static int en8811h_wait_mcu_ready(struct phy_device *ph= ydev) { int ret, reg_value; =20 - ret =3D air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_FINISH); + ret =3D air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_FINISH); if (ret) return ret; =20 @@ -506,28 +313,29 @@ static int an8811hb_check_crc(struct phy_device *phyd= ev, u32 set1, int ret; =20 /* Configure CRC */ - ret =3D air_buckpbus_reg_modify(phydev, set1, - AN8811HB_CRC_RD_EN, - AN8811HB_CRC_RD_EN); + ret =3D air_phy_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); + air_phy_buckpbus_reg_read(phydev, set1, &pbus_value); =20 do { msleep(300); - air_buckpbus_reg_read(phydev, mon2, &pbus_value); + air_phy_buckpbus_reg_read(phydev, mon2, &pbus_value); =20 /* We do not know what errors this check is supposed * catch or what to do about a failure. So print the * result and continue like the vendor driver does. */ if (pbus_value & AN8811HB_CRC_ST) { - air_buckpbus_reg_read(phydev, mon3, &pbus_value); + air_phy_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); + return air_phy_buckpbus_reg_modify(phydev, set1, + AN8811HB_CRC_RD_EN, + 0); } } while (--retry); =20 @@ -539,8 +347,8 @@ static void en8811h_print_fw_version(struct phy_device = *phydev) { struct en8811h_priv *priv =3D phydev->priv; =20 - air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, - &priv->firmware_version); + air_phy_buckpbus_reg_read(phydev, EN8811H_FW_VERSION, + &priv->firmware_version); phydev_info(phydev, "MD32 firmware version: %08x\n", priv->firmware_version); } @@ -565,8 +373,8 @@ static int an8811hb_load_firmware(struct phy_device *ph= ydev) { int ret; =20 - ret =3D air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_START); + ret =3D air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); if (ret < 0) return ret; =20 @@ -607,14 +415,14 @@ static int en8811h_load_firmware(struct phy_device *p= hydev) if (ret < 0) goto en8811h_load_firmware_rel1; =20 - ret =3D air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_START); + ret =3D air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); if (ret < 0) goto en8811h_load_firmware_out; =20 - ret =3D air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, - EN8811H_FW_CTRL_2_LOADING, - EN8811H_FW_CTRL_2_LOADING); + ret =3D air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, + EN8811H_FW_CTRL_2_LOADING); if (ret < 0) goto en8811h_load_firmware_out; =20 @@ -626,8 +434,8 @@ static int en8811h_load_firmware(struct phy_device *phy= dev) if (ret < 0) goto en8811h_load_firmware_out; =20 - ret =3D air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, - EN8811H_FW_CTRL_2_LOADING, 0); + ret =3D air_phy_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2, + EN8811H_FW_CTRL_2_LOADING, 0); if (ret < 0) goto en8811h_load_firmware_out; =20 @@ -653,8 +461,8 @@ static int en8811h_restart_mcu(struct phy_device *phyde= v) { int ret; =20 - ret =3D air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, - EN8811H_FW_CTRL_1_START); + ret =3D air_phy_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1, + EN8811H_FW_CTRL_1_START); if (ret < 0) return ret; =20 @@ -948,7 +756,7 @@ static unsigned long an8811hb_clk_recalc_rate(struct cl= k_hw *hw, u32 pbus_value; int ret; =20 - ret =3D air_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value); + ret =3D air_phy_buckpbus_reg_read(phydev, AN8811HB_HWTRAP2, &pbus_value); if (ret < 0) return ret; =20 @@ -960,9 +768,9 @@ 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; =20 - return air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, - AN8811HB_CLK_DRV_CKO_MASK, - AN8811HB_CLK_DRV_CKO_MASK); + return air_phy_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, + AN8811HB_CLK_DRV_CKO_MASK, + AN8811HB_CLK_DRV_CKO_MASK); } =20 static void an8811hb_clk_disable(struct clk_hw *hw) @@ -970,8 +778,8 @@ 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; =20 - air_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, - AN8811HB_CLK_DRV_CKO_MASK, 0); + air_phy_buckpbus_reg_modify(phydev, AN8811HB_CLK_DRV, + AN8811HB_CLK_DRV_CKO_MASK, 0); } =20 static int an8811hb_clk_is_enabled(struct clk_hw *hw) @@ -981,7 +789,7 @@ static int an8811hb_clk_is_enabled(struct clk_hw *hw) u32 pbus_value; int ret; =20 - ret =3D air_buckpbus_reg_read(phydev, AN8811HB_CLK_DRV, &pbus_value); + ret =3D air_phy_buckpbus_reg_read(phydev, AN8811HB_CLK_DRV, &pbus_value); if (ret < 0) return ret; =20 @@ -1047,7 +855,7 @@ static unsigned long en8811h_clk_recalc_rate(struct cl= k_hw *hw, u32 pbus_value; int ret; =20 - ret =3D air_buckpbus_reg_read(phydev, EN8811H_HWTRAP1, &pbus_value); + ret =3D air_phy_buckpbus_reg_read(phydev, EN8811H_HWTRAP1, &pbus_value); if (ret < 0) return ret; =20 @@ -1059,9 +867,9 @@ static int en8811h_clk_enable(struct clk_hw *hw) struct en8811h_priv *priv =3D clk_hw_to_en8811h_priv(hw); struct phy_device *phydev =3D priv->phydev; =20 - return air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, - EN8811H_CLK_CGM_CKO, - EN8811H_CLK_CGM_CKO); + return air_phy_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, + EN8811H_CLK_CGM_CKO, + EN8811H_CLK_CGM_CKO); } =20 static void en8811h_clk_disable(struct clk_hw *hw) @@ -1069,8 +877,8 @@ static void en8811h_clk_disable(struct clk_hw *hw) struct en8811h_priv *priv =3D clk_hw_to_en8811h_priv(hw); struct phy_device *phydev =3D priv->phydev; =20 - air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, - EN8811H_CLK_CGM_CKO, 0); + air_phy_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, + EN8811H_CLK_CGM_CKO, 0); } =20 static int en8811h_clk_is_enabled(struct clk_hw *hw) @@ -1080,7 +888,7 @@ static int en8811h_clk_is_enabled(struct clk_hw *hw) u32 pbus_value; int ret; =20 - ret =3D air_buckpbus_reg_read(phydev, EN8811H_CLK_CGM, &pbus_value); + ret =3D air_phy_buckpbus_reg_read(phydev, EN8811H_CLK_CGM, &pbus_value); if (ret < 0) return ret; =20 @@ -1191,9 +999,9 @@ static int an8811hb_probe(struct phy_device *phydev) return ret; =20 /* Configure led gpio pins as output */ - ret =3D air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT, - AN8811HB_GPIO_OUTPUT_345, - AN8811HB_GPIO_OUTPUT_345); + ret =3D air_phy_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT, + AN8811HB_GPIO_OUTPUT_345, + AN8811HB_GPIO_OUTPUT_345); if (ret < 0) return ret; =20 @@ -1232,9 +1040,9 @@ static int en8811h_probe(struct phy_device *phydev) return ret; =20 /* Configure led gpio pins as output */ - ret =3D air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT, - EN8811H_GPIO_OUTPUT_345, - EN8811H_GPIO_OUTPUT_345); + ret =3D air_phy_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT, + EN8811H_GPIO_OUTPUT_345, + EN8811H_GPIO_OUTPUT_345); if (ret < 0) return ret; =20 @@ -1254,9 +1062,9 @@ static int an8811hb_config_serdes_polarity(struct phy= _device *phydev) 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); + ret =3D air_phy_buckpbus_reg_modify(phydev, AN8811HB_RX_POLARITY, + AN8811HB_RX_POLARITY_NORMAL, + pbus_value); if (ret < 0) return ret; =20 @@ -1267,9 +1075,9 @@ static int an8811hb_config_serdes_polarity(struct phy= _device *phydev) 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); + return air_phy_buckpbus_reg_modify(phydev, AN8811HB_TX_POLARITY, + AN8811HB_TX_POLARITY_NORMAL, + pbus_value); } =20 static int en8811h_config_serdes_polarity(struct phy_device *phydev) @@ -1303,9 +1111,10 @@ static int en8811h_config_serdes_polarity(struct phy= _device *phydev) if (pol =3D=3D PHY_POL_NORMAL) pbus_value |=3D EN8811H_POLARITY_TX_NORMAL; =20 - return air_buckpbus_reg_modify(phydev, EN8811H_POLARITY, - EN8811H_POLARITY_RX_REVERSE | - EN8811H_POLARITY_TX_NORMAL, pbus_value); + return air_phy_buckpbus_reg_modify(phydev, EN8811H_POLARITY, + EN8811H_POLARITY_RX_REVERSE | + EN8811H_POLARITY_TX_NORMAL, + pbus_value); } =20 static int an8811hb_config_init(struct phy_device *phydev) @@ -1457,8 +1266,8 @@ static int en8811h_read_status(struct phy_device *phy= dev) val & MDIO_AN_10GBT_STAT_LP2_5G); } else { /* Get link partner 2.5GBASE-T ability from vendor register */ - ret =3D air_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, - &pbus_value); + ret =3D air_phy_buckpbus_reg_read(phydev, EN8811H_2P5G_LPA, + &pbus_value); if (ret < 0) return ret; linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, diff --git a/drivers/net/phy/air_phy_lib.c b/drivers/net/phy/air_phy_lib.c index 04c4719a073f98ef75eabd54c4851f6a16391350..780ce2a17d3a58d5e3105534e9f= ea71bf3097b36 100644 --- a/drivers/net/phy/air_phy_lib.c +++ b/drivers/net/phy/air_phy_lib.c @@ -13,6 +13,186 @@ =20 #define AIR_EXT_PAGE_ACCESS 0x1f =20 +static int __air_buckpbus_reg_read(struct phy_device *phydev, + u32 pbus_address, u32 *pbus_data) +{ + int pbus_data_low, pbus_data_high; + int ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + pbus_data_high =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) + return pbus_data_high; + + pbus_data_low =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) + return pbus_data_low; + + *pbus_data =3D pbus_data_low | (pbus_data_high << 16); + return 0; +} + +static int __air_buckpbus_reg_write(struct phy_device *phydev, + u32 pbus_address, u32 pbus_data) +{ + int ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, + upper_16_bits(pbus_data)); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, + lower_16_bits(pbus_data)); + if (ret < 0) + return ret; + + return 0; +} + +static int __air_buckpbus_reg_modify(struct phy_device *phydev, + u32 pbus_address, u32 mask, u32 set) +{ + int pbus_data_low, pbus_data_high; + u32 pbus_data_old, pbus_data_new; + int ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + pbus_data_high =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); + if (pbus_data_high < 0) + return pbus_data_high; + + pbus_data_low =3D __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); + if (pbus_data_low < 0) + return pbus_data_low; + + pbus_data_old =3D pbus_data_low | (pbus_data_high << 16); + pbus_data_new =3D (pbus_data_old & ~mask) | set; + if (pbus_data_new =3D=3D pbus_data_old) + return 0; + + ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, + upper_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, + lower_16_bits(pbus_address)); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, + upper_16_bits(pbus_data_new)); + if (ret < 0) + return ret; + + ret =3D __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, + lower_16_bits(pbus_data_new)); + if (ret < 0) + return ret; + + return 0; +} + +int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address, + u32 *pbus_data) +{ + int saved_page; + int ret =3D 0; + + saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >=3D 0) { + ret =3D __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} +EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_read); + +int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address, + u32 pbus_data) +{ + int saved_page; + int ret =3D 0; + + saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >=3D 0) { + ret =3D __air_buckpbus_reg_write(phydev, pbus_address, + pbus_data); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} +EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_write); + +int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_addres= s, + u32 mask, u32 set) +{ + int saved_page; + int ret =3D 0; + + saved_page =3D phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); + + if (saved_page >=3D 0) { + ret =3D __air_buckpbus_reg_modify(phydev, pbus_address, mask, + set); + if (ret < 0) + phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, + pbus_address, ret); + } + + return phy_restore_page(phydev, saved_page, ret); +} +EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_modify); + int air_phy_read_page(struct phy_device *phydev) { return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); diff --git a/drivers/net/phy/air_phy_lib.h b/drivers/net/phy/air_phy_lib.h index dd501b175a3a38e57744f79571eb1bc4ef46fdf5..23d8e3e318398958f2bba297619= f35a86f9871a9 100644 --- a/drivers/net/phy/air_phy_lib.h +++ b/drivers/net/phy/air_phy_lib.h @@ -8,6 +8,29 @@ #ifndef __AIR_PHY_LIB_H #define __AIR_PHY_LIB_H =20 +#define AIR_PHY_PAGE_STANDARD 0x0000 +#define AIR_PHY_PAGE_EXTENDED_1 0x0001 +#define AIR_PHY_PAGE_EXTENDED_4 0x0004 + +/* MII Registers Page 4*/ +#define AIR_BPBUS_MODE 0x10 +#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000 +#define AIR_BPBUS_MODE_ADDR_INCR BIT(15) +#define AIR_BPBUS_WR_ADDR_HIGH 0x11 +#define AIR_BPBUS_WR_ADDR_LOW 0x12 +#define AIR_BPBUS_WR_DATA_HIGH 0x13 +#define AIR_BPBUS_WR_DATA_LOW 0x14 +#define AIR_BPBUS_RD_ADDR_HIGH 0x15 +#define AIR_BPBUS_RD_ADDR_LOW 0x16 +#define AIR_BPBUS_RD_DATA_HIGH 0x17 +#define AIR_BPBUS_RD_DATA_LOW 0x18 + +int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_addres= s, + u32 mask, u32 set); +int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address, + u32 *pbus_data); +int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address, + u32 pbus_data); int air_phy_read_page(struct phy_device *phydev); int air_phy_write_page(struct phy_device *phydev, int page); =20 --=20 2.53.0 From nobody Thu Apr 2 22:17:53 2026 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (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 2738F3F7E98; Thu, 26 Mar 2026 12:05:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774526739; cv=none; b=WekLjp4n6SrT53q4s21Z3fu9rU8Gx1NVci7z7/5AgglJwxc+WCpfKBQ+k497dqxtgFrnFGUNId6NU7dwmRO0l+kqJYsMRsnskMoj4GF2RCKyu2ascaGrrlehEy6LiQyNuhS3uKyNIpYvhpBTrlQupnvfUxrKarH17oTdFDcId8E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774526739; c=relaxed/simple; bh=ngyBwGrO4QzM8hmRm3XbhXijWXNz4Le8oDvmZrnnkUs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=b0CHGF4bFLHR3+ZLA3mFWqmP8NXgsJHNwo7r6u0OWzC4Mhpobmtb/6Q74J1is3bFK7h9gWVPWZ5nglBJk84+ivndQ3MyJD4HaKiseiKBIkhu0P4BKFEYcyjB3JWHKCsPSjqBTLDN0L7lMCouFp9e4gPuGbVv1tNmJ4z/LxdsP4w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=m2pSduEB; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="m2pSduEB" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1774526734; bh=ngyBwGrO4QzM8hmRm3XbhXijWXNz4Le8oDvmZrnnkUs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=m2pSduEBzDM2Ny92y961DX823Myi4vnpGCSBSoYZ+tfQD72jl3hYZ8mJkSV9MyZK7 qDi1WtHcT+8GRkRqgACtroTOlk5SFcrDbK9ehjooMpQX98iTVLtVjjlqf7oHCmK6L4 d6yXIRLQdwv9Y84IC5Qy5Es+lAkwGK0TyJHv3TzH3832poX0IX4zX5On+xQpHwrHe0 fIQDLBPp4plaSCfBsJsNrTkVdHrB2a1FVgOU/CmxsjjVWbyOH9L2GraR5Cu6R6FXp7 qz64BsPGFJOL7UIWZkUzm1TwuBrbvrVEaEmdpCfS84dlMNO9B9WruRXqed9xVAi7Pd GlZIJGYqSKalQ== Received: from yukiji.home (lfbn-idf1-1-2269-27.w92-151.abo.wanadoo.fr [92.151.67.27]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: laeyraud) by bali.collaboradmins.com (Postfix) with ESMTPSA id D06B917E60C7; Thu, 26 Mar 2026 13:05:33 +0100 (CET) From: Louis-Alexis Eyraud Date: Thu, 26 Mar 2026 13:04:15 +0100 Subject: [PATCH net-next v2 4/4] net: phy: Introduce Airoha AN8801/R Gigabit Ethernet PHY driver 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 Message-Id: <20260326-add-airoha-an8801-support-v2-4-1a42d6b6050f@collabora.com> References: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com> In-Reply-To: <20260326-add-airoha-an8801-support-v2-0-1a42d6b6050f@collabora.com> To: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , AngeloGioacchino Del Regno , Andrew Lunn , Heiner Kallweit , Russell King Cc: kevin-kw.huang@airoha.com, macpaul.lin@mediatek.com, matthias.bgg@gmail.com, kernel@collabora.com, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org, Louis-Alexis Eyraud X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1774526729; l=32474; i=louisalexis.eyraud@collabora.com; s=20250113; h=from:subject:message-id; bh=u6MF4DQbEO/3cL61CJ4LobPOQaH0z2cavIrE3OPxVJc=; b=v55dq9h1D/8JHwPp86GuoM1B8+96cNKBjIB9koa4AsJ4FsdsMKwL3fDzRo2rdNB+hPKy3xpjH qP7kqgufZSfB/tUTRKFOOXbdAtRLYamLBFDZfiEEeIpFWqjFELzGnOd X-Developer-Key: i=louisalexis.eyraud@collabora.com; a=ed25519; pk=CHFBDB2Kqh4EHc6JIqFn69GhxJJAzc0Zr4e8QxtumuM= From: AngeloGioacchino Del Regno Introduce a driver for the Airoha AN8801R Series Gigabit Ethernet PHY; this currently supports setting up PHY LEDs, 10/100M, 1000M speeds, and Wake on LAN and PHY interrupts. Signed-off-by: AngeloGioacchino Del Regno Signed-off-by: Louis-Alexis Eyraud --- drivers/net/phy/Kconfig | 6 + drivers/net/phy/Makefile | 1 + drivers/net/phy/air_an8801.c | 1115 ++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 1122 insertions(+) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index b6b1cde7e51fa1e470bfe210c8764a193449acb5..4dd77ba487763eaf16c2b390feb= 237e667f76746 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -90,6 +90,12 @@ config AS21XXX_PHY AS21210PB1 that all register with the PHY ID 0x7500 0x7500 before the firmware is loaded. =20 +config AIR_AN8801_PHY + tristate "Airoha AN8801 Gigabit PHY" + select AIR_NET_PHYLIB + help + Currently supports the Airoha AN8801R PHY. + config AIR_EN8811H_PHY tristate "Airoha EN8811H 2.5 Gigabit PHY" select AIR_NET_PHYLIB diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 7cf1fa9e12cb6073a9e68c0ffa6284b361b80487..de660ae949453d99f8a383ca0c0= e3cf3f1a14922 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -29,6 +29,7 @@ obj-y +=3D $(sfp-obj-y) $(sfp-obj-m) =20 obj-$(CONFIG_ADIN_PHY) +=3D adin.o obj-$(CONFIG_ADIN1100_PHY) +=3D adin1100.o +obj-$(CONFIG_AIR_AN8801_PHY) +=3D air_an8801.o obj-$(CONFIG_AIR_EN8811H_PHY) +=3D air_en8811h.o obj-$(CONFIG_AIR_NET_PHYLIB) +=3D air_phy_lib.o obj-$(CONFIG_AMD_PHY) +=3D amd.o diff --git a/drivers/net/phy/air_an8801.c b/drivers/net/phy/air_an8801.c new file mode 100644 index 0000000000000000000000000000000000000000..53ade9665fe9e08010fe208f760= 051f1b621a128 --- /dev/null +++ b/drivers/net/phy/air_an8801.c @@ -0,0 +1,1115 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for the Airoha AN8801 Gigabit PHY. + * + * Copyright (C) 2025 Airoha Technology Corp. + * Copyright (C) 2025 Collabora Ltd. + * AngeloGioacchino Del Regno + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "air_phy_lib.h" + +#define AN8801R_PHY_ID 0xc0ff0421 + +/* MII Registers */ + +/* MII Registers - Airoha Page 1 */ +#define AN8801_EXT_REG_PHY 0x14 +#define AN8801_EXT_PHY_STATUS0 GENMASK(1, 0) +#define AN8801_EXT_PHY_DOWNSHIFT_CTL GENMASK(3, 2) /* 2 to 5 1G auto-neg= attempts (0..3) */ +#define AN8801_EXT_PHY_DOWNSHIFT_EN BIT(4) +#define AN8801_EXT_PHY_CTRL0 BIT(5) +#define AN8801_EXT_PHY_STATUS1 GENMASK(8, 6) +#define AN8801_EXT_PHY_CTRL1 GENMASK(14, 9) + +/* MII Registers - Airoha Page 4 */ +#define AN8801_PBUS_ACCESS BIT(28) +#define AN8801_PBUS_EPHY_ACCESS BIT(24) +#define AN8801_PBUS_CL22_ACCESS BIT(23) + +/* BPBUS Registers */ +#define AN8801_BPBUS_REG_LED_GPIO 0x54 +#define AN8801_BPBUS_REG_LED_ID_SEL 0x58 +#define LED_ID_GPIO_SEL(led, gpio) ((led) << ((gpio) * 3)) +#define AN8801_BPBUS_REG_GPIO_MODE 0x70 +#define AN8801_BPBUS_REG_PHY_IRQ_GPIO 0x7c +#define AN8801_PHY_IRQ_GPIO_NUM_MASK GENMASK(19, 16) +#define AN8801_PHY_IRQ_GPIO_NUM 1 + +#define AN8801_BPBUS_REG_CKO 0x1a4 +#define AN8801_CKO_OUTPUT_MODE_AUTO 3 + +#define AN8801_BPBUS_REG_LINK_MODE 0x5054 +#define AN8801_BPBUS_LINK_MODE_1000 BIT(0) + +#define AN8801_BPBUS_REG_BYPASS_PTP 0x21c004 +#define AN8801_BYP_PTP_SGMII_TO_GPHY BIT(8) +#define AN8801_BYP_PTP_RGMII_TO_GPHY BIT(0) + +#define AN8801_BPBUS_REG_TXDLY_STEP 0x21c024 +#define RGMII_DELAY_STEP_MASK GENMASK(2, 0) +#define RGMII_DELAY_NO_STEP 0 +#define RGMII_DELAY_STEP_1 1 +#define RGMII_DELAY_STEP_2 2 +#define RGMII_DELAY_STEP_3 3 +#define RGMII_DELAY_STEP_4 4 +#define RGMII_DELAY_STEP_5 5 +#define RGMII_DELAY_STEP_6 6 +#define RGMII_DELAY_STEP_7 7 +#define RGMII_TXDELAY_FORCE_MODE BIT(24) + +#define AN8801_RGMII_TXDELAY_DEFAULT RGMII_DELAY_STEP_4 /* 1.883ns delay */ + +#define AN8801_BPBUS_REG_RXDLY_STEP 0x21c02c +#define RGMII_RXDELAY_ALIGN BIT(4) +#define RGMII_RXDELAY_FORCE_MODE BIT(24) + +#define AN8801_RGMII_RXDELAY_DEFAULT RGMII_DELAY_NO_STEP /* 1.992ns delay = */ + +#define AN8801_BPBUS_REG_EFIFO_CTL(x) (0x270004 + (0x100 * (x))) /* 0..2 */ +#define AN8801_EFIFO_ALL_EN GENMASK(7, 0) +#define AN8801_EFIFO_RX_EN BIT(0) +#define AN8801_EFIFO_TX_EN BIT(1) +#define AN8801_EFIFO_RX_CLK_EN BIT(2) +#define AN8801_EFIFO_TX_CLK_EN BIT(3) +#define AN8801_EFIFO_RX_EEE_EN BIT(4) +#define AN8801_EFIFO_TX_EEE_EN BIT(5) +#define AN8801_EFIFO_RX_ODD_NIBBLE_EN BIT(6) +#define AN8801_EFIFO_TX_ODD_NIBBLE_EN BIT(7) + +#define AN8801_BPBUS_REG_WOL_MAC_16_47 0x285114 +#define AN8801_BPBUS_REG_WOL_MAC_0_15 0x285118 + +#define AN8801_BPBUS_REG_WAKEUP_CTL1 0x285400 +#define AN8801_WOL_WAKE_MAGIC_EN GENMASK(3, 1) +#define AN8801_WOL_WAKE_LNKCHG_EN BIT(4) + +#define AN8801_BPBUS_REG_WAKEUP_CTL2 0x285404 +#define AN8801_WAKE_OUT_TYPE_PULSE BIT(0) /* Set/Unset: Pulse/Static */ +#define AN8801_WAKE_OUT_POLARITY_NEG BIT(1) /* Set/Unset: Negative/Posit= ive */ +#define AN8801_WAKE_OUT_WIDTH GENMASK(2, 3) +#define AN8801_WAKE_OUT_84MS 0 +#define AN8801_WAKE_OUT_168MS 1 +#define AN8801_WAKE_OUT_336MS 2 +#define AN8801_WAKE_OUT_672MS 3 +#define AN8801_WAKE_OUT_EN BIT(4) +#define AN8801_PME_WAKEUP_CLR BIT(8) + +#define AN8801_BPBUS_REG_WAKE_IRQ_EN 0x285700 +#define AN8801_BPBUS_REG_WAKE_IRQ_STS 0x285704 +#define AN8801_IRQ_WAKE_LNKCHG BIT(0) /* Wake on link change */ +#define AN8801_IRQ_WAKE_UNIPKT BIT(1) /* Wake on unicast packet */ +#define AN8801_IRQ_WAKE_MULPKT BIT(2) /* Wake on multicast packet */ +#define AN8801_IRQ_WAKE_BCPKT BIT(3) /* Wake on broadcast packet */ +#define AN8801_IRQ_WAKE_MAGICPKT BIT(4) /* Wake on magic packet */ +#define AN8801_IRQ_WAKE_ALL GENMASK(4, 0) + +/* MDIO_MMD_VEND1 Registers */ +#define AN8801_PHY_TX_PAIR_DLY_SEL_GBE 0x13 +#define AN8801_PHY_PAIR_DLY_SEL_A_GBE GENMASK(14, 12) +#define AN8801_PHY_PAIR_DLY_SEL_B_GBE GENMASK(10, 8) +#define AN8801_PHY_PAIR_DLY_SEL_C_GBE GENMASK(6, 4) +#define AN8801_PHY_PAIR_DLY_SEL_D_GBE GENMASK(2, 0) +#define AN8801_PHY_RXADC_CTRL 0xd8 +#define AN8801_PHY_RXADC_SAMP_PHSEL_A BIT(12) +#define AN8801_PHY_RXADC_SAMP_PHSEL_B BIT(8) +#define AN8801_PHY_RXADC_SAMP_PHSEL_C BIT(4) +#define AN8801_PHY_RXADC_SAMP_PHSEL_D BIT(0) +#define AN8801_PHY_RXADC_REV_0 0xd9 +#define AN8801_PHY_RXADC_REV_MASK_A GENMASK(15, 8) +#define AN8801_PHY_RXADC_REV_MASK_B GENMASK(7, 0) +#define AN8801_PHY_RXADC_REV_1 0xda +#define AN8801_PHY_RXADC_REV_MASK_C GENMASK(15, 8) +#define AN8801_PHY_RXADC_REV_MASK_D GENMASK(7, 0) + +/* MDIO_MMD_VEND2 Registers */ +#define LED_BCR 0x21 +#define LED_BCR_MODE_MASK GENMASK(1, 0) +#define LED_BCR_TIME_TEST BIT(2) +#define LED_BCR_CLK_EN BIT(3) +#define LED_BCR_EVT_ALL BIT(4) +#define LED_BCR_EXT_CTRL BIT(15) +#define LED_BCR_MODE_DISABLE 0 +#define LED_BCR_MODE_2LED 1 +#define LED_BCR_MODE_3LED_1 2 +#define LED_BCR_MODE_3LED_2 3 + +#define LED_ON_DUR 0x22 +#define LED_ON_DUR_MASK GENMASK(15, 0) + +#define LED_BLINK_DUR 0x23 +#define LED_BLINK_DUR_MASK GENMASK(15, 0) + +#define LED_ON_CTRL(i) (0x24 + ((i) * 2)) +#define LED_ON_EVT_MASK GENMASK(6, 0) +#define LED_ON_EVT_LINK_1000M BIT(0) +#define LED_ON_EVT_LINK_100M BIT(1) +#define LED_ON_EVT_LINK_10M BIT(2) +#define LED_ON_EVT_LINK_DN BIT(3) +#define LED_ON_EVT_FDX BIT(4) +#define LED_ON_EVT_HDX BIT(5) +#define LED_ON_EVT_FORCE BIT(6) +#define LED_ON_POL BIT(14) +#define LED_ON_EN BIT(15) + +#define LED_BLINK_CTRL(i) (0x25 + ((i) * 2)) +#define LED_BLINK_EVT_MASK GENMASK(9, 0) +#define LED_BLINK_EVT_1000M_TX BIT(0) +#define LED_BLINK_EVT_1000M_RX BIT(1) +#define LED_BLINK_EVT_100M_TX BIT(2) +#define LED_BLINK_EVT_100M_RX BIT(3) +#define LED_BLINK_EVT_10M_TX BIT(4) +#define LED_BLINK_EVT_10M_RX BIT(5) +#define LED_BLINK_EVT_COLLISION BIT(6) +#define LED_BLINK_EVT_RX_CRC_ERR BIT(7) +#define LED_BLINK_EVT_RX_IDLE_ERR BIT(8) +#define LED_BLINK_EVT_FORCE BIT(9) + +#define AN8801R_NUM_LEDS 3 +#define AN8801_PERIOD_SHIFT 15 +#define AN8801_PERIOD_UNIT 32768 /* (1 << AN8801_PERIOD_SHIFT) */ +#define AN8801_MAX_PERIOD_MS 2147 + +#define LED_BLINK_DURATION_UNIT 780 +#define LED_BLINK_DURATION(f) (LED_BLINK_DURATION_UNIT << (f)) + +#define AN8801_LED_DURATION_UNIT_US 32768 + +#define AN8801_REG_PHY_INTERNAL0 0x600 +#define AN8801_REG_PHY_INTERNAL1 0x601 +#define AN8801_PHY_INTFUNC_MASK GENMASK(15, 0) /* PHY internal functions= */ + +enum an8801r_led_fn { + AN8801R_LED_FN_NONE, + AN8801R_LED_FN_LINK, + AN8801R_LED_FN_ACTIVITY, + AN8801R_LED_FN_MAX, +}; + +struct an8801r_priv { + bool wake_magic_enabled; + bool wake_lnkchg_enabled; +}; + +static int an8801_buckpbus_reg_rmw(struct phy_device *phydev, + u32 addr, u32 mask, u32 set) +{ + return air_phy_buckpbus_reg_modify(phydev, + addr | AN8801_PBUS_ACCESS, + mask, set); +} + +static int an8801_buckpbus_reg_set_bits(struct phy_device *phydev, + u32 addr, u32 mask) +{ + return air_phy_buckpbus_reg_modify(phydev, + addr | AN8801_PBUS_ACCESS, + mask, mask); +} + +static int an8801_buckpbus_reg_clear_bits(struct phy_device *phydev, + u32 addr, u32 mask) +{ + return air_phy_buckpbus_reg_modify(phydev, + addr | AN8801_PBUS_ACCESS, + mask, 0); +} + +static int an8801_buckpbus_reg_write(struct phy_device *phydev, u32 addr, + u32 data) +{ + return air_phy_buckpbus_reg_write(phydev, + addr | AN8801_PBUS_ACCESS, + data); +} + +static int an8801_buckpbus_reg_read(struct phy_device *phydev, u32 addr, + u32 *data) +{ + return air_phy_buckpbus_reg_read(phydev, + addr | AN8801_PBUS_ACCESS, + data); +} + +static u32 an8801r_led_blink_ms_to_hw(unsigned long req_ms) +{ + u32 req_ns, regval; + + if (req_ms > AN8801_MAX_PERIOD_MS) + req_ms =3D AN8801_MAX_PERIOD_MS; + + req_ns =3D req_ms * 1000000; + + /* Round to the nearest period unit... */ + regval =3D req_ns + (AN8801_PERIOD_UNIT / 2); + + /* ...and now divide by the full period */ + regval >>=3D AN8801_PERIOD_SHIFT; + + return regval; +} + +static int an8801r_led_blink_set(struct phy_device *phydev, u8 index, + unsigned long *delay_on, + unsigned long *delay_off) +{ + u32 hw_delay_on, hw_delay_off; + bool blink; + int ret; + + if (index >=3D AN8801R_NUM_LEDS) + return -EINVAL; + + if (delay_on && delay_off) { + blink =3D true; + + if (*delay_on =3D=3D 0 || *delay_off =3D=3D 0) { + *delay_on =3D 64; + *delay_off =3D 64; + } + + hw_delay_on =3D an8801r_led_blink_ms_to_hw(*delay_on); + hw_delay_off =3D an8801r_led_blink_ms_to_hw(*delay_off); + } else { + blink =3D false; + } + + if (blink) { + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_DUR, + LED_BLINK_DURATION(hw_delay_on)); + if (ret) + goto error; + + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND2, LED_ON_DUR, + LED_BLINK_DURATION(hw_delay_off) >> 1); + if (ret) + goto error; + } + + ret =3D phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EN, blink ? LED_ON_EN : 0); + if (ret) + return ret; + + return 0; +error: + phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EN, 0); + return ret; +} + +static int an8801r_led_brightness_set(struct phy_device *phydev, u8 index, + enum led_brightness value) +{ + int ret; + + if (index >=3D AN8801R_NUM_LEDS) + return -EINVAL; + + ret =3D phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EVT_MASK, + (value =3D=3D LED_OFF) ? 0 : LED_ON_EVT_FORCE); + if (ret) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EN, (value =3D=3D LED_OFF) ? 0 : LED_ON_EN); +} + +static int an8801r_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) +{ + int on, blink; + + on =3D phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index)); + if (on < 0) + return on; + + blink =3D phy_read_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(index)); + if (blink < 0) + return blink; + + if (FIELD_GET(LED_ON_EVT_LINK_10M, on)) + __set_bit(TRIGGER_NETDEV_LINK_10, rules); + + if (FIELD_GET(LED_ON_EVT_LINK_100M, on)) + __set_bit(TRIGGER_NETDEV_LINK_100, rules); + + if (FIELD_GET(LED_ON_EVT_LINK_1000M, on)) + __set_bit(TRIGGER_NETDEV_LINK_1000, rules); + + if (FIELD_GET(LED_ON_EVT_LINK_10M, on) && + FIELD_GET(LED_ON_EVT_LINK_100M, on) && + FIELD_GET(LED_ON_EVT_LINK_1000M, on)) + __set_bit(TRIGGER_NETDEV_LINK, rules); + + if (FIELD_GET(LED_BLINK_EVT_10M_RX, blink) || + FIELD_GET(LED_BLINK_EVT_100M_RX, blink) || + FIELD_GET(LED_BLINK_EVT_1000M_RX, blink)) + __set_bit(TRIGGER_NETDEV_RX, rules); + + if (FIELD_GET(LED_BLINK_EVT_10M_TX, blink) || + FIELD_GET(LED_BLINK_EVT_100M_TX, blink) || + FIELD_GET(LED_BLINK_EVT_1000M_TX, blink)) + __set_bit(TRIGGER_NETDEV_TX, rules); + + if (FIELD_GET(LED_BLINK_EVT_RX_CRC_ERR, blink)) + __set_bit(TRIGGER_NETDEV_RX_ERR, rules); + + return 0; +} + +static int an8801r_led_trig_to_hw(unsigned long rules, u16 *on, u16 *blink) +{ + if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) + *on |=3D LED_ON_EVT_LINK_10M; + + if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) + *on |=3D LED_ON_EVT_LINK_100M; + + if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) + *on |=3D LED_ON_EVT_LINK_1000M; + + if (test_bit(TRIGGER_NETDEV_LINK, &rules)) { + *on |=3D LED_ON_EVT_LINK_10M; + *on |=3D LED_ON_EVT_LINK_100M; + *on |=3D LED_ON_EVT_LINK_1000M; + } + + if (test_bit(TRIGGER_NETDEV_RX, &rules)) { + *blink |=3D LED_BLINK_EVT_10M_RX; + *blink |=3D LED_BLINK_EVT_100M_RX; + *blink |=3D LED_BLINK_EVT_1000M_RX; + } + + if (test_bit(TRIGGER_NETDEV_TX, &rules)) { + *blink |=3D LED_BLINK_EVT_10M_TX; + *blink |=3D LED_BLINK_EVT_100M_TX; + *blink |=3D LED_BLINK_EVT_1000M_TX; + } + + if (test_bit(TRIGGER_NETDEV_RX_ERR, &rules)) + *blink |=3D LED_BLINK_EVT_RX_CRC_ERR; + + if (rules && !*on && !*blink) + return -EOPNOTSUPP; + + return 0; +} + +static int an8801r_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + u16 on =3D 0, blink =3D 0; + + if (index >=3D AN8801R_NUM_LEDS) + return -EINVAL; + + return an8801r_led_trig_to_hw(rules, &on, &blink); +} + +static int an8801r_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + u16 on =3D 0, blink =3D 0; + int ret; + + if (index >=3D AN8801R_NUM_LEDS) + return -EINVAL; + + ret =3D an8801r_led_trig_to_hw(rules, &on, &blink); + if (ret) + return ret; + + ret =3D phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EVT_MASK, on); + if (ret) + return ret; + + ret =3D phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_BLINK_CTRL(index), + LED_BLINK_EVT_MASK, blink); + + if (ret) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_EN, on | blink ? LED_ON_EN : 0); +} + +static int an8801r_led_polarity_set(struct phy_device *phydev, int index, + unsigned long modes) +{ + unsigned long mode; + bool active_high; + + if (index >=3D AN8801R_NUM_LEDS) + return -EINVAL; + + for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { + switch (mode) { + case PHY_LED_ACTIVE_HIGH: + active_high =3D true; + break; + case PHY_LED_ACTIVE_LOW: + active_high =3D false; + break; + default: + return -EINVAL; + } + } + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, LED_ON_CTRL(index), + LED_ON_POL, active_high ? LED_ON_POL : 0); +} + +static int an8801r_led_init(struct phy_device *phydev, u8 *led_cfg) +{ + int led_id, ret; + + /* Set LED BCR Enable */ + ret =3D phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, LED_BCR, + LED_BCR_EXT_CTRL | LED_BCR_CLK_EN); + if (ret) + return ret; + + for (led_id =3D 0; led_id < AN8801R_NUM_LEDS; led_id++) { + unsigned long led_trigger =3D 0; + u32 led_gpio =3D led_id + 1; + + switch (led_cfg[led_id]) { + case AN8801R_LED_FN_LINK: + led_trigger =3D BIT(TRIGGER_NETDEV_LINK); + break; + case AN8801R_LED_FN_ACTIVITY: + led_trigger =3D BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX); + break; + default: + led_trigger =3D 0; + break; + } + + ret =3D an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_LED_GPIO, + BIT(led_gpio)); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_LED_ID_SEL, + LED_ID_GPIO_SEL(led_id, + led_gpio)); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_GPIO_MODE, + BIT(led_gpio)); + if (ret) + return ret; + + if (!led_trigger) + continue; + + ret =3D an8801r_led_hw_control_set(phydev, led_id, led_trigger); + if (ret) + return ret; + } + return 0; +} + +static int an8801r_reset_wake(struct phy_device *phydev) +{ + struct an8801r_priv *priv =3D phydev->priv; + u32 reg_val =3D 0; + int ret; + + /* Enable wakeup clear and disable wake up output */ + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL2, + AN8801_PME_WAKEUP_CLR | + AN8801_WAKE_OUT_POLARITY_NEG); + if (ret) + return ret; + + /* Clear WAKEUP_CTL1 register before enabling the wakeup events + * again + */ + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL1, + 0); + if (ret) + return ret; + + if (priv->wake_magic_enabled) + reg_val |=3D AN8801_WOL_WAKE_MAGIC_EN; + + if (priv->wake_lnkchg_enabled) + reg_val |=3D AN8801_WOL_WAKE_LNKCHG_EN; + + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL1, + reg_val); + if (ret) + return ret; + + /* Disable wake up clear and re-enable wake up output */ + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKEUP_CTL2, + AN8801_WAKE_OUT_POLARITY_NEG | + AN8801_WAKE_OUT_EN); + if (ret) + return ret; + + return 0; +} + +static int an8801r_ack_interrupt(struct phy_device *phydev) +{ + int ret; + + /* Reset wake status */ + ret =3D an8801r_reset_wake(phydev); + if (ret) + return ret; + + /* Clear the interrupts by writing the reg */ + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_WAKE_IRQ_STS, + AN8801_IRQ_WAKE_ALL); + if (ret) + return ret; + + return 0; +} + +static int an8801r_config_intr(struct phy_device *phydev) +{ + int ret; + + if (phydev->interrupts =3D=3D PHY_INTERRUPT_ENABLED) { + u32 val =3D FIELD_PREP(AN8801_PHY_IRQ_GPIO_NUM_MASK, + AN8801_PHY_IRQ_GPIO_NUM); + + ret =3D an8801_buckpbus_reg_write(phydev, + AN8801_BPBUS_REG_PHY_IRQ_GPIO, + val); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_LNKCHG); + if (ret) + return ret; + + } else { + ret =3D an8801_buckpbus_reg_write(phydev, + AN8801_BPBUS_REG_PHY_IRQ_GPIO, + 0); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_LNKCHG); + if (ret) + return ret; + } + + ret =3D an8801r_ack_interrupt(phydev); + if (ret) + return ret; + + return 0; +} + +static irqreturn_t an8801r_handle_interrupt(struct phy_device *phydev) +{ + u32 irq_status =3D 0; + int ret; + + ret =3D an8801_buckpbus_reg_read(phydev, AN8801_BPBUS_REG_WAKE_IRQ_STS, + &irq_status); + if (ret) + return ret; + + ret =3D an8801r_ack_interrupt(phydev); + if (ret) + return IRQ_NONE; + + if (irq_status & AN8801_IRQ_WAKE_MAGICPKT) { + pm_wakeup_event(&phydev->mdio.dev, 0); + return IRQ_HANDLED; + } + + if (irq_status & AN8801_IRQ_WAKE_LNKCHG) { + phy_trigger_machine(phydev); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void an8801r_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + u32 reg_val; + + /* If the PHY is not capable of waking the system, then WoL can not + * be supported. + */ + if (!device_can_wakeup(&phydev->mdio.dev)) { + wol->supported =3D 0; + return; + } + + wol->supported =3D WAKE_MAGIC; + + an8801_buckpbus_reg_read(phydev, AN8801_BPBUS_REG_WAKEUP_CTL1, + ®_val); + + if (reg_val & AN8801_WOL_WAKE_MAGIC_EN) + wol->wolopts |=3D WAKE_MAGIC; + else + wol->wolopts &=3D ~WAKE_MAGIC; +} + +static int an8801r_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + struct net_device *attach_dev =3D phydev->attached_dev; + const unsigned char *macaddr =3D attach_dev->dev_addr; + struct an8801r_priv *priv =3D phydev->priv; + u32 reg_val; + int ret; + + if (!device_can_wakeup(&phydev->mdio.dev)) + return -EOPNOTSUPP; + + if (wol->wolopts & WAKE_MAGIC) { + /* MAC bits 16..47 */ + reg_val =3D (macaddr[2] << 24) | (macaddr[3] << 16); + reg_val |=3D (macaddr[4] << 8) | (macaddr[5]); + + ret =3D an8801_buckpbus_reg_write(phydev, + AN8801_BPBUS_REG_WOL_MAC_16_47, + reg_val); + if (ret) + return ret; + + /* MAC bits 0..15 */ + reg_val =3D (macaddr[0] << 8) | (macaddr[1]); + + ret =3D an8801_buckpbus_reg_write(phydev, + AN8801_BPBUS_REG_WOL_MAC_0_15, + reg_val); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKEUP_CTL1, + AN8801_WOL_WAKE_MAGIC_EN); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_MAGICPKT); + if (ret) + return ret; + + } else { + ret =3D an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_WAKEUP_CTL1, + AN8801_WOL_WAKE_MAGIC_EN); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_MAGICPKT); + if (ret) + return ret; + } + + priv->wake_magic_enabled =3D !!(wol->wolopts & WAKE_MAGIC); + + device_set_wakeup_enable(&phydev->mdio.dev, + priv->wake_magic_enabled); + + return 0; +} + +static int an8801r_of_init_leds(struct phy_device *phydev, u8 *led_cfg) +{ + struct device *dev =3D &phydev->mdio.dev; + struct device_node *np =3D dev->of_node; + struct device_node *leds; + u32 function_enum_idx; + int ret; + + if (!np) + return 0; + + /* If devicetree is present, leds configuration is required */ + leds =3D of_get_child_by_name(np, "leds"); + if (!leds) + return 0; + + for_each_available_child_of_node_scoped(leds, led) { + u32 led_idx; + + ret =3D of_property_read_u32(led, "reg", &led_idx); + if (ret) + goto out; + + if (led_idx >=3D AN8801R_NUM_LEDS) { + ret =3D -EINVAL; + goto out; + } + + ret =3D of_property_read_u32(led, "function-enumerator", + &function_enum_idx); + if (ret) + function_enum_idx =3D AN8801R_LED_FN_NONE; + + if (function_enum_idx >=3D AN8801R_LED_FN_MAX) { + ret =3D -EINVAL; + goto out; + } + + led_cfg[led_idx] =3D function_enum_idx; + } +out: + of_node_put(leds); + return ret; +} + +static int an8801r_rgmii_rxdelay(struct phy_device *phydev, bool enable, + u16 delay_steps) +{ + u32 reg_val; + + if (delay_steps > RGMII_DELAY_STEP_MASK) + return -EINVAL; + + if (enable) { + reg_val =3D delay_steps & RGMII_DELAY_STEP_MASK; + + /* Set align bit to add extra offset for RX delay */ + reg_val |=3D RGMII_RXDELAY_ALIGN; + + /* Set force mode bit to enable RX delay insertion */ + reg_val |=3D RGMII_RXDELAY_FORCE_MODE; + } else { + reg_val =3D 0; + } + + return an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_RXDLY_STEP, + reg_val); +} + +static int an8801r_rgmii_txdelay(struct phy_device *phydev, bool enable, + u16 delay_steps) +{ + u32 reg_val; + + if (delay_steps > RGMII_DELAY_STEP_MASK) + return -EINVAL; + + if (enable) { + reg_val =3D delay_steps & RGMII_DELAY_STEP_MASK; + + /* Set force mode bit to enable TX delay insertion */ + reg_val |=3D RGMII_TXDELAY_FORCE_MODE; + } else { + reg_val =3D 0; + } + + return an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_TXDLY_STEP, + reg_val); +} + +static int an8801r_rgmii_delay_config(struct phy_device *phydev) +{ + bool enable_delay; + u16 delay_step; + int ret; + + if (phydev->interface =3D=3D PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface =3D=3D PHY_INTERFACE_MODE_RGMII_TXID) { + enable_delay =3D true; + delay_step =3D AN8801_RGMII_TXDELAY_DEFAULT; + } else { + enable_delay =3D false; + delay_step =3D RGMII_DELAY_NO_STEP; + } + + ret =3D an8801r_rgmii_txdelay(phydev, enable_delay, delay_step); + if (ret) + return ret; + + if (phydev->interface =3D=3D PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface =3D=3D PHY_INTERFACE_MODE_RGMII_RXID) { + enable_delay =3D true; + delay_step =3D AN8801_RGMII_RXDELAY_DEFAULT; + } else { + enable_delay =3D false; + delay_step =3D RGMII_DELAY_NO_STEP; + } + + ret =3D an8801r_rgmii_rxdelay(phydev, enable_delay, delay_step); + if (ret) + return ret; + + return 0; +} + +static int an8801r_config_init(struct phy_device *phydev) +{ + u8 led_default_function[AN8801R_NUM_LEDS] =3D { 0 }; + int ret; + + ret =3D an8801r_of_init_leds(phydev, led_default_function); + if (ret) + return ret; + + /* Disable Low Power Mode (LPM) */ + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL0, + FIELD_PREP(AN8801_PHY_INTFUNC_MASK, 0x1e)); + if (ret) + return ret; + + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND2, AN8801_REG_PHY_INTERNAL1, + FIELD_PREP(AN8801_PHY_INTFUNC_MASK, 0x2)); + if (ret) + return ret; + + /* Set the PHY to perform auto-downshift after 3 auto-negotiation + * attempts + */ + ret =3D phy_write_paged(phydev, AIR_PHY_PAGE_EXTENDED_1, + AN8801_EXT_REG_PHY, + FIELD_PREP(AN8801_EXT_PHY_CTRL1, 0x1d) | + FIELD_PREP(AN8801_EXT_PHY_DOWNSHIFT_CTL, 1) | + AN8801_EXT_PHY_DOWNSHIFT_EN); + if (ret < 0) + return ret; + + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_BYPASS_PTP, + AN8801_BYP_PTP_RGMII_TO_GPHY); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(0), + AN8801_EFIFO_RX_EN | + AN8801_EFIFO_TX_EN | + AN8801_EFIFO_RX_CLK_EN | + AN8801_EFIFO_TX_CLK_EN | + AN8801_EFIFO_RX_EEE_EN | + AN8801_EFIFO_TX_EEE_EN); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(1), + AN8801_EFIFO_ALL_EN); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_EFIFO_CTL(2), + AN8801_EFIFO_ALL_EN); + if (ret) + return ret; + + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND1, + AN8801_PHY_TX_PAIR_DLY_SEL_GBE, + FIELD_PREP(AN8801_PHY_PAIR_DLY_SEL_A_GBE, 4) | + FIELD_PREP(AN8801_PHY_PAIR_DLY_SEL_C_GBE, 4)); + if (ret) + return ret; + + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8801_PHY_RXADC_CTRL, + AN8801_PHY_RXADC_SAMP_PHSEL_A | + AN8801_PHY_RXADC_SAMP_PHSEL_C); + if (ret) + return ret; + + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8801_PHY_RXADC_REV_0, + FIELD_PREP(AN8801_PHY_RXADC_REV_MASK_A, 1)); + if (ret) + return ret; + + ret =3D phy_write_mmd(phydev, MDIO_MMD_VEND1, AN8801_PHY_RXADC_REV_1, + FIELD_PREP(AN8801_PHY_RXADC_REV_MASK_C, 1)); + if (ret) + return ret; + + ret =3D an8801r_rgmii_delay_config(phydev); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_write(phydev, AN8801_BPBUS_REG_CKO, + AN8801_CKO_OUTPUT_MODE_AUTO); + if (ret) + return ret; + + ret =3D an8801r_led_init(phydev, led_default_function); + if (ret) { + phydev_err(phydev, "Cannot initialize LEDs: %d\n", ret); + return ret; + } + + return 0; +} + +static int an8801r_read_status(struct phy_device *phydev) +{ + int prev_speed, ret; + u32 val; + + prev_speed =3D phydev->speed; + + ret =3D genphy_read_status(phydev); + if (ret) + return ret; + + if (phydev->link && prev_speed !=3D phydev->speed) { + val =3D phydev->speed =3D=3D SPEED_1000 ? + AN8801_BPBUS_LINK_MODE_1000 : 0; + + return an8801_buckpbus_reg_rmw(phydev, + AN8801_BPBUS_REG_LINK_MODE, + AN8801_BPBUS_LINK_MODE_1000, + val); + }; + + return 0; +} + +static int an8801r_probe(struct phy_device *phydev) +{ + struct device *dev =3D &phydev->mdio.dev; + struct an8801r_priv *priv; + + priv =3D devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->wake_lnkchg_enabled =3D true; + + phydev->priv =3D priv; + + /* Mark this PHY as wakeup capable and register the interrupt as a + * wakeup IRQ if the PHY is marked as a wakeup source in devicetree, + * and the interrupt is valid. + */ + if (of_property_read_bool(dev->of_node, "wakeup-source") && + phy_interrupt_is_valid(phydev)) { + device_set_wakeup_capable(dev, true); + devm_pm_set_wake_irq(dev, phydev->irq); + } + + return 0; +} + +static int an8801r_suspend(struct phy_device *phydev) +{ + struct an8801r_priv *priv =3D phydev->priv; + int ret; + + /* If the PHY may wake up by a wake-on-line event, disable the link + * interrupt to only keep the WOL magic interrupt enabled + */ + if (device_may_wakeup(&phydev->mdio.dev)) { + priv->wake_lnkchg_enabled =3D false; + + ret =3D an8801_buckpbus_reg_clear_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_LNKCHG); + if (ret) + return ret; + + /* Reset Wol status */ + ret =3D an8801r_reset_wake(phydev); + if (ret) + return ret; + } + + if (!phydev->wol_enabled) + return genphy_suspend(phydev); + + return 0; +} + +static int an8801r_resume(struct phy_device *phydev) +{ + struct an8801r_priv *priv =3D phydev->priv; + int ret; + + ret =3D genphy_resume(phydev); + if (ret) + return ret; + + /* Restore the interrupt enable so phylib can receive link + * state interrupts. + */ + if (device_may_wakeup(&phydev->mdio.dev)) { + priv->wake_lnkchg_enabled =3D true; + + ret =3D an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKEUP_CTL1, + AN8801_WOL_WAKE_LNKCHG_EN); + if (ret) + return ret; + + ret =3D an8801_buckpbus_reg_set_bits(phydev, + AN8801_BPBUS_REG_WAKE_IRQ_EN, + AN8801_IRQ_WAKE_LNKCHG); + if (ret) + return ret; + } + + return 0; +} + +static struct phy_driver airoha_driver[] =3D { +{ + PHY_ID_MATCH_MODEL(AN8801R_PHY_ID), + .name =3D "Airoha AN8801R", + .probe =3D an8801r_probe, + .config_init =3D an8801r_config_init, + .suspend =3D an8801r_suspend, + .resume =3D an8801r_resume, + .config_aneg =3D genphy_config_aneg, + .read_status =3D an8801r_read_status, + .config_intr =3D an8801r_config_intr, + .handle_interrupt =3D an8801r_handle_interrupt, + .set_wol =3D an8801r_set_wol, + .get_wol =3D an8801r_get_wol, + .read_page =3D air_phy_read_page, + .write_page =3D air_phy_write_page, + .flags =3D PHY_ALWAYS_CALL_SUSPEND, + .led_brightness_set =3D an8801r_led_brightness_set, + .led_blink_set =3D an8801r_led_blink_set, + .led_hw_is_supported =3D an8801r_led_hw_is_supported, + .led_hw_control_set =3D an8801r_led_hw_control_set, + .led_hw_control_get =3D an8801r_led_hw_control_get, + .led_polarity_set =3D an8801r_led_polarity_set, +} }; +module_phy_driver(airoha_driver); + +static struct mdio_device_id __maybe_unused an8801_tbl[] =3D { + { PHY_ID_MATCH_MODEL(AN8801R_PHY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(mdio, an8801_tbl); + +MODULE_DESCRIPTION("Airoha AN8801 PHY driver"); +MODULE_AUTHOR("AngeloGioacchino Del Regno "); +MODULE_LICENSE("GPL"); --=20 2.53.0