generic: backport support for Aeonsemi AS21xxx PHY
authorChristian Marangi <[email protected]>
Sat, 5 Jul 2025 11:01:21 +0000 (13:01 +0200)
committerChristian Marangi <[email protected]>
Tue, 2 Sep 2025 22:58:48 +0000 (00:58 +0200)
Backport support for Aeonsemi AS121xxx PHY. The PHY require dedicated
firmware to be loaded to correctly work and support a big family of
Aeonsemi PHY that provide from 1G to 10G speed.

Automatically refresh all affected patch and file (rtl PHY).

Link: https://github.com/openwrt/openwrt/pull/19816
Signed-off-by: Christian Marangi <[email protected]>
39 files changed:
package/kernel/linux/modules/netdevices.mk
target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch [new file with mode: 0644]
target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch [new file with mode: 0644]
target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch
target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch
target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch
target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch
target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch
target/linux/generic/config-6.12
target/linux/generic/config-6.6
target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch
target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch
target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch
target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch
target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch
target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch
target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch
target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch
target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch
target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch
target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch
target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch
target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch
target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch
target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch
target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch
target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch
target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch
target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c
target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch
target/linux/realtek/patches-6.12/720-add-rtl-phy.patch
target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch
target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch

index 9ae3958dfed8823708f3019b1ff08ccc51262895..f27624d4ccbf26670f85ec3a7d5625018ac366bf 100644 (file)
@@ -557,6 +557,23 @@ endef
 $(eval $(call KernelPackage,phy-vitesse))
 
 
+define KernelPackage/phy-aeonsemi-as21xxx
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  TITLE:=Aeonsemi AS21xxx 10G Ethernet PHY
+  DEPENDS:=+aeonsemi-as21xxx-firmware +kmod-libphy
+  KCONFIG:=CONFIG_AS21XXX_PHY
+  FILES:= \
+   $(LINUX_DIR)/drivers/net/phy/as21xxx.ko
+  AUTOLOAD:=$(call AutoLoad,18,as21xxx)
+endef
+
+define KernelPackage/phy-aeonsemi-as21x1x/description
+  Kernel modules for Aeonsemi AS21x1x 10G Ethernet PHY
+endef
+
+$(eval $(call KernelPackage,phy-aeonsemi-as21xxx))
+
+
 define KernelPackage/phy-airoha-en8811h
   SUBMENU:=$(NETWORK_DEVICES_MENU)
   TITLE:=Airoha EN8811H 2.5G Ethernet PHY
diff --git a/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch b/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch
new file mode 100644 (file)
index 0000000..cda5226
--- /dev/null
@@ -0,0 +1,303 @@
+From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Sat, 17 May 2025 22:13:45 +0200
+Subject: [PATCH] net: phy: pass PHY driver to .match_phy_device OP
+
+Pass PHY driver pointer to .match_phy_device OP in addition to phydev.
+Having access to the PHY driver struct might be useful to check the
+PHY ID of the driver is being matched for in case the PHY ID scanned in
+the phydev is not consistent.
+
+A scenario for this is a PHY that change PHY ID after a firmware is
+loaded, in such case, the PHY ID stored in PHY device struct is not
+valid anymore and PHY will manually scan the ID in the match_phy_device
+function.
+
+Having the PHY driver info is also useful for those PHY driver that
+implement multiple simple .match_phy_device OP to match specific MMD PHY
+ID. With this extra info if the parsing logic is the same, the matching
+function can be generalized by using the phy_id in the PHY driver
+instead of hardcoding.
+
+Rust wrapper callback is updated to align to the new match_phy_device
+arguments.
+
+Suggested-by: Russell King (Oracle) <[email protected]>
+Reviewed-by: Russell King (Oracle) <[email protected]>
+Signed-off-by: Christian Marangi <[email protected]>
+Reviewed-by: Benno Lossin <[email protected]> # for Rust
+Reviewed-by: FUJITA Tomonori <[email protected]>
+Link: https://patch.msgid.link/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/bcm87xx.c              |  6 ++++--
+ drivers/net/phy/icplus.c               |  6 ++++--
+ drivers/net/phy/marvell10g.c           | 12 ++++++++----
+ drivers/net/phy/micrel.c               |  6 ++++--
+ drivers/net/phy/nxp-c45-tja11xx.c      | 12 ++++++++----
+ drivers/net/phy/nxp-tja11xx.c          |  6 ++++--
+ drivers/net/phy/phy_device.c           |  2 +-
+ drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++---------
+ drivers/net/phy/teranetics.c           |  3 ++-
+ include/linux/phy.h                    |  3 ++-
+ rust/kernel/net/phy.rs                 |  1 +
+ 11 files changed, 56 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/phy/bcm87xx.c
++++ b/drivers/net/phy/bcm87xx.c
+@@ -185,12 +185,14 @@ static irqreturn_t bcm87xx_handle_interr
+       return IRQ_HANDLED;
+ }
+-static int bcm8706_match_phy_device(struct phy_device *phydev)
++static int bcm8706_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706;
+ }
+-static int bcm8727_match_phy_device(struct phy_device *phydev)
++static int bcm8727_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727;
+ }
+--- a/drivers/net/phy/icplus.c
++++ b/drivers/net/phy/icplus.c
+@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(str
+       return ip101a == !ret;
+ }
+-static int ip101a_match_phy_device(struct phy_device *phydev)
++static int ip101a_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return ip101a_g_match_phy_device(phydev, true);
+ }
+-static int ip101g_match_phy_device(struct phy_device *phydev)
++static int ip101g_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return ip101a_g_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/marvell10g.c
++++ b/drivers/net/phy/marvell10g.c
+@@ -1284,7 +1284,8 @@ static int mv3310_get_number_of_ports(st
+       return ret + 1;
+ }
+-static int mv3310_match_phy_device(struct phy_device *phydev)
++static int mv3310_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
+            MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
+@@ -1293,7 +1294,8 @@ static int mv3310_match_phy_device(struc
+       return mv3310_get_number_of_ports(phydev) == 1;
+ }
+-static int mv3340_match_phy_device(struct phy_device *phydev)
++static int mv3340_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
+            MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
+@@ -1317,12 +1319,14 @@ static int mv211x_match_phy_device(struc
+       return !!(val & MDIO_PCS_SPEED_5G) == has_5g;
+ }
+-static int mv2110_match_phy_device(struct phy_device *phydev)
++static int mv2110_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return mv211x_match_phy_device(phydev, true);
+ }
+-static int mv2111_match_phy_device(struct phy_device *phydev)
++static int mv2111_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return mv211x_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/micrel.c
++++ b/drivers/net/phy/micrel.c
+@@ -768,7 +768,8 @@ static int ksz8051_ksz8795_match_phy_dev
+               return !ret;
+ }
+-static int ksz8051_match_phy_device(struct phy_device *phydev)
++static int ksz8051_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return ksz8051_ksz8795_match_phy_device(phydev, true);
+ }
+@@ -888,7 +889,8 @@ static int ksz8061_config_init(struct ph
+       return kszphy_config_init(phydev);
+ }
+-static int ksz8795_match_phy_device(struct phy_device *phydev)
++static int ksz8795_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return ksz8051_ksz8795_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/nxp-c45-tja11xx.c
++++ b/drivers/net/phy/nxp-c45-tja11xx.c
+@@ -1944,13 +1944,15 @@ static int nxp_c45_macsec_ability(struct
+       return macsec_ability;
+ }
+-static int tja1103_match_phy_device(struct phy_device *phydev)
++static int tja1103_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) &&
+              !nxp_c45_macsec_ability(phydev);
+ }
+-static int tja1104_match_phy_device(struct phy_device *phydev)
++static int tja1104_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) &&
+              nxp_c45_macsec_ability(phydev);
+--- a/drivers/net/phy/nxp-tja11xx.c
++++ b/drivers/net/phy/nxp-tja11xx.c
+@@ -646,12 +646,14 @@ static int tja1102_match_phy_device(stru
+       return !ret;
+ }
+-static int tja1102_p0_match_phy_device(struct phy_device *phydev)
++static int tja1102_p0_match_phy_device(struct phy_device *phydev,
++                                     const struct phy_driver *phydrv)
+ {
+       return tja1102_match_phy_device(phydev, true);
+ }
+-static int tja1102_p1_match_phy_device(struct phy_device *phydev)
++static int tja1102_p1_match_phy_device(struct phy_device *phydev,
++                                     const struct phy_driver *phydrv)
+ {
+       return tja1102_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -600,7 +600,7 @@ static int phy_bus_match(struct device *
+               return 0;
+       if (phydrv->match_phy_device)
+-              return phydrv->match_phy_device(phydev);
++              return phydrv->match_phy_device(phydev, phydrv);
+       if (phydev->is_c45) {
+               for (i = 1; i < num_ids; i++) {
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -1343,13 +1343,15 @@ static bool rtlgen_supports_mmd(struct p
+       return val > 0;
+ }
+-static int rtlgen_match_phy_device(struct phy_device *phydev)
++static int rtlgen_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return phydev->phy_id == RTL_GENERIC_PHYID &&
+              !rtlgen_supports_2_5gbps(phydev);
+ }
+-static int rtl8226_match_phy_device(struct phy_device *phydev)
++static int rtl8226_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return phydev->phy_id == RTL_GENERIC_PHYID &&
+              rtlgen_supports_2_5gbps(phydev) &&
+@@ -1365,32 +1367,38 @@ static int rtlgen_is_c45_match(struct ph
+               return !is_c45 && (id == phydev->phy_id);
+ }
+-static int rtl8221b_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_match_phy_device(struct phy_device *phydev,
++                                   const struct phy_driver *phydrv)
+ {
+       return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev);
+ }
+-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev,
++                                             const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false);
+ }
+-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev,
++                                             const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
+ }
+-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev,
++                                             const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
+ }
+-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev,
++                                             const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
+ }
+-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev)
++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev,
++                                              const struct phy_driver *phydrv)
+ {
+       if (phydev->is_c45)
+               return false;
+@@ -1409,7 +1417,8 @@ static int rtl_internal_nbaset_match_phy
+       return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev);
+ }
+-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev,
++                                       const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8251B, true);
+ }
+--- a/drivers/net/phy/teranetics.c
++++ b/drivers/net/phy/teranetics.c
+@@ -67,7 +67,8 @@ static int teranetics_read_status(struct
+       return 0;
+ }
+-static int teranetics_match_phy_device(struct phy_device *phydev)
++static int teranetics_match_phy_device(struct phy_device *phydev,
++                                     const struct phy_driver *phydrv)
+ {
+       return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020;
+ }
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1004,7 +1004,8 @@ struct phy_driver {
+        * driver for the given phydev.  If NULL, matching is based on
+        * phy_id and phy_id_mask.
+        */
+-      int (*match_phy_device)(struct phy_device *phydev);
++      int (*match_phy_device)(struct phy_device *phydev,
++                              const struct phy_driver *phydrv);
+       /**
+        * @set_wol: Some devices (e.g. qnap TS-119P II) require PHY
+--- a/rust/kernel/net/phy.rs
++++ b/rust/kernel/net/phy.rs
+@@ -421,6 +421,7 @@ impl<T: Driver> Adapter<T> {
+     /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+     unsafe extern "C" fn match_phy_device_callback(
+         phydev: *mut bindings::phy_device,
++        _phydrv: *const bindings::phy_driver,
+     ) -> crate::ffi::c_int {
+         // SAFETY: This callback is called only in contexts
+         // where we hold `phy_device->lock`, so the accessors on
diff --git a/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch b/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch
new file mode 100644 (file)
index 0000000..9e28011
--- /dev/null
@@ -0,0 +1,109 @@
+From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Sat, 17 May 2025 22:13:48 +0200
+Subject: [PATCH 4/5] net: phy: introduce genphy_match_phy_device()
+
+Introduce new API, genphy_match_phy_device(), to provide a way to check
+to match a PHY driver for a PHY device based on the info stored in the
+PHY device struct.
+
+The function generalize the logic used in phy_bus_match() to check the
+PHY ID whether if C45 or C22 ID should be used for matching.
+
+This is useful for custom .match_phy_device function that wants to use
+the generic logic under some condition. (example a PHY is already setup
+and provide the correct PHY ID)
+
+Reviewed-by: Russell King (Oracle) <[email protected]>
+Signed-off-by: Christian Marangi <[email protected]>
+Link: https://patch.msgid.link/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/phy_device.c | 52 +++++++++++++++++++++++++-----------
+ include/linux/phy.h          |  3 +++
+ 2 files changed, 40 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -589,20 +589,26 @@ static int phy_scan_fixups(struct phy_de
+       return 0;
+ }
+-static int phy_bus_match(struct device *dev, const struct device_driver *drv)
++/**
++ * genphy_match_phy_device - match a PHY device with a PHY driver
++ * @phydev: target phy_device struct
++ * @phydrv: target phy_driver struct
++ *
++ * Description: Checks whether the given PHY device matches the specified
++ * PHY driver. For Clause 45 PHYs, iterates over the available device
++ * identifiers and compares them against the driver's expected PHY ID,
++ * applying the provided mask. For Clause 22 PHYs, a direct ID comparison
++ * is performed.
++ *
++ * Return: 1 if the PHY device matches the driver, 0 otherwise.
++ */
++int genphy_match_phy_device(struct phy_device *phydev,
++                          const struct phy_driver *phydrv)
+ {
+-      struct phy_device *phydev = to_phy_device(dev);
+-      const struct phy_driver *phydrv = to_phy_driver(drv);
+-      const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
+-      int i;
+-
+-      if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
+-              return 0;
+-
+-      if (phydrv->match_phy_device)
+-              return phydrv->match_phy_device(phydev, phydrv);
+-
+       if (phydev->is_c45) {
++              const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
++              int i;
++
+               for (i = 1; i < num_ids; i++) {
+                       if (phydev->c45_ids.device_ids[i] == 0xffffffff)
+                               continue;
+@@ -611,11 +617,27 @@ static int phy_bus_match(struct device *
+                                          phydrv->phy_id, phydrv->phy_id_mask))
+                               return 1;
+               }
++
+               return 0;
+-      } else {
+-              return phy_id_compare(phydev->phy_id, phydrv->phy_id,
+-                                    phydrv->phy_id_mask);
+       }
++
++      return phy_id_compare(phydev->phy_id, phydrv->phy_id,
++                            phydrv->phy_id_mask);
++}
++EXPORT_SYMBOL_GPL(genphy_match_phy_device);
++
++static int phy_bus_match(struct device *dev, const struct device_driver *drv)
++{
++      struct phy_device *phydev = to_phy_device(dev);
++      const struct phy_driver *phydrv = to_phy_driver(drv);
++
++      if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
++              return 0;
++
++      if (phydrv->match_phy_device)
++              return phydrv->match_phy_device(phydev, phydrv);
++
++      return genphy_match_phy_device(phydev, phydrv);
+ }
+ static ssize_t
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1906,6 +1906,9 @@ char *phy_attached_info_irq(struct phy_d
+       __malloc;
+ void phy_attached_info(struct phy_device *phydev);
++int genphy_match_phy_device(struct phy_device *phydev,
++                          const struct phy_driver *phydrv);
++
+ /* Clause 22 PHY */
+ int genphy_read_abilities(struct phy_device *phydev);
+ int genphy_setup_forced(struct phy_device *phydev);
diff --git a/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch b/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch
new file mode 100644 (file)
index 0000000..bcf9b1a
--- /dev/null
@@ -0,0 +1,1165 @@
+From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Sat, 17 May 2025 22:13:49 +0200
+Subject: [PATCH 5/5] net: phy: Add support for Aeonsemi AS21xxx PHYs
+
+Add support for Aeonsemi AS21xxx 10G C45 PHYs. These PHYs integrate
+an IPC to setup some configuration and require special handling to
+sync with the parity bit. The parity bit is a way the IPC use to
+follow correct order of command sent.
+
+Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
+AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
+AS21210PB1 that all register with the PHY ID 0x7500 0x7510
+before the firmware is loaded.
+
+They all support up to 5 LEDs with various HW mode supported.
+
+While implementing it was found some strange coincidence with using the
+same logic for implementing C22 in MMD regs in Broadcom PHYs.
+
+For reference here the AS21xxx PHY name logic:
+
+AS21x1xxB1
+    ^ ^^
+    | |J: Supports SyncE/PTP
+    | |P: No SyncE/PTP support
+    | 1: Supports 2nd Serdes
+    | 2: Not 2nd Serdes support
+    0: 10G, 5G, 2.5G
+    5: 5G, 2.5G
+    2: 2.5G
+
+Reviewed-by: Andrew Lunn <[email protected]>
+Signed-off-by: Christian Marangi <[email protected]>
+Link: https://patch.msgid.link/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ MAINTAINERS               |    6 +
+ drivers/net/phy/Kconfig   |   12 +
+ drivers/net/phy/Makefile  |    1 +
+ drivers/net/phy/as21xxx.c | 1087 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 1106 insertions(+)
+ create mode 100644 drivers/net/phy/as21xxx.c
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -79,6 +79,18 @@ config SFP
+ comment "MII PHY device drivers"
++config AS21XXX_PHY
++      tristate "Aeonsemi AS21xxx PHYs"
++      help
++        Currently supports the Aeonsemi AS21xxx PHY.
++
++        These are C45 PHYs 10G that require all a generic firmware.
++
++        Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
++        AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
++        AS21210PB1 that all register with the PHY ID 0x7500 0x7500
++        before the firmware is loaded.
++
+ config AIR_EN8811H_PHY
+       tristate "Airoha EN8811H 2.5 Gigabit PHY"
+       help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -39,6 +39,7 @@ obj-$(CONFIG_AIR_EN8811H_PHY)   += air_e
+ obj-$(CONFIG_AMD_PHY)         += amd.o
+ obj-$(CONFIG_AMCC_QT2025_PHY) += qt2025.o
+ obj-$(CONFIG_AQUANTIA_PHY)    += aquantia/
++obj-$(CONFIG_AS21XXX_PHY)     += as21xxx.o
+ ifdef CONFIG_AX88796B_RUST_PHY
+   obj-$(CONFIG_AX88796B_PHY)  += ax88796b_rust.o
+ else
+--- /dev/null
++++ b/drivers/net/phy/as21xxx.c
+@@ -0,0 +1,1087 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Aeonsemi AS21XXxX PHY Driver
++ *
++ * Author: Christian Marangi <[email protected]>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/firmware.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/phy.h>
++
++#define VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR 0x3
++#define VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR 0x4
++
++#define VEND1_GLB_REG_CPU_CTRL                0xe
++#define   VEND1_GLB_CPU_CTRL_MASK     GENMASK(4, 0)
++#define   VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK GENMASK(12, 8)
++#define   VEND1_GLB_CPU_CTRL_LED_POLARITY(_n) FIELD_PREP(VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK, \
++                                                       BIT(_n))
++
++#define VEND1_FW_START_ADDR           0x100
++
++#define VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD 0x101
++#define VEND1_GLB_REG_MDIO_INDIRECT_LOAD 0x102
++
++#define VEND1_GLB_REG_MDIO_INDIRECT_STATUS 0x103
++
++#define VEND1_PTP_CLK                 0x142
++#define   VEND1_PTP_CLK_EN            BIT(6)
++
++/* 5 LED at step of 0x20
++ * FE: Fast-Ethernet (10/100)
++ * GE: Gigabit-Ethernet (1000)
++ * NG: New-Generation (2500/5000/10000)
++ */
++#define VEND1_LED_REG(_n)             (0x1800 + ((_n) * 0x10))
++#define   VEND1_LED_REG_A_EVENT               GENMASK(15, 11)
++#define VEND1_LED_CONF                        0x1881
++#define   VEND1_LED_CONFG_BLINK               GENMASK(7, 0)
++
++#define VEND1_SPEED_STATUS            0x4002
++#define   VEND1_SPEED_MASK            GENMASK(7, 0)
++#define   VEND1_SPEED_10000           FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x3)
++#define   VEND1_SPEED_5000            FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x5)
++#define   VEND1_SPEED_2500            FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x9)
++#define   VEND1_SPEED_1000            FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x10)
++#define   VEND1_SPEED_100             FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x20)
++#define   VEND1_SPEED_10              FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x0)
++
++#define VEND1_IPC_CMD                 0x5801
++#define   AEON_IPC_CMD_PARITY         BIT(15)
++#define   AEON_IPC_CMD_SIZE           GENMASK(10, 6)
++#define   AEON_IPC_CMD_OPCODE         GENMASK(5, 0)
++
++#define IPC_CMD_NOOP                  0x0  /* Do nothing */
++#define IPC_CMD_INFO                  0x1  /* Get Firmware Version */
++#define IPC_CMD_SYS_CPU                       0x2  /* SYS_CPU */
++#define IPC_CMD_BULK_DATA             0xa  /* Pass bulk data in ipc registers. */
++#define IPC_CMD_BULK_WRITE            0xc  /* Write bulk data to memory */
++#define IPC_CMD_CFG_PARAM             0x1a /* Write config parameters to memory */
++#define IPC_CMD_NG_TESTMODE           0x1b /* Set NG test mode and tone */
++#define IPC_CMD_TEMP_MON              0x15 /* Temperature monitoring function */
++#define IPC_CMD_SET_LED                       0x23 /* Set led */
++
++#define VEND1_IPC_STS                 0x5802
++#define   AEON_IPC_STS_PARITY         BIT(15)
++#define   AEON_IPC_STS_SIZE           GENMASK(14, 10)
++#define   AEON_IPC_STS_OPCODE         GENMASK(9, 4)
++#define   AEON_IPC_STS_STATUS         GENMASK(3, 0)
++#define   AEON_IPC_STS_STATUS_RCVD    FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x1)
++#define   AEON_IPC_STS_STATUS_PROCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x2)
++#define   AEON_IPC_STS_STATUS_SUCCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x4)
++#define   AEON_IPC_STS_STATUS_ERROR   FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x8)
++#define   AEON_IPC_STS_STATUS_BUSY    FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xe)
++#define   AEON_IPC_STS_STATUS_READY   FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xf)
++
++#define VEND1_IPC_DATA0                       0x5808
++#define VEND1_IPC_DATA1                       0x5809
++#define VEND1_IPC_DATA2                       0x580a
++#define VEND1_IPC_DATA3                       0x580b
++#define VEND1_IPC_DATA4                       0x580c
++#define VEND1_IPC_DATA5                       0x580d
++#define VEND1_IPC_DATA6                       0x580e
++#define VEND1_IPC_DATA7                       0x580f
++#define VEND1_IPC_DATA(_n)            (VEND1_IPC_DATA0 + (_n))
++
++/* Sub command of CMD_INFO */
++#define IPC_INFO_VERSION              0x1
++
++/* Sub command of CMD_SYS_CPU */
++#define IPC_SYS_CPU_REBOOT            0x3
++#define IPC_SYS_CPU_IMAGE_OFST                0x4
++#define IPC_SYS_CPU_IMAGE_CHECK               0x5
++#define IPC_SYS_CPU_PHY_ENABLE                0x6
++
++/* Sub command of CMD_CFG_PARAM */
++#define IPC_CFG_PARAM_DIRECT          0x4
++
++/* CFG DIRECT sub command */
++#define IPC_CFG_PARAM_DIRECT_NG_PHYCTRL       0x1
++#define IPC_CFG_PARAM_DIRECT_CU_AN    0x2
++#define IPC_CFG_PARAM_DIRECT_SDS_PCS  0x3
++#define IPC_CFG_PARAM_DIRECT_AUTO_EEE 0x4
++#define IPC_CFG_PARAM_DIRECT_SDS_PMA  0x5
++#define IPC_CFG_PARAM_DIRECT_DPC_RA   0x6
++#define IPC_CFG_PARAM_DIRECT_DPC_PKT_CHK 0x7
++#define IPC_CFG_PARAM_DIRECT_DPC_SDS_WAIT_ETH 0x8
++#define IPC_CFG_PARAM_DIRECT_WDT      0x9
++#define IPC_CFG_PARAM_DIRECT_SDS_RESTART_AN 0x10
++#define IPC_CFG_PARAM_DIRECT_TEMP_MON 0x11
++#define IPC_CFG_PARAM_DIRECT_WOL      0x12
++
++/* Sub command of CMD_TEMP_MON */
++#define IPC_CMD_TEMP_MON_GET          0x4
++
++#define AS21XXX_MDIO_AN_C22           0xffe0
++
++#define PHY_ID_AS21XXX                        0x75009410
++/* AS21xxx ID Legend
++ * AS21x1xxB1
++ *     ^ ^^
++ *     | |J: Supports SyncE/PTP
++ *     | |P: No SyncE/PTP support
++ *     | 1: Supports 2nd Serdes
++ *     | 2: Not 2nd Serdes support
++ *     0: 10G, 5G, 2.5G
++ *     5: 5G, 2.5G
++ *     2: 2.5G
++ */
++#define PHY_ID_AS21011JB1             0x75009402
++#define PHY_ID_AS21011PB1             0x75009412
++#define PHY_ID_AS21010JB1             0x75009422
++#define PHY_ID_AS21010PB1             0x75009432
++#define PHY_ID_AS21511JB1             0x75009442
++#define PHY_ID_AS21511PB1             0x75009452
++#define PHY_ID_AS21510JB1             0x75009462
++#define PHY_ID_AS21510PB1             0x75009472
++#define PHY_ID_AS21210JB1             0x75009482
++#define PHY_ID_AS21210PB1             0x75009492
++#define PHY_VENDOR_AEONSEMI           0x75009400
++
++#define AEON_MAX_LEDS                 5
++#define AEON_IPC_DELAY                        10000
++#define AEON_IPC_TIMEOUT              (AEON_IPC_DELAY * 100)
++#define AEON_IPC_DATA_NUM_REGISTERS   8
++#define AEON_IPC_DATA_MAX             (AEON_IPC_DATA_NUM_REGISTERS * sizeof(u16))
++
++#define AEON_BOOT_ADDR                        0x1000
++#define AEON_CPU_BOOT_ADDR            0x2000
++#define AEON_CPU_CTRL_FW_LOAD         (BIT(4) | BIT(2) | BIT(1) | BIT(0))
++#define AEON_CPU_CTRL_FW_START                BIT(0)
++
++enum as21xxx_led_event {
++      VEND1_LED_REG_A_EVENT_ON_10 = 0x0,
++      VEND1_LED_REG_A_EVENT_ON_100,
++      VEND1_LED_REG_A_EVENT_ON_1000,
++      VEND1_LED_REG_A_EVENT_ON_2500,
++      VEND1_LED_REG_A_EVENT_ON_5000,
++      VEND1_LED_REG_A_EVENT_ON_10000,
++      VEND1_LED_REG_A_EVENT_ON_FE_GE,
++      VEND1_LED_REG_A_EVENT_ON_NG,
++      VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX,
++      VEND1_LED_REG_A_EVENT_ON_COLLISION,
++      VEND1_LED_REG_A_EVENT_BLINK_TX,
++      VEND1_LED_REG_A_EVENT_BLINK_RX,
++      VEND1_LED_REG_A_EVENT_BLINK_ACT,
++      VEND1_LED_REG_A_EVENT_ON_LINK,
++      VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT,
++      VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX,
++      VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT,
++      VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT,
++      VEND1_LED_REG_A_EVENT_ON_NG_BLINK_FE_GE,
++      VEND1_LED_REG_A_EVENT_ON_FD_BLINK_COLLISION,
++      VEND1_LED_REG_A_EVENT_ON,
++      VEND1_LED_REG_A_EVENT_OFF,
++};
++
++struct as21xxx_led_pattern_info {
++      unsigned int pattern;
++      u16 val;
++};
++
++struct as21xxx_priv {
++      bool parity_status;
++      /* Protect concurrent IPC access */
++      struct mutex ipc_lock;
++};
++
++static struct as21xxx_led_pattern_info as21xxx_led_supported_pattern[] = {
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10),
++              .val = VEND1_LED_REG_A_EVENT_ON_10
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_100),
++              .val = VEND1_LED_REG_A_EVENT_ON_100
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_1000),
++              .val = VEND1_LED_REG_A_EVENT_ON_1000
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_2500),
++              .val = VEND1_LED_REG_A_EVENT_ON_2500
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_5000),
++              .val = VEND1_LED_REG_A_EVENT_ON_5000
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10000),
++              .val = VEND1_LED_REG_A_EVENT_ON_10000
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK),
++              .val = VEND1_LED_REG_A_EVENT_ON_LINK
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000),
++              .val = VEND1_LED_REG_A_EVENT_ON_FE_GE
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000),
++              .val = VEND1_LED_REG_A_EVENT_ON_NG
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
++              .val = VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_TX),
++              .val = VEND1_LED_REG_A_EVENT_BLINK_TX
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_BLINK_RX
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_TX) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_BLINK_ACT
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000) |
++                         BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000),
++              .val = VEND1_LED_REG_A_EVENT_ON_LINK
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000) |
++                         BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000) |
++                         BIT(TRIGGER_NETDEV_TX) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000) |
++                         BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000) |
++                         BIT(TRIGGER_NETDEV_TX) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000) |
++                         BIT(TRIGGER_NETDEV_TX) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT
++      }
++};
++
++static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data,
++                            size_t size)
++{
++      int i, ret;
++      u16 val;
++
++      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL,
++                           VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_LOAD);
++      if (ret)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_FW_START_ADDR,
++                          AEON_BOOT_ADDR);
++      if (ret)
++              return ret;
++
++      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++                           VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD,
++                           0x3ffc, 0xc000);
++      if (ret)
++              return ret;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++                         VEND1_GLB_REG_MDIO_INDIRECT_STATUS);
++      if (val > 1) {
++              phydev_err(phydev, "wrong origin mdio_indirect_status: %x\n", val);
++              return -EINVAL;
++      }
++
++      /* Firmware is always aligned to u16 */
++      for (i = 0; i < size; i += 2) {
++              val = data[i + 1] << 8 | data[i];
++
++              ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++                                  VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val);
++              if (ret)
++                      return ret;
++      }
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++                          VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR,
++                          lower_16_bits(AEON_CPU_BOOT_ADDR));
++      if (ret)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++                          VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR,
++                          upper_16_bits(AEON_CPU_BOOT_ADDR));
++      if (ret)
++              return ret;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL,
++                            VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_START);
++}
++
++static int aeon_firmware_load(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      const struct firmware *fw;
++      const char *fw_name;
++      int ret;
++
++      ret = of_property_read_string(dev->of_node, "firmware-name",
++                                    &fw_name);
++      if (ret)
++              return ret;
++
++      ret = request_firmware(&fw, fw_name, dev);
++      if (ret) {
++              phydev_err(phydev, "failed to find FW file %s (%d)\n",
++                         fw_name, ret);
++              return ret;
++      }
++
++      ret = aeon_firmware_boot(phydev, fw->data, fw->size);
++
++      release_firmware(fw);
++
++      return ret;
++}
++
++static bool aeon_ipc_ready(u16 val, bool parity_status)
++{
++      u16 status;
++
++      if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status)
++              return false;
++
++      status = val & AEON_IPC_STS_STATUS;
++
++      return status != AEON_IPC_STS_STATUS_RCVD &&
++             status != AEON_IPC_STS_STATUS_PROCESS &&
++             status != AEON_IPC_STS_STATUS_BUSY;
++}
++
++static int aeon_ipc_wait_cmd(struct phy_device *phydev, bool parity_status)
++{
++      u16 val;
++
++      /* Exit condition logic:
++       * - Wait for parity bit equal
++       * - Wait for status success, error OR ready
++       */
++      return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val,
++                                       aeon_ipc_ready(val, parity_status),
++                                       AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false);
++}
++
++static int aeon_ipc_send_cmd(struct phy_device *phydev,
++                           struct as21xxx_priv *priv,
++                           u16 cmd, u16 *ret_sts)
++{
++      bool curr_parity;
++      int ret;
++
++      /* The IPC sync by using a single parity bit.
++       * Each CMD have alternately this bit set or clear
++       * to understand correct flow and packet order.
++       */
++      curr_parity = priv->parity_status;
++      if (priv->parity_status)
++              cmd |= AEON_IPC_CMD_PARITY;
++
++      /* Always update parity for next packet */
++      priv->parity_status = !priv->parity_status;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd);
++      if (ret)
++              return ret;
++
++      /* Wait for packet to be processed */
++      usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000);
++
++      /* With no ret_sts, ignore waiting for packet completion
++       * (ipc parity bit sync)
++       */
++      if (!ret_sts)
++              return 0;
++
++      ret = aeon_ipc_wait_cmd(phydev, curr_parity);
++      if (ret)
++              return ret;
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS);
++      if (ret < 0)
++              return ret;
++
++      *ret_sts = ret;
++      if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS)
++              return -EINVAL;
++
++      return 0;
++}
++
++/* If data is NULL, return 0 or negative error.
++ * If data not NULL, return number of Bytes received from IPC or
++ * a negative error.
++ */
++static int aeon_ipc_send_msg(struct phy_device *phydev,
++                           u16 opcode, u16 *data, unsigned int data_len,
++                           u16 *ret_data)
++{
++      struct as21xxx_priv *priv = phydev->priv;
++      unsigned int ret_size;
++      u16 cmd, ret_sts;
++      int ret;
++      int i;
++
++      /* IPC have a max of 8 register to transfer data,
++       * make sure we never exceed this.
++       */
++      if (data_len > AEON_IPC_DATA_MAX)
++              return -EINVAL;
++
++      for (i = 0; i < data_len / sizeof(u16); i++)
++              phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i),
++                            data[i]);
++
++      cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) |
++            FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode);
++
++      mutex_lock(&priv->ipc_lock);
++
++      ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts);
++      if (ret) {
++              phydev_err(phydev, "failed to send ipc msg for %x: %d\n",
++                         opcode, ret);
++              goto out;
++      }
++
++      if (!data)
++              goto out;
++
++      if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      /* Prevent IPC from stack smashing the kernel.
++       * We can't trust IPC to return a good value and we always
++       * preallocate space for 16 Bytes.
++       */
++      ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts);
++      if (ret_size > AEON_IPC_DATA_MAX) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      /* Read data from IPC data register for ret_size value from IPC */
++      for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) {
++              ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i));
++              if (ret < 0)
++                      goto out;
++
++              ret_data[i] = ret;
++      }
++
++      ret = ret_size;
++
++out:
++      mutex_unlock(&priv->ipc_lock);
++
++      return ret;
++}
++
++static int aeon_ipc_noop(struct phy_device *phydev,
++                       struct as21xxx_priv *priv, u16 *ret_sts)
++{
++      u16 cmd;
++
++      cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) |
++            FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_NOOP);
++
++      return aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts);
++}
++
++/* Logic to sync parity bit with IPC.
++ * We send 2 NOP cmd with same partity and we wait for IPC
++ * to handle the packet only for the second one. This way
++ * we make sure we are sync for every next cmd.
++ */
++static int aeon_ipc_sync_parity(struct phy_device *phydev,
++                              struct as21xxx_priv *priv)
++{
++      u16 ret_sts;
++      int ret;
++
++      mutex_lock(&priv->ipc_lock);
++
++      /* Send NOP with no parity */
++      aeon_ipc_noop(phydev, priv, NULL);
++
++      /* Reset packet parity */
++      priv->parity_status = false;
++
++      /* Send second NOP with no parity */
++      ret = aeon_ipc_noop(phydev, priv, &ret_sts);
++
++      mutex_unlock(&priv->ipc_lock);
++
++      /* We expect to return -EINVAL */
++      if (ret != -EINVAL)
++              return ret;
++
++      if ((ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_READY) {
++              phydev_err(phydev, "Invalid IPC status on sync parity: %x\n",
++                         ret_sts);
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int aeon_ipc_get_fw_version(struct phy_device *phydev)
++{
++      u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS], data[1];
++      char fw_version[AEON_IPC_DATA_MAX + 1];
++      int ret;
++
++      data[0] = IPC_INFO_VERSION;
++
++      ret = aeon_ipc_send_msg(phydev, IPC_CMD_INFO, data,
++                              sizeof(data), ret_data);
++      if (ret < 0)
++              return ret;
++
++      /* Make sure FW version is NULL terminated */
++      memcpy(fw_version, ret_data, ret);
++      fw_version[ret] = '\0';
++
++      phydev_info(phydev, "Firmware Version: %s\n", fw_version);
++
++      return 0;
++}
++
++static int aeon_dpc_ra_enable(struct phy_device *phydev)
++{
++      u16 data[2];
++
++      data[0] = IPC_CFG_PARAM_DIRECT;
++      data[1] = IPC_CFG_PARAM_DIRECT_DPC_RA;
++
++      return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data,
++                               sizeof(data), NULL);
++}
++
++static int as21xxx_probe(struct phy_device *phydev)
++{
++      struct as21xxx_priv *priv;
++      int ret;
++
++      priv = devm_kzalloc(&phydev->mdio.dev,
++                          sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++      phydev->priv = priv;
++
++      ret = devm_mutex_init(&phydev->mdio.dev,
++                            &priv->ipc_lock);
++      if (ret)
++              return ret;
++
++      ret = aeon_ipc_sync_parity(phydev, priv);
++      if (ret)
++              return ret;
++
++      ret = aeon_ipc_get_fw_version(phydev);
++      if (ret)
++              return ret;
++
++      /* Enable PTP clk if not already Enabled */
++      ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK,
++                             VEND1_PTP_CLK_EN);
++      if (ret)
++              return ret;
++
++      return aeon_dpc_ra_enable(phydev);
++}
++
++static int as21xxx_read_link(struct phy_device *phydev, int *bmcr)
++{
++      int status;
++
++      /* Normal C22 BMCR report inconsistent data, use
++       * the mapped C22 in C45 to have more consistent link info.
++       */
++      *bmcr = phy_read_mmd(phydev, MDIO_MMD_AN,
++                           AS21XXX_MDIO_AN_C22 + MII_BMCR);
++      if (*bmcr < 0)
++              return *bmcr;
++
++      /* Autoneg is being started, therefore disregard current
++       * link status and report link as down.
++       */
++      if (*bmcr & BMCR_ANRESTART) {
++              phydev->link = 0;
++              return 0;
++      }
++
++      status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
++      if (status < 0)
++              return status;
++
++      phydev->link = !!(status & MDIO_STAT1_LSTATUS);
++
++      return 0;
++}
++
++static int as21xxx_read_c22_lpa(struct phy_device *phydev)
++{
++      int lpagb;
++
++      /* MII_STAT1000 are only filled in the mapped C22
++       * in C45, use that to fill lpagb values and check.
++       */
++      lpagb = phy_read_mmd(phydev, MDIO_MMD_AN,
++                           AS21XXX_MDIO_AN_C22 + MII_STAT1000);
++      if (lpagb < 0)
++              return lpagb;
++
++      if (lpagb & LPA_1000MSFAIL) {
++              int adv = phy_read_mmd(phydev, MDIO_MMD_AN,
++                                     AS21XXX_MDIO_AN_C22 + MII_CTRL1000);
++
++              if (adv < 0)
++                      return adv;
++
++              if (adv & CTL1000_ENABLE_MASTER)
++                      phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
++              else
++                      phydev_err(phydev, "Master/Slave resolution failed\n");
++              return -ENOLINK;
++      }
++
++      mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
++                                      lpagb);
++
++      return 0;
++}
++
++static int as21xxx_read_status(struct phy_device *phydev)
++{
++      int bmcr, old_link = phydev->link;
++      int ret;
++
++      ret = as21xxx_read_link(phydev, &bmcr);
++      if (ret)
++              return ret;
++
++      /* why bother the PHY if nothing can have changed */
++      if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
++              return 0;
++
++      phydev->speed = SPEED_UNKNOWN;
++      phydev->duplex = DUPLEX_UNKNOWN;
++      phydev->pause = 0;
++      phydev->asym_pause = 0;
++
++      if (phydev->autoneg == AUTONEG_ENABLE) {
++              ret = genphy_c45_read_lpa(phydev);
++              if (ret)
++                      return ret;
++
++              ret = as21xxx_read_c22_lpa(phydev);
++              if (ret)
++                      return ret;
++
++              phy_resolve_aneg_linkmode(phydev);
++      } else {
++              int speed;
++
++              linkmode_zero(phydev->lp_advertising);
++
++              speed = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++                                   VEND1_SPEED_STATUS);
++              if (speed < 0)
++                      return speed;
++
++              switch (speed & VEND1_SPEED_STATUS) {
++              case VEND1_SPEED_10000:
++                      phydev->speed = SPEED_10000;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              case VEND1_SPEED_5000:
++                      phydev->speed = SPEED_5000;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              case VEND1_SPEED_2500:
++                      phydev->speed = SPEED_2500;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              case VEND1_SPEED_1000:
++                      phydev->speed = SPEED_1000;
++                      if (bmcr & BMCR_FULLDPLX)
++                              phydev->duplex = DUPLEX_FULL;
++                      else
++                              phydev->duplex = DUPLEX_HALF;
++                      break;
++              case VEND1_SPEED_100:
++                      phydev->speed = SPEED_100;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              case VEND1_SPEED_10:
++                      phydev->speed = SPEED_10;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      return 0;
++}
++
++static int as21xxx_led_brightness_set(struct phy_device *phydev,
++                                    u8 index, enum led_brightness value)
++{
++      u16 val = VEND1_LED_REG_A_EVENT_OFF;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      if (value)
++              val = VEND1_LED_REG_A_EVENT_ON;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++                            VEND1_LED_REG(index),
++                            VEND1_LED_REG_A_EVENT,
++                            FIELD_PREP(VEND1_LED_REG_A_EVENT, val));
++}
++
++static int as21xxx_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                     unsigned long rules)
++{
++      int i;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++              if (rules == as21xxx_led_supported_pattern[i].pattern)
++                      return 0;
++
++      return -EOPNOTSUPP;
++}
++
++static int as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                    unsigned long *rules)
++{
++      int i, val;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index));
++      if (val < 0)
++              return val;
++
++      val = FIELD_GET(VEND1_LED_REG_A_EVENT, val);
++      for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++              if (val == as21xxx_led_supported_pattern[i].val) {
++                      *rules = as21xxx_led_supported_pattern[i].pattern;
++                      return 0;
++              }
++
++      /* Should be impossible */
++      return -EINVAL;
++}
++
++static int as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                    unsigned long rules)
++{
++      u16 val = 0;
++      int i;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++              if (rules == as21xxx_led_supported_pattern[i].pattern) {
++                      val = as21xxx_led_supported_pattern[i].val;
++                      break;
++              }
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++                            VEND1_LED_REG(index),
++                            VEND1_LED_REG_A_EVENT,
++                            FIELD_PREP(VEND1_LED_REG_A_EVENT, val));
++}
++
++static int as21xxx_led_polarity_set(struct phy_device *phydev, int index,
++                                  unsigned long modes)
++{
++      bool led_active_low = false;
++      u16 mask, val = 0;
++      u32 mode;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++              switch (mode) {
++              case PHY_LED_ACTIVE_LOW:
++                      led_active_low = true;
++                      break;
++              case PHY_LED_ACTIVE_HIGH: /* default mode */
++                      led_active_low = false;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      mask = VEND1_GLB_CPU_CTRL_LED_POLARITY(index);
++      if (led_active_low)
++              val = VEND1_GLB_CPU_CTRL_LED_POLARITY(index);
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++                            VEND1_GLB_REG_CPU_CTRL,
++                            mask, val);
++}
++
++static int as21xxx_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
++{
++      struct as21xxx_priv *priv;
++      u16 ret_sts;
++      u32 phy_id;
++      int ret;
++
++      /* Skip PHY that are not AS21xxx or already have firmware loaded */
++      if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX)
++              return genphy_match_phy_device(phydev, (struct phy_driver *)phydrv);
++
++      /* Read PHY ID to handle firmware just loaded */
++      ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1);
++      if (ret < 0)
++              return ret;
++      phy_id = ret << 16;
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2);
++      if (ret < 0)
++              return ret;
++      phy_id |= ret;
++
++      /* With PHY ID not the generic AS21xxx one assume
++       * the firmware just loaded
++       */
++      if (phy_id != PHY_ID_AS21XXX)
++              return phy_id == phydrv->phy_id;
++
++      /* Allocate temp priv and load the firmware */
++      priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      mutex_init(&priv->ipc_lock);
++
++      ret = aeon_firmware_load(phydev);
++      if (ret)
++              goto out;
++
++      /* Sync parity... */
++      ret = aeon_ipc_sync_parity(phydev, priv);
++      if (ret)
++              goto out;
++
++      /* ...and send a third NOOP cmd to wait for firmware finish loading */
++      ret = aeon_ipc_noop(phydev, priv, &ret_sts);
++      if (ret)
++              goto out;
++
++out:
++      mutex_destroy(&priv->ipc_lock);
++      kfree(priv);
++
++      /* Return can either be 0 or a negative error code.
++       * Returning 0 here means THIS is NOT a suitable PHY.
++       *
++       * For the specific case of the generic Aeonsemi PHY ID that
++       * needs the firmware the be loaded first to have a correct PHY ID,
++       * this is OK as a matching PHY ID will be found right after.
++       * This relies on the driver probe order where the first PHY driver
++       * probed is the generic one.
++       */
++      return ret;
++}
++
++static struct phy_driver as21xxx_drivers[] = {
++      {
++              /* PHY expose in C45 as 0x7500 0x9410
++               * before firmware is loaded.
++               * This driver entry must be attempted first to load
++               * the firmware and thus update the ID registers.
++               */
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21XXX),
++              .name           = "Aeonsemi AS21xxx",
++              .match_phy_device = as21xxx_match_phy_device,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21011JB1),
++              .name           = "Aeonsemi AS21011JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1),
++              .name           = "Aeonsemi AS21011PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1),
++              .name           = "Aeonsemi AS21010PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1),
++              .name           = "Aeonsemi AS21010JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1),
++              .name           = "Aeonsemi AS21210PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1),
++              .name           = "Aeonsemi AS21510JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1),
++              .name           = "Aeonsemi AS21510PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1),
++              .name           = "Aeonsemi AS21511JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1),
++              .name           = "Aeonsemi AS21210JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1),
++              .name           = "Aeonsemi AS21511PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++};
++module_phy_driver(as21xxx_drivers);
++
++static struct mdio_device_id __maybe_unused as21xxx_tbl[] = {
++      { PHY_ID_MATCH_VENDOR(PHY_VENDOR_AEONSEMI) },
++      { }
++};
++MODULE_DEVICE_TABLE(mdio, as21xxx_tbl);
++
++MODULE_DESCRIPTION("Aeonsemi AS21xxx PHY driver");
++MODULE_AUTHOR("Christian Marangi <[email protected]>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch b/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch
new file mode 100644 (file)
index 0000000..b8460a2
--- /dev/null
@@ -0,0 +1,273 @@
+From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Sat, 17 May 2025 22:13:45 +0200
+Subject: [PATCH 1/5] net: phy: pass PHY driver to .match_phy_device OP
+
+Pass PHY driver pointer to .match_phy_device OP in addition to phydev.
+Having access to the PHY driver struct might be useful to check the
+PHY ID of the driver is being matched for in case the PHY ID scanned in
+the phydev is not consistent.
+
+A scenario for this is a PHY that change PHY ID after a firmware is
+loaded, in such case, the PHY ID stored in PHY device struct is not
+valid anymore and PHY will manually scan the ID in the match_phy_device
+function.
+
+Having the PHY driver info is also useful for those PHY driver that
+implement multiple simple .match_phy_device OP to match specific MMD PHY
+ID. With this extra info if the parsing logic is the same, the matching
+function can be generalized by using the phy_id in the PHY driver
+instead of hardcoding.
+
+Rust wrapper callback is updated to align to the new match_phy_device
+arguments.
+
+Suggested-by: Russell King (Oracle) <[email protected]>
+Reviewed-by: Russell King (Oracle) <[email protected]>
+Signed-off-by: Christian Marangi <[email protected]>
+Reviewed-by: Benno Lossin <[email protected]> # for Rust
+Reviewed-by: FUJITA Tomonori <[email protected]>
+Link: https://patch.msgid.link/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/bcm87xx.c              |  6 ++++--
+ drivers/net/phy/icplus.c               |  6 ++++--
+ drivers/net/phy/marvell10g.c           | 12 ++++++++----
+ drivers/net/phy/micrel.c               |  6 ++++--
+ drivers/net/phy/nxp-c45-tja11xx.c      | 12 ++++++++----
+ drivers/net/phy/nxp-tja11xx.c          |  6 ++++--
+ drivers/net/phy/phy_device.c           |  2 +-
+ drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++---------
+ drivers/net/phy/teranetics.c           |  3 ++-
+ include/linux/phy.h                    |  3 ++-
+ rust/kernel/net/phy.rs                 |  1 +
+ 11 files changed, 56 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/phy/bcm87xx.c
++++ b/drivers/net/phy/bcm87xx.c
+@@ -185,12 +185,14 @@ static irqreturn_t bcm87xx_handle_interr
+       return IRQ_HANDLED;
+ }
+-static int bcm8706_match_phy_device(struct phy_device *phydev)
++static int bcm8706_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706;
+ }
+-static int bcm8727_match_phy_device(struct phy_device *phydev)
++static int bcm8727_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727;
+ }
+--- a/drivers/net/phy/icplus.c
++++ b/drivers/net/phy/icplus.c
+@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(str
+       return ip101a == !ret;
+ }
+-static int ip101a_match_phy_device(struct phy_device *phydev)
++static int ip101a_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return ip101a_g_match_phy_device(phydev, true);
+ }
+-static int ip101g_match_phy_device(struct phy_device *phydev)
++static int ip101g_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return ip101a_g_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/marvell10g.c
++++ b/drivers/net/phy/marvell10g.c
+@@ -1221,7 +1221,8 @@ static int mv3310_get_number_of_ports(st
+       return ret + 1;
+ }
+-static int mv3310_match_phy_device(struct phy_device *phydev)
++static int mv3310_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
+            MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
+@@ -1230,7 +1231,8 @@ static int mv3310_match_phy_device(struc
+       return mv3310_get_number_of_ports(phydev) == 1;
+ }
+-static int mv3340_match_phy_device(struct phy_device *phydev)
++static int mv3340_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] &
+            MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310)
+@@ -1254,12 +1256,14 @@ static int mv211x_match_phy_device(struc
+       return !!(val & MDIO_PCS_SPEED_5G) == has_5g;
+ }
+-static int mv2110_match_phy_device(struct phy_device *phydev)
++static int mv2110_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return mv211x_match_phy_device(phydev, true);
+ }
+-static int mv2111_match_phy_device(struct phy_device *phydev)
++static int mv2111_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return mv211x_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/micrel.c
++++ b/drivers/net/phy/micrel.c
+@@ -670,7 +670,8 @@ static int ksz8051_ksz8795_match_phy_dev
+               return !ret;
+ }
+-static int ksz8051_match_phy_device(struct phy_device *phydev)
++static int ksz8051_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return ksz8051_ksz8795_match_phy_device(phydev, true);
+ }
+@@ -790,7 +791,8 @@ static int ksz8061_config_init(struct ph
+       return kszphy_config_init(phydev);
+ }
+-static int ksz8795_match_phy_device(struct phy_device *phydev)
++static int ksz8795_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return ksz8051_ksz8795_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/nxp-tja11xx.c
++++ b/drivers/net/phy/nxp-tja11xx.c
+@@ -648,12 +648,14 @@ static int tja1102_match_phy_device(stru
+       return !ret;
+ }
+-static int tja1102_p0_match_phy_device(struct phy_device *phydev)
++static int tja1102_p0_match_phy_device(struct phy_device *phydev,
++                                     const struct phy_driver *phydrv)
+ {
+       return tja1102_match_phy_device(phydev, true);
+ }
+-static int tja1102_p1_match_phy_device(struct phy_device *phydev)
++static int tja1102_p1_match_phy_device(struct phy_device *phydev,
++                                     const struct phy_driver *phydrv)
+ {
+       return tja1102_match_phy_device(phydev, false);
+ }
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -533,7 +533,7 @@ static int phy_bus_match(struct device *
+               return 0;
+       if (phydrv->match_phy_device)
+-              return phydrv->match_phy_device(phydev);
++              return phydrv->match_phy_device(phydev, phydrv);
+       if (phydev->is_c45) {
+               for (i = 1; i < num_ids; i++) {
+--- a/drivers/net/phy/realtek/realtek_main.c
++++ b/drivers/net/phy/realtek/realtek_main.c
+@@ -1315,13 +1315,15 @@ static bool rtlgen_supports_mmd(struct p
+       return val > 0;
+ }
+-static int rtlgen_match_phy_device(struct phy_device *phydev)
++static int rtlgen_match_phy_device(struct phy_device *phydev,
++                                 const struct phy_driver *phydrv)
+ {
+       return phydev->phy_id == RTL_GENERIC_PHYID &&
+              !rtlgen_supports_2_5gbps(phydev);
+ }
+-static int rtl8226_match_phy_device(struct phy_device *phydev)
++static int rtl8226_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
+ {
+       return phydev->phy_id == RTL_GENERIC_PHYID &&
+              rtlgen_supports_2_5gbps(phydev) &&
+@@ -1337,32 +1339,38 @@ static int rtlgen_is_c45_match(struct ph
+               return !is_c45 && (id == phydev->phy_id);
+ }
+-static int rtl8221b_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_match_phy_device(struct phy_device *phydev,
++                                   const struct phy_driver *phydrv)
+ {
+       return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev);
+ }
+-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev,
++                                             const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false);
+ }
+-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev,
++                                             const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
+ }
+-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev,
++                                             const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
+ }
+-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev,
++                                             const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
+ }
+-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev)
++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev,
++                                              const struct phy_driver *phydrv)
+ {
+       if (phydev->is_c45)
+               return false;
+@@ -1381,7 +1389,8 @@ static int rtl_internal_nbaset_match_phy
+       return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev);
+ }
+-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev)
++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev,
++                                       const struct phy_driver *phydrv)
+ {
+       return rtlgen_is_c45_match(phydev, RTL_8251B, true);
+ }
+--- a/drivers/net/phy/teranetics.c
++++ b/drivers/net/phy/teranetics.c
+@@ -67,7 +67,8 @@ static int teranetics_read_status(struct
+       return 0;
+ }
+-static int teranetics_match_phy_device(struct phy_device *phydev)
++static int teranetics_match_phy_device(struct phy_device *phydev,
++                                     const struct phy_driver *phydrv)
+ {
+       return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020;
+ }
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -972,7 +972,8 @@ struct phy_driver {
+        * driver for the given phydev.  If NULL, matching is based on
+        * phy_id and phy_id_mask.
+        */
+-      int (*match_phy_device)(struct phy_device *phydev);
++      int (*match_phy_device)(struct phy_device *phydev,
++                              const struct phy_driver *phydrv);
+       /**
+        * @set_wol: Some devices (e.g. qnap TS-119P II) require PHY
diff --git a/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch b/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch
new file mode 100644 (file)
index 0000000..1c0b5d8
--- /dev/null
@@ -0,0 +1,109 @@
+From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Sat, 17 May 2025 22:13:48 +0200
+Subject: [PATCH 4/5] net: phy: introduce genphy_match_phy_device()
+
+Introduce new API, genphy_match_phy_device(), to provide a way to check
+to match a PHY driver for a PHY device based on the info stored in the
+PHY device struct.
+
+The function generalize the logic used in phy_bus_match() to check the
+PHY ID whether if C45 or C22 ID should be used for matching.
+
+This is useful for custom .match_phy_device function that wants to use
+the generic logic under some condition. (example a PHY is already setup
+and provide the correct PHY ID)
+
+Reviewed-by: Russell King (Oracle) <[email protected]>
+Signed-off-by: Christian Marangi <[email protected]>
+Link: https://patch.msgid.link/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/phy_device.c | 52 +++++++++++++++++++++++++-----------
+ include/linux/phy.h          |  3 +++
+ 2 files changed, 40 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -522,20 +522,26 @@ static int phy_scan_fixups(struct phy_de
+       return 0;
+ }
+-static int phy_bus_match(struct device *dev, struct device_driver *drv)
++/**
++ * genphy_match_phy_device - match a PHY device with a PHY driver
++ * @phydev: target phy_device struct
++ * @phydrv: target phy_driver struct
++ *
++ * Description: Checks whether the given PHY device matches the specified
++ * PHY driver. For Clause 45 PHYs, iterates over the available device
++ * identifiers and compares them against the driver's expected PHY ID,
++ * applying the provided mask. For Clause 22 PHYs, a direct ID comparison
++ * is performed.
++ *
++ * Return: 1 if the PHY device matches the driver, 0 otherwise.
++ */
++int genphy_match_phy_device(struct phy_device *phydev,
++                          struct phy_driver *phydrv)
+ {
+-      struct phy_device *phydev = to_phy_device(dev);
+-      struct phy_driver *phydrv = to_phy_driver(drv);
+-      const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
+-      int i;
+-
+-      if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
+-              return 0;
+-
+-      if (phydrv->match_phy_device)
+-              return phydrv->match_phy_device(phydev, phydrv);
+-
+       if (phydev->is_c45) {
++              const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
++              int i;
++
+               for (i = 1; i < num_ids; i++) {
+                       if (phydev->c45_ids.device_ids[i] == 0xffffffff)
+                               continue;
+@@ -544,11 +550,27 @@ static int phy_bus_match(struct device *
+                                          phydrv->phy_id, phydrv->phy_id_mask))
+                               return 1;
+               }
++
+               return 0;
+-      } else {
+-              return phy_id_compare(phydev->phy_id, phydrv->phy_id,
+-                                    phydrv->phy_id_mask);
+       }
++
++      return phy_id_compare(phydev->phy_id, phydrv->phy_id,
++                            phydrv->phy_id_mask);
++}
++EXPORT_SYMBOL_GPL(genphy_match_phy_device);
++
++static int phy_bus_match(struct device *dev, struct device_driver *drv)
++{
++      struct phy_device *phydev = to_phy_device(dev);
++      struct phy_driver *phydrv = to_phy_driver(drv);
++
++      if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
++              return 0;
++
++      if (phydrv->match_phy_device)
++              return phydrv->match_phy_device(phydev, phydrv);
++
++      return genphy_match_phy_device(phydev, phydrv);
+ }
+ static ssize_t
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -1812,6 +1812,9 @@ char *phy_attached_info_irq(struct phy_d
+       __malloc;
+ void phy_attached_info(struct phy_device *phydev);
++int genphy_match_phy_device(struct phy_device *phydev,
++                          struct phy_driver *phydrv);
++
+ /* Clause 22 PHY */
+ int genphy_read_abilities(struct phy_device *phydev);
+ int genphy_setup_forced(struct phy_device *phydev);
diff --git a/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch b/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch
new file mode 100644 (file)
index 0000000..cf33b7b
--- /dev/null
@@ -0,0 +1,1165 @@
+From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Sat, 17 May 2025 22:13:49 +0200
+Subject: [PATCH 5/5] net: phy: Add support for Aeonsemi AS21xxx PHYs
+
+Add support for Aeonsemi AS21xxx 10G C45 PHYs. These PHYs integrate
+an IPC to setup some configuration and require special handling to
+sync with the parity bit. The parity bit is a way the IPC use to
+follow correct order of command sent.
+
+Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
+AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
+AS21210PB1 that all register with the PHY ID 0x7500 0x7510
+before the firmware is loaded.
+
+They all support up to 5 LEDs with various HW mode supported.
+
+While implementing it was found some strange coincidence with using the
+same logic for implementing C22 in MMD regs in Broadcom PHYs.
+
+For reference here the AS21xxx PHY name logic:
+
+AS21x1xxB1
+    ^ ^^
+    | |J: Supports SyncE/PTP
+    | |P: No SyncE/PTP support
+    | 1: Supports 2nd Serdes
+    | 2: Not 2nd Serdes support
+    0: 10G, 5G, 2.5G
+    5: 5G, 2.5G
+    2: 2.5G
+
+Reviewed-by: Andrew Lunn <[email protected]>
+Signed-off-by: Christian Marangi <[email protected]>
+Link: https://patch.msgid.link/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ MAINTAINERS               |    6 +
+ drivers/net/phy/Kconfig   |   12 +
+ drivers/net/phy/Makefile  |    1 +
+ drivers/net/phy/as21xxx.c | 1087 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 1106 insertions(+)
+ create mode 100644 drivers/net/phy/as21xxx.c
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -68,6 +68,18 @@ config SFP
+ comment "MII PHY device drivers"
++config AS21XXX_PHY
++      tristate "Aeonsemi AS21xxx PHYs"
++      help
++        Currently supports the Aeonsemi AS21xxx PHY.
++
++        These are C45 PHYs 10G that require all a generic firmware.
++
++        Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1,
++        AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1,
++        AS21210PB1 that all register with the PHY ID 0x7500 0x7500
++        before the firmware is loaded.
++
+ config AMD_PHY
+       tristate "AMD PHYs"
+       help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -36,6 +36,7 @@ obj-$(CONFIG_ADIN_PHY)               += adin.o
+ obj-$(CONFIG_ADIN1100_PHY)    += adin1100.o
+ obj-$(CONFIG_AMD_PHY)         += amd.o
+ obj-$(CONFIG_AQUANTIA_PHY)    += aquantia/
++obj-$(CONFIG_AS21XXX_PHY)     += as21xxx.o
+ obj-$(CONFIG_AX88796B_PHY)    += ax88796b.o
+ obj-$(CONFIG_BCM54140_PHY)    += bcm54140.o
+ obj-$(CONFIG_BCM63XX_PHY)     += bcm63xx.o
+--- /dev/null
++++ b/drivers/net/phy/as21xxx.c
+@@ -0,0 +1,1087 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Aeonsemi AS21XXxX PHY Driver
++ *
++ * Author: Christian Marangi <[email protected]>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/firmware.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/phy.h>
++
++#define VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR 0x3
++#define VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR 0x4
++
++#define VEND1_GLB_REG_CPU_CTRL                0xe
++#define   VEND1_GLB_CPU_CTRL_MASK     GENMASK(4, 0)
++#define   VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK GENMASK(12, 8)
++#define   VEND1_GLB_CPU_CTRL_LED_POLARITY(_n) FIELD_PREP(VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK, \
++                                                       BIT(_n))
++
++#define VEND1_FW_START_ADDR           0x100
++
++#define VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD 0x101
++#define VEND1_GLB_REG_MDIO_INDIRECT_LOAD 0x102
++
++#define VEND1_GLB_REG_MDIO_INDIRECT_STATUS 0x103
++
++#define VEND1_PTP_CLK                 0x142
++#define   VEND1_PTP_CLK_EN            BIT(6)
++
++/* 5 LED at step of 0x20
++ * FE: Fast-Ethernet (10/100)
++ * GE: Gigabit-Ethernet (1000)
++ * NG: New-Generation (2500/5000/10000)
++ */
++#define VEND1_LED_REG(_n)             (0x1800 + ((_n) * 0x10))
++#define   VEND1_LED_REG_A_EVENT               GENMASK(15, 11)
++#define VEND1_LED_CONF                        0x1881
++#define   VEND1_LED_CONFG_BLINK               GENMASK(7, 0)
++
++#define VEND1_SPEED_STATUS            0x4002
++#define   VEND1_SPEED_MASK            GENMASK(7, 0)
++#define   VEND1_SPEED_10000           FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x3)
++#define   VEND1_SPEED_5000            FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x5)
++#define   VEND1_SPEED_2500            FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x9)
++#define   VEND1_SPEED_1000            FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x10)
++#define   VEND1_SPEED_100             FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x20)
++#define   VEND1_SPEED_10              FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x0)
++
++#define VEND1_IPC_CMD                 0x5801
++#define   AEON_IPC_CMD_PARITY         BIT(15)
++#define   AEON_IPC_CMD_SIZE           GENMASK(10, 6)
++#define   AEON_IPC_CMD_OPCODE         GENMASK(5, 0)
++
++#define IPC_CMD_NOOP                  0x0  /* Do nothing */
++#define IPC_CMD_INFO                  0x1  /* Get Firmware Version */
++#define IPC_CMD_SYS_CPU                       0x2  /* SYS_CPU */
++#define IPC_CMD_BULK_DATA             0xa  /* Pass bulk data in ipc registers. */
++#define IPC_CMD_BULK_WRITE            0xc  /* Write bulk data to memory */
++#define IPC_CMD_CFG_PARAM             0x1a /* Write config parameters to memory */
++#define IPC_CMD_NG_TESTMODE           0x1b /* Set NG test mode and tone */
++#define IPC_CMD_TEMP_MON              0x15 /* Temperature monitoring function */
++#define IPC_CMD_SET_LED                       0x23 /* Set led */
++
++#define VEND1_IPC_STS                 0x5802
++#define   AEON_IPC_STS_PARITY         BIT(15)
++#define   AEON_IPC_STS_SIZE           GENMASK(14, 10)
++#define   AEON_IPC_STS_OPCODE         GENMASK(9, 4)
++#define   AEON_IPC_STS_STATUS         GENMASK(3, 0)
++#define   AEON_IPC_STS_STATUS_RCVD    FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x1)
++#define   AEON_IPC_STS_STATUS_PROCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x2)
++#define   AEON_IPC_STS_STATUS_SUCCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x4)
++#define   AEON_IPC_STS_STATUS_ERROR   FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x8)
++#define   AEON_IPC_STS_STATUS_BUSY    FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xe)
++#define   AEON_IPC_STS_STATUS_READY   FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xf)
++
++#define VEND1_IPC_DATA0                       0x5808
++#define VEND1_IPC_DATA1                       0x5809
++#define VEND1_IPC_DATA2                       0x580a
++#define VEND1_IPC_DATA3                       0x580b
++#define VEND1_IPC_DATA4                       0x580c
++#define VEND1_IPC_DATA5                       0x580d
++#define VEND1_IPC_DATA6                       0x580e
++#define VEND1_IPC_DATA7                       0x580f
++#define VEND1_IPC_DATA(_n)            (VEND1_IPC_DATA0 + (_n))
++
++/* Sub command of CMD_INFO */
++#define IPC_INFO_VERSION              0x1
++
++/* Sub command of CMD_SYS_CPU */
++#define IPC_SYS_CPU_REBOOT            0x3
++#define IPC_SYS_CPU_IMAGE_OFST                0x4
++#define IPC_SYS_CPU_IMAGE_CHECK               0x5
++#define IPC_SYS_CPU_PHY_ENABLE                0x6
++
++/* Sub command of CMD_CFG_PARAM */
++#define IPC_CFG_PARAM_DIRECT          0x4
++
++/* CFG DIRECT sub command */
++#define IPC_CFG_PARAM_DIRECT_NG_PHYCTRL       0x1
++#define IPC_CFG_PARAM_DIRECT_CU_AN    0x2
++#define IPC_CFG_PARAM_DIRECT_SDS_PCS  0x3
++#define IPC_CFG_PARAM_DIRECT_AUTO_EEE 0x4
++#define IPC_CFG_PARAM_DIRECT_SDS_PMA  0x5
++#define IPC_CFG_PARAM_DIRECT_DPC_RA   0x6
++#define IPC_CFG_PARAM_DIRECT_DPC_PKT_CHK 0x7
++#define IPC_CFG_PARAM_DIRECT_DPC_SDS_WAIT_ETH 0x8
++#define IPC_CFG_PARAM_DIRECT_WDT      0x9
++#define IPC_CFG_PARAM_DIRECT_SDS_RESTART_AN 0x10
++#define IPC_CFG_PARAM_DIRECT_TEMP_MON 0x11
++#define IPC_CFG_PARAM_DIRECT_WOL      0x12
++
++/* Sub command of CMD_TEMP_MON */
++#define IPC_CMD_TEMP_MON_GET          0x4
++
++#define AS21XXX_MDIO_AN_C22           0xffe0
++
++#define PHY_ID_AS21XXX                        0x75009410
++/* AS21xxx ID Legend
++ * AS21x1xxB1
++ *     ^ ^^
++ *     | |J: Supports SyncE/PTP
++ *     | |P: No SyncE/PTP support
++ *     | 1: Supports 2nd Serdes
++ *     | 2: Not 2nd Serdes support
++ *     0: 10G, 5G, 2.5G
++ *     5: 5G, 2.5G
++ *     2: 2.5G
++ */
++#define PHY_ID_AS21011JB1             0x75009402
++#define PHY_ID_AS21011PB1             0x75009412
++#define PHY_ID_AS21010JB1             0x75009422
++#define PHY_ID_AS21010PB1             0x75009432
++#define PHY_ID_AS21511JB1             0x75009442
++#define PHY_ID_AS21511PB1             0x75009452
++#define PHY_ID_AS21510JB1             0x75009462
++#define PHY_ID_AS21510PB1             0x75009472
++#define PHY_ID_AS21210JB1             0x75009482
++#define PHY_ID_AS21210PB1             0x75009492
++#define PHY_VENDOR_AEONSEMI           0x75009400
++
++#define AEON_MAX_LEDS                 5
++#define AEON_IPC_DELAY                        10000
++#define AEON_IPC_TIMEOUT              (AEON_IPC_DELAY * 100)
++#define AEON_IPC_DATA_NUM_REGISTERS   8
++#define AEON_IPC_DATA_MAX             (AEON_IPC_DATA_NUM_REGISTERS * sizeof(u16))
++
++#define AEON_BOOT_ADDR                        0x1000
++#define AEON_CPU_BOOT_ADDR            0x2000
++#define AEON_CPU_CTRL_FW_LOAD         (BIT(4) | BIT(2) | BIT(1) | BIT(0))
++#define AEON_CPU_CTRL_FW_START                BIT(0)
++
++enum as21xxx_led_event {
++      VEND1_LED_REG_A_EVENT_ON_10 = 0x0,
++      VEND1_LED_REG_A_EVENT_ON_100,
++      VEND1_LED_REG_A_EVENT_ON_1000,
++      VEND1_LED_REG_A_EVENT_ON_2500,
++      VEND1_LED_REG_A_EVENT_ON_5000,
++      VEND1_LED_REG_A_EVENT_ON_10000,
++      VEND1_LED_REG_A_EVENT_ON_FE_GE,
++      VEND1_LED_REG_A_EVENT_ON_NG,
++      VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX,
++      VEND1_LED_REG_A_EVENT_ON_COLLISION,
++      VEND1_LED_REG_A_EVENT_BLINK_TX,
++      VEND1_LED_REG_A_EVENT_BLINK_RX,
++      VEND1_LED_REG_A_EVENT_BLINK_ACT,
++      VEND1_LED_REG_A_EVENT_ON_LINK,
++      VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT,
++      VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX,
++      VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT,
++      VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT,
++      VEND1_LED_REG_A_EVENT_ON_NG_BLINK_FE_GE,
++      VEND1_LED_REG_A_EVENT_ON_FD_BLINK_COLLISION,
++      VEND1_LED_REG_A_EVENT_ON,
++      VEND1_LED_REG_A_EVENT_OFF,
++};
++
++struct as21xxx_led_pattern_info {
++      unsigned int pattern;
++      u16 val;
++};
++
++struct as21xxx_priv {
++      bool parity_status;
++      /* Protect concurrent IPC access */
++      struct mutex ipc_lock;
++};
++
++static struct as21xxx_led_pattern_info as21xxx_led_supported_pattern[] = {
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10),
++              .val = VEND1_LED_REG_A_EVENT_ON_10
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_100),
++              .val = VEND1_LED_REG_A_EVENT_ON_100
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_1000),
++              .val = VEND1_LED_REG_A_EVENT_ON_1000
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_2500),
++              .val = VEND1_LED_REG_A_EVENT_ON_2500
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_5000),
++              .val = VEND1_LED_REG_A_EVENT_ON_5000
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10000),
++              .val = VEND1_LED_REG_A_EVENT_ON_10000
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK),
++              .val = VEND1_LED_REG_A_EVENT_ON_LINK
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000),
++              .val = VEND1_LED_REG_A_EVENT_ON_FE_GE
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000),
++              .val = VEND1_LED_REG_A_EVENT_ON_NG
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
++              .val = VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_TX),
++              .val = VEND1_LED_REG_A_EVENT_BLINK_TX
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_BLINK_RX
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_TX) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_BLINK_ACT
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000) |
++                         BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000),
++              .val = VEND1_LED_REG_A_EVENT_ON_LINK
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000) |
++                         BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000) |
++                         BIT(TRIGGER_NETDEV_TX) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000) |
++                         BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_10) |
++                         BIT(TRIGGER_NETDEV_LINK_100) |
++                         BIT(TRIGGER_NETDEV_LINK_1000) |
++                         BIT(TRIGGER_NETDEV_TX) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT
++      },
++      {
++              .pattern = BIT(TRIGGER_NETDEV_LINK_2500) |
++                         BIT(TRIGGER_NETDEV_LINK_5000) |
++                         BIT(TRIGGER_NETDEV_LINK_10000) |
++                         BIT(TRIGGER_NETDEV_TX) |
++                         BIT(TRIGGER_NETDEV_RX),
++              .val = VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT
++      }
++};
++
++static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data,
++                            size_t size)
++{
++      int i, ret;
++      u16 val;
++
++      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL,
++                           VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_LOAD);
++      if (ret)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_FW_START_ADDR,
++                          AEON_BOOT_ADDR);
++      if (ret)
++              return ret;
++
++      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++                           VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD,
++                           0x3ffc, 0xc000);
++      if (ret)
++              return ret;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++                         VEND1_GLB_REG_MDIO_INDIRECT_STATUS);
++      if (val > 1) {
++              phydev_err(phydev, "wrong origin mdio_indirect_status: %x\n", val);
++              return -EINVAL;
++      }
++
++      /* Firmware is always aligned to u16 */
++      for (i = 0; i < size; i += 2) {
++              val = data[i + 1] << 8 | data[i];
++
++              ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++                                  VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val);
++              if (ret)
++                      return ret;
++      }
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++                          VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR,
++                          lower_16_bits(AEON_CPU_BOOT_ADDR));
++      if (ret)
++              return ret;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
++                          VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR,
++                          upper_16_bits(AEON_CPU_BOOT_ADDR));
++      if (ret)
++              return ret;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL,
++                            VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_START);
++}
++
++static int aeon_firmware_load(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      const struct firmware *fw;
++      const char *fw_name;
++      int ret;
++
++      ret = of_property_read_string(dev->of_node, "firmware-name",
++                                    &fw_name);
++      if (ret)
++              return ret;
++
++      ret = request_firmware(&fw, fw_name, dev);
++      if (ret) {
++              phydev_err(phydev, "failed to find FW file %s (%d)\n",
++                         fw_name, ret);
++              return ret;
++      }
++
++      ret = aeon_firmware_boot(phydev, fw->data, fw->size);
++
++      release_firmware(fw);
++
++      return ret;
++}
++
++static bool aeon_ipc_ready(u16 val, bool parity_status)
++{
++      u16 status;
++
++      if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status)
++              return false;
++
++      status = val & AEON_IPC_STS_STATUS;
++
++      return status != AEON_IPC_STS_STATUS_RCVD &&
++             status != AEON_IPC_STS_STATUS_PROCESS &&
++             status != AEON_IPC_STS_STATUS_BUSY;
++}
++
++static int aeon_ipc_wait_cmd(struct phy_device *phydev, bool parity_status)
++{
++      u16 val;
++
++      /* Exit condition logic:
++       * - Wait for parity bit equal
++       * - Wait for status success, error OR ready
++       */
++      return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val,
++                                       aeon_ipc_ready(val, parity_status),
++                                       AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false);
++}
++
++static int aeon_ipc_send_cmd(struct phy_device *phydev,
++                           struct as21xxx_priv *priv,
++                           u16 cmd, u16 *ret_sts)
++{
++      bool curr_parity;
++      int ret;
++
++      /* The IPC sync by using a single parity bit.
++       * Each CMD have alternately this bit set or clear
++       * to understand correct flow and packet order.
++       */
++      curr_parity = priv->parity_status;
++      if (priv->parity_status)
++              cmd |= AEON_IPC_CMD_PARITY;
++
++      /* Always update parity for next packet */
++      priv->parity_status = !priv->parity_status;
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd);
++      if (ret)
++              return ret;
++
++      /* Wait for packet to be processed */
++      usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000);
++
++      /* With no ret_sts, ignore waiting for packet completion
++       * (ipc parity bit sync)
++       */
++      if (!ret_sts)
++              return 0;
++
++      ret = aeon_ipc_wait_cmd(phydev, curr_parity);
++      if (ret)
++              return ret;
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS);
++      if (ret < 0)
++              return ret;
++
++      *ret_sts = ret;
++      if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS)
++              return -EINVAL;
++
++      return 0;
++}
++
++/* If data is NULL, return 0 or negative error.
++ * If data not NULL, return number of Bytes received from IPC or
++ * a negative error.
++ */
++static int aeon_ipc_send_msg(struct phy_device *phydev,
++                           u16 opcode, u16 *data, unsigned int data_len,
++                           u16 *ret_data)
++{
++      struct as21xxx_priv *priv = phydev->priv;
++      unsigned int ret_size;
++      u16 cmd, ret_sts;
++      int ret;
++      int i;
++
++      /* IPC have a max of 8 register to transfer data,
++       * make sure we never exceed this.
++       */
++      if (data_len > AEON_IPC_DATA_MAX)
++              return -EINVAL;
++
++      for (i = 0; i < data_len / sizeof(u16); i++)
++              phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i),
++                            data[i]);
++
++      cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) |
++            FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode);
++
++      mutex_lock(&priv->ipc_lock);
++
++      ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts);
++      if (ret) {
++              phydev_err(phydev, "failed to send ipc msg for %x: %d\n",
++                         opcode, ret);
++              goto out;
++      }
++
++      if (!data)
++              goto out;
++
++      if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      /* Prevent IPC from stack smashing the kernel.
++       * We can't trust IPC to return a good value and we always
++       * preallocate space for 16 Bytes.
++       */
++      ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts);
++      if (ret_size > AEON_IPC_DATA_MAX) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      /* Read data from IPC data register for ret_size value from IPC */
++      for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) {
++              ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i));
++              if (ret < 0)
++                      goto out;
++
++              ret_data[i] = ret;
++      }
++
++      ret = ret_size;
++
++out:
++      mutex_unlock(&priv->ipc_lock);
++
++      return ret;
++}
++
++static int aeon_ipc_noop(struct phy_device *phydev,
++                       struct as21xxx_priv *priv, u16 *ret_sts)
++{
++      u16 cmd;
++
++      cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) |
++            FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_NOOP);
++
++      return aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts);
++}
++
++/* Logic to sync parity bit with IPC.
++ * We send 2 NOP cmd with same partity and we wait for IPC
++ * to handle the packet only for the second one. This way
++ * we make sure we are sync for every next cmd.
++ */
++static int aeon_ipc_sync_parity(struct phy_device *phydev,
++                              struct as21xxx_priv *priv)
++{
++      u16 ret_sts;
++      int ret;
++
++      mutex_lock(&priv->ipc_lock);
++
++      /* Send NOP with no parity */
++      aeon_ipc_noop(phydev, priv, NULL);
++
++      /* Reset packet parity */
++      priv->parity_status = false;
++
++      /* Send second NOP with no parity */
++      ret = aeon_ipc_noop(phydev, priv, &ret_sts);
++
++      mutex_unlock(&priv->ipc_lock);
++
++      /* We expect to return -EINVAL */
++      if (ret != -EINVAL)
++              return ret;
++
++      if ((ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_READY) {
++              phydev_err(phydev, "Invalid IPC status on sync parity: %x\n",
++                         ret_sts);
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int aeon_ipc_get_fw_version(struct phy_device *phydev)
++{
++      u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS], data[1];
++      char fw_version[AEON_IPC_DATA_MAX + 1];
++      int ret;
++
++      data[0] = IPC_INFO_VERSION;
++
++      ret = aeon_ipc_send_msg(phydev, IPC_CMD_INFO, data,
++                              sizeof(data), ret_data);
++      if (ret < 0)
++              return ret;
++
++      /* Make sure FW version is NULL terminated */
++      memcpy(fw_version, ret_data, ret);
++      fw_version[ret] = '\0';
++
++      phydev_info(phydev, "Firmware Version: %s\n", fw_version);
++
++      return 0;
++}
++
++static int aeon_dpc_ra_enable(struct phy_device *phydev)
++{
++      u16 data[2];
++
++      data[0] = IPC_CFG_PARAM_DIRECT;
++      data[1] = IPC_CFG_PARAM_DIRECT_DPC_RA;
++
++      return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data,
++                               sizeof(data), NULL);
++}
++
++static int as21xxx_probe(struct phy_device *phydev)
++{
++      struct as21xxx_priv *priv;
++      int ret;
++
++      priv = devm_kzalloc(&phydev->mdio.dev,
++                          sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++      phydev->priv = priv;
++
++      ret = devm_mutex_init(&phydev->mdio.dev,
++                            &priv->ipc_lock);
++      if (ret)
++              return ret;
++
++      ret = aeon_ipc_sync_parity(phydev, priv);
++      if (ret)
++              return ret;
++
++      ret = aeon_ipc_get_fw_version(phydev);
++      if (ret)
++              return ret;
++
++      /* Enable PTP clk if not already Enabled */
++      ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK,
++                             VEND1_PTP_CLK_EN);
++      if (ret)
++              return ret;
++
++      return aeon_dpc_ra_enable(phydev);
++}
++
++static int as21xxx_read_link(struct phy_device *phydev, int *bmcr)
++{
++      int status;
++
++      /* Normal C22 BMCR report inconsistent data, use
++       * the mapped C22 in C45 to have more consistent link info.
++       */
++      *bmcr = phy_read_mmd(phydev, MDIO_MMD_AN,
++                           AS21XXX_MDIO_AN_C22 + MII_BMCR);
++      if (*bmcr < 0)
++              return *bmcr;
++
++      /* Autoneg is being started, therefore disregard current
++       * link status and report link as down.
++       */
++      if (*bmcr & BMCR_ANRESTART) {
++              phydev->link = 0;
++              return 0;
++      }
++
++      status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
++      if (status < 0)
++              return status;
++
++      phydev->link = !!(status & MDIO_STAT1_LSTATUS);
++
++      return 0;
++}
++
++static int as21xxx_read_c22_lpa(struct phy_device *phydev)
++{
++      int lpagb;
++
++      /* MII_STAT1000 are only filled in the mapped C22
++       * in C45, use that to fill lpagb values and check.
++       */
++      lpagb = phy_read_mmd(phydev, MDIO_MMD_AN,
++                           AS21XXX_MDIO_AN_C22 + MII_STAT1000);
++      if (lpagb < 0)
++              return lpagb;
++
++      if (lpagb & LPA_1000MSFAIL) {
++              int adv = phy_read_mmd(phydev, MDIO_MMD_AN,
++                                     AS21XXX_MDIO_AN_C22 + MII_CTRL1000);
++
++              if (adv < 0)
++                      return adv;
++
++              if (adv & CTL1000_ENABLE_MASTER)
++                      phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n");
++              else
++                      phydev_err(phydev, "Master/Slave resolution failed\n");
++              return -ENOLINK;
++      }
++
++      mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
++                                      lpagb);
++
++      return 0;
++}
++
++static int as21xxx_read_status(struct phy_device *phydev)
++{
++      int bmcr, old_link = phydev->link;
++      int ret;
++
++      ret = as21xxx_read_link(phydev, &bmcr);
++      if (ret)
++              return ret;
++
++      /* why bother the PHY if nothing can have changed */
++      if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
++              return 0;
++
++      phydev->speed = SPEED_UNKNOWN;
++      phydev->duplex = DUPLEX_UNKNOWN;
++      phydev->pause = 0;
++      phydev->asym_pause = 0;
++
++      if (phydev->autoneg == AUTONEG_ENABLE) {
++              ret = genphy_c45_read_lpa(phydev);
++              if (ret)
++                      return ret;
++
++              ret = as21xxx_read_c22_lpa(phydev);
++              if (ret)
++                      return ret;
++
++              phy_resolve_aneg_linkmode(phydev);
++      } else {
++              int speed;
++
++              linkmode_zero(phydev->lp_advertising);
++
++              speed = phy_read_mmd(phydev, MDIO_MMD_VEND1,
++                                   VEND1_SPEED_STATUS);
++              if (speed < 0)
++                      return speed;
++
++              switch (speed & VEND1_SPEED_STATUS) {
++              case VEND1_SPEED_10000:
++                      phydev->speed = SPEED_10000;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              case VEND1_SPEED_5000:
++                      phydev->speed = SPEED_5000;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              case VEND1_SPEED_2500:
++                      phydev->speed = SPEED_2500;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              case VEND1_SPEED_1000:
++                      phydev->speed = SPEED_1000;
++                      if (bmcr & BMCR_FULLDPLX)
++                              phydev->duplex = DUPLEX_FULL;
++                      else
++                              phydev->duplex = DUPLEX_HALF;
++                      break;
++              case VEND1_SPEED_100:
++                      phydev->speed = SPEED_100;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              case VEND1_SPEED_10:
++                      phydev->speed = SPEED_10;
++                      phydev->duplex = DUPLEX_FULL;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      return 0;
++}
++
++static int as21xxx_led_brightness_set(struct phy_device *phydev,
++                                    u8 index, enum led_brightness value)
++{
++      u16 val = VEND1_LED_REG_A_EVENT_OFF;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      if (value)
++              val = VEND1_LED_REG_A_EVENT_ON;
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++                            VEND1_LED_REG(index),
++                            VEND1_LED_REG_A_EVENT,
++                            FIELD_PREP(VEND1_LED_REG_A_EVENT, val));
++}
++
++static int as21xxx_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                     unsigned long rules)
++{
++      int i;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++              if (rules == as21xxx_led_supported_pattern[i].pattern)
++                      return 0;
++
++      return -EOPNOTSUPP;
++}
++
++static int as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                    unsigned long *rules)
++{
++      int i, val;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index));
++      if (val < 0)
++              return val;
++
++      val = FIELD_GET(VEND1_LED_REG_A_EVENT, val);
++      for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++              if (val == as21xxx_led_supported_pattern[i].val) {
++                      *rules = as21xxx_led_supported_pattern[i].pattern;
++                      return 0;
++              }
++
++      /* Should be impossible */
++      return -EINVAL;
++}
++
++static int as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                    unsigned long rules)
++{
++      u16 val = 0;
++      int i;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++)
++              if (rules == as21xxx_led_supported_pattern[i].pattern) {
++                      val = as21xxx_led_supported_pattern[i].val;
++                      break;
++              }
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++                            VEND1_LED_REG(index),
++                            VEND1_LED_REG_A_EVENT,
++                            FIELD_PREP(VEND1_LED_REG_A_EVENT, val));
++}
++
++static int as21xxx_led_polarity_set(struct phy_device *phydev, int index,
++                                  unsigned long modes)
++{
++      bool led_active_low = false;
++      u16 mask, val = 0;
++      u32 mode;
++
++      if (index > AEON_MAX_LEDS)
++              return -EINVAL;
++
++      for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++              switch (mode) {
++              case PHY_LED_ACTIVE_LOW:
++                      led_active_low = true;
++                      break;
++              case PHY_LED_ACTIVE_HIGH: /* default mode */
++                      led_active_low = false;
++                      break;
++              default:
++                      return -EINVAL;
++              }
++      }
++
++      mask = VEND1_GLB_CPU_CTRL_LED_POLARITY(index);
++      if (led_active_low)
++              val = VEND1_GLB_CPU_CTRL_LED_POLARITY(index);
++
++      return phy_modify_mmd(phydev, MDIO_MMD_VEND1,
++                            VEND1_GLB_REG_CPU_CTRL,
++                            mask, val);
++}
++
++static int as21xxx_match_phy_device(struct phy_device *phydev,
++                                  const struct phy_driver *phydrv)
++{
++      struct as21xxx_priv *priv;
++      u16 ret_sts;
++      u32 phy_id;
++      int ret;
++
++      /* Skip PHY that are not AS21xxx or already have firmware loaded */
++      if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX)
++              return genphy_match_phy_device(phydev, (struct phy_driver *)phydrv);
++
++      /* Read PHY ID to handle firmware just loaded */
++      ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1);
++      if (ret < 0)
++              return ret;
++      phy_id = ret << 16;
++
++      ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2);
++      if (ret < 0)
++              return ret;
++      phy_id |= ret;
++
++      /* With PHY ID not the generic AS21xxx one assume
++       * the firmware just loaded
++       */
++      if (phy_id != PHY_ID_AS21XXX)
++              return phy_id == phydrv->phy_id;
++
++      /* Allocate temp priv and load the firmware */
++      priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      mutex_init(&priv->ipc_lock);
++
++      ret = aeon_firmware_load(phydev);
++      if (ret)
++              goto out;
++
++      /* Sync parity... */
++      ret = aeon_ipc_sync_parity(phydev, priv);
++      if (ret)
++              goto out;
++
++      /* ...and send a third NOOP cmd to wait for firmware finish loading */
++      ret = aeon_ipc_noop(phydev, priv, &ret_sts);
++      if (ret)
++              goto out;
++
++out:
++      mutex_destroy(&priv->ipc_lock);
++      kfree(priv);
++
++      /* Return can either be 0 or a negative error code.
++       * Returning 0 here means THIS is NOT a suitable PHY.
++       *
++       * For the specific case of the generic Aeonsemi PHY ID that
++       * needs the firmware the be loaded first to have a correct PHY ID,
++       * this is OK as a matching PHY ID will be found right after.
++       * This relies on the driver probe order where the first PHY driver
++       * probed is the generic one.
++       */
++      return ret;
++}
++
++static struct phy_driver as21xxx_drivers[] = {
++      {
++              /* PHY expose in C45 as 0x7500 0x9410
++               * before firmware is loaded.
++               * This driver entry must be attempted first to load
++               * the firmware and thus update the ID registers.
++               */
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21XXX),
++              .name           = "Aeonsemi AS21xxx",
++              .match_phy_device = as21xxx_match_phy_device,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21011JB1),
++              .name           = "Aeonsemi AS21011JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1),
++              .name           = "Aeonsemi AS21011PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1),
++              .name           = "Aeonsemi AS21010PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1),
++              .name           = "Aeonsemi AS21010JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1),
++              .name           = "Aeonsemi AS21210PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1),
++              .name           = "Aeonsemi AS21510JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1),
++              .name           = "Aeonsemi AS21510PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1),
++              .name           = "Aeonsemi AS21511JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1),
++              .name           = "Aeonsemi AS21210JB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++      {
++              PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1),
++              .name           = "Aeonsemi AS21511PB1",
++              .probe          = as21xxx_probe,
++              .match_phy_device = as21xxx_match_phy_device,
++              .read_status    = as21xxx_read_status,
++              .led_brightness_set = as21xxx_led_brightness_set,
++              .led_hw_is_supported = as21xxx_led_hw_is_supported,
++              .led_hw_control_set = as21xxx_led_hw_control_set,
++              .led_hw_control_get = as21xxx_led_hw_control_get,
++              .led_polarity_set = as21xxx_led_polarity_set,
++      },
++};
++module_phy_driver(as21xxx_drivers);
++
++static struct mdio_device_id __maybe_unused as21xxx_tbl[] = {
++      { PHY_ID_MATCH_VENDOR(PHY_VENDOR_AEONSEMI) },
++      { }
++};
++MODULE_DEVICE_TABLE(mdio, as21xxx_tbl);
++
++MODULE_DESCRIPTION("Aeonsemi AS21xxx PHY driver");
++MODULE_AUTHOR("Christian Marangi <[email protected]>");
++MODULE_LICENSE("GPL");
index 5b627cf44975c2a29467a6965c1fb565118923e6..ecea0e987d5b1262282249a10502469e8940e632 100644 (file)
@@ -27,9 +27,9 @@ Signed-off-by: Jakub Kicinski <[email protected]>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -68,6 +68,11 @@ config SFP
- comment "MII PHY device drivers"
+@@ -80,6 +80,11 @@ config AS21XXX_PHY
+         AS21210PB1 that all register with the PHY ID 0x7500 0x7500
+         before the firmware is loaded.
  
 +config AIR_EN8811H_PHY
 +      tristate "Airoha EN8811H 2.5 Gigabit PHY"
@@ -48,7 +48,7 @@ Signed-off-by: Jakub Kicinski <[email protected]>
 +obj-$(CONFIG_AIR_EN8811H_PHY)   += air_en8811h.o
  obj-$(CONFIG_AMD_PHY)         += amd.o
  obj-$(CONFIG_AQUANTIA_PHY)    += aquantia/
- obj-$(CONFIG_AX88796B_PHY)    += ax88796b.o
+ obj-$(CONFIG_AS21XXX_PHY)     += as21xxx.o
 --- /dev/null
 +++ b/drivers/net/phy/air_en8811h.c
 @@ -0,0 +1,1086 @@
index 07287206f698b393626a4d8588c8608af7768453..1ed1008ee537556df8f30f58d4b85df2a1fbe680 100644 (file)
@@ -16,7 +16,7 @@ Signed-off-by: Linus Walleij <[email protected]>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -74,9 +74,9 @@ config AIR_EN8811H_PHY
+@@ -86,9 +86,9 @@ config AIR_EN8811H_PHY
          Currently supports the Airoha EN8811H PHY.
  
  config AMD_PHY
index 54932436c747c4ccd302b00bcbd5e30b2d27cad2..4d8742f0e3329e558740d67d9ac4b898413b7e76 100644 (file)
@@ -28,7 +28,7 @@ Signed-off-by: Jakub Kicinski <[email protected]>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -3204,6 +3204,7 @@ static int of_phy_led(struct phy_device
+@@ -3226,6 +3226,7 @@ static int of_phy_led(struct phy_device
        struct device *dev = &phydev->mdio.dev;
        struct led_init_data init_data = {};
        struct led_classdev *cdev;
@@ -36,7 +36,7 @@ Signed-off-by: Jakub Kicinski <[email protected]>
        struct phy_led *phyled;
        u32 index;
        int err;
-@@ -3221,6 +3222,21 @@ static int of_phy_led(struct phy_device
+@@ -3243,6 +3244,21 @@ static int of_phy_led(struct phy_device
        if (index > U8_MAX)
                return -EINVAL;
  
@@ -76,7 +76,7 @@ Signed-off-by: Jakub Kicinski <[email protected]>
  /**
   * struct phy_driver - Driver structure for a particular PHY type
   *
-@@ -1143,6 +1152,19 @@ struct phy_driver {
+@@ -1144,6 +1153,19 @@ struct phy_driver {
        int (*led_hw_control_get)(struct phy_device *dev, u8 index,
                                  unsigned long *rules);
  
index 3d8a15bd1e086959b0ce03903f38aaaaf48e7b8e..1b979f8662a4844fba78ec55a7dc14fe0b9fd955 100644 (file)
@@ -19,7 +19,7 @@ Signed-off-by: Paolo Abeni <[email protected]>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -3222,11 +3222,17 @@ static int of_phy_led(struct phy_device
+@@ -3244,11 +3244,17 @@ static int of_phy_led(struct phy_device
        if (index > U8_MAX)
                return -EINVAL;
  
index b86dbea898524c25c3ceb8b54962b147ac011d83..43e2c92ef009da794594f83a817f34a27b17f525 100644 (file)
@@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski <[email protected]>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -1247,6 +1247,8 @@ int phy_init_hw(struct phy_device *phyde
+@@ -1269,6 +1269,8 @@ int phy_init_hw(struct phy_device *phyde
        if (ret < 0)
                return ret;
  
index 261166c6c3228c18ff611086ab17ccea1fa6ef0f..dc836ccb106e0ef8e5637d47da487232bb699ff6 100644 (file)
@@ -458,6 +458,7 @@ CONFIG_ARM_MODULE_PLTS=y
 # CONFIG_ARM_TIMER_SP804 is not set
 # CONFIG_ARM_UNWIND is not set
 # CONFIG_ARM_VIRT_EXT is not set
+# CONFIG_AS21XXX_PHY is not set
 # CONFIG_AS3935 is not set
 # CONFIG_AS73211 is not set
 # CONFIG_ASM9260_TIMER is not set
index a7d06efed3d923827c1ed2f05b9479075182f719..be73510dd02e1953cb567f689d4076cdf2cd63d9 100644 (file)
@@ -440,6 +440,7 @@ CONFIG_ARM_MODULE_PLTS=y
 # CONFIG_ARM_TIMER_SP804 is not set
 # CONFIG_ARM_UNWIND is not set
 # CONFIG_ARM_VIRT_EXT is not set
+# CONFIG_AS21XXX_PHY is not set
 # CONFIG_AS3935 is not set
 # CONFIG_AS73211 is not set
 # CONFIG_ASM9260_TIMER is not set
index 4591a42f7827145cfc4664fe1f9dfb5a4e59795d..9b1f53af3e4a07c8a43420cbfe133f574ab9fc1e 100644 (file)
@@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau <[email protected]>
 +
  comment "MII PHY device drivers"
  
- config AIR_EN8811H_PHY
+ config AS21XXX_PHY
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
 @@ -27,6 +27,21 @@ libphy-$(CONFIG_OPEN_ALLIANCE_HELPERS) +
index 616db258906a5f401dd2317409ba4556c662f69e..cfcc72066218ca1f30ee100ab0336f8287711ceb 100644 (file)
@@ -1,6 +1,6 @@
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -419,6 +419,8 @@ config QSEMI_PHY
+@@ -431,6 +431,8 @@ config QSEMI_PHY
  
  source "drivers/net/phy/realtek/Kconfig"
  
@@ -11,7 +11,7 @@
        help
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -110,6 +110,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY)      += nxp-tja
+@@ -111,6 +111,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY)      += nxp-tja
  obj-y                         += qcom/
  obj-$(CONFIG_QSEMI_PHY)               += qsemi.o
  obj-$(CONFIG_REALTEK_PHY)     += realtek/
index 4428ebbb5adda8dac14053fc914d711945b12915..59cc16cb753dce4aae2a11b1c2d0bddc8efdbe6a 100644 (file)
@@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau <[email protected]>
 +
  comment "MII PHY device drivers"
  
- config AIR_EN8811H_PHY
+ config AS21XXX_PHY
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
 @@ -26,6 +26,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY)    += phy_
index 946869c3037377a302c653c0bbeb1769f0c3aebe..b142683a28869a0add2b3d2c917d6dde2cafa1eb 100644 (file)
@@ -1,6 +1,6 @@
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -406,6 +406,8 @@ config QSEMI_PHY
+@@ -418,6 +418,8 @@ config QSEMI_PHY
  
  source "drivers/net/phy/realtek/Kconfig"
  
@@ -11,7 +11,7 @@
        help
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -100,6 +100,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY)      += nxp-tja
+@@ -101,6 +101,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY)      += nxp-tja
  obj-y                         += qcom/
  obj-$(CONFIG_QSEMI_PHY)               += qsemi.o
  obj-$(CONFIG_REALTEK_PHY)     += realtek/
index b8d20d86104a01715a3c606b9e1961c8eb618ad2..ed6c9070b24bca71c454d8f2eeb45e78c15b1490 100644 (file)
@@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos <[email protected]>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -2015,6 +2015,9 @@ void phy_detach(struct phy_device *phyde
+@@ -2037,6 +2037,9 @@ void phy_detach(struct phy_device *phyde
                phydev->devlink = NULL;
        }
  
index 9bb5737b396757f1b72c30bbfaad4bcb36d722bf..a4cb71b83b2b0f291d1c35c9c07ef91bfd505386 100644 (file)
@@ -15,7 +15,7 @@ Signed-off-by: Daniel Golle <[email protected]>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1666,6 +1666,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1675,6 +1675,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .name           = "RTL8226 2.5Gbps PHY",
                .match_phy_device = rtl8226_match_phy_device,
@@ -23,7 +23,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
                .read_status    = rtl822x_read_status,
-@@ -1676,6 +1677,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1685,6 +1686,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_match_phy_device,
                .name           = "RTL8226B_RTL8221B 2.5Gbps PHY",
@@ -31,7 +31,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
                .config_init    = rtl822xb_config_init,
-@@ -1698,6 +1700,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1707,6 +1709,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                PHY_ID_MATCH_EXACT(0x001cc848),
                .name           = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
@@ -39,7 +39,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
                .config_init    = rtl822xb_config_init,
-@@ -1710,6 +1713,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1719,6 +1722,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
                .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
@@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .probe          = rtl822x_probe,
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
-@@ -1723,6 +1727,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1732,6 +1736,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
                .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
@@ -55,7 +55,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .probe          = rtl822x_probe,
                .config_init    = rtl822xb_config_init,
                .get_rate_matching = rtl822xb_get_rate_matching,
-@@ -1734,6 +1739,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1743,6 +1748,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
                .name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
@@ -63,7 +63,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .probe          = rtl822x_probe,
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
-@@ -1747,6 +1753,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1756,6 +1762,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
                .name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
index 4e07882b1ec70a6ec4fb066aa360c88a86551c24..186b3ff2b9ac4afeb55bfc21aca7f21c9ca09e93 100644 (file)
@@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle <[email protected]>
 Signed-off-by: Mieczyslaw Nalewaj <[email protected]>
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1398,10 +1398,32 @@ static int rtl8226_match_phy_device(stru
+@@ -1400,10 +1400,32 @@ static int rtl8226_match_phy_device(stru
  static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
                               bool is_c45)
  {
@@ -49,4 +49,4 @@ Signed-off-by: Mieczyslaw Nalewaj <[email protected]>
 +      }
  }
  
- static int rtl8221b_match_phy_device(struct phy_device *phydev)
+ static int rtl8221b_match_phy_device(struct phy_device *phydev,
index 855ea41c8cebf91550b7e0b52ed39ca4fb1c9ecb..e908af055f39ae72a9fa7063fc34c5c4b29fb308 100644 (file)
@@ -12,7 +12,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1610,6 +1610,51 @@ static irqreturn_t rtl9000a_handle_inter
+@@ -1619,6 +1619,51 @@ static irqreturn_t rtl9000a_handle_inter
        return IRQ_HANDLED;
  }
  
@@ -64,7 +64,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
  static struct phy_driver realtek_drvs[] = {
        {
                PHY_ID_MATCH_EXACT(0x00008201),
-@@ -1774,6 +1819,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1783,6 +1828,8 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
                .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
@@ -73,7 +73,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
                .soft_reset     = genphy_soft_reset,
                .probe          = rtl822x_probe,
                .get_features   = rtl822x_get_features,
-@@ -1788,6 +1835,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1797,6 +1844,8 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
                .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
@@ -82,7 +82,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
                .soft_reset     = rtl822x_c45_soft_reset,
                .probe          = rtl822x_probe,
                .config_init    = rtl822xb_config_init,
-@@ -1800,6 +1849,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1809,6 +1858,8 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
                .name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
@@ -91,7 +91,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
                .soft_reset     = genphy_soft_reset,
                .probe          = rtl822x_probe,
                .get_features   = rtl822x_get_features,
-@@ -1814,6 +1865,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1823,6 +1874,8 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
                .name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
index e451d13bd8fdb52406e414fa58b7dde1701f1473..d4920b5c97a8489c2179774d85545d277fe1de4c 100644 (file)
@@ -38,7 +38,7 @@ Signed-off-by: Daniel Golle <[email protected]>
  static int rtl822xb_get_rate_matching(struct phy_device *phydev,
                                      phy_interface_t iface)
  {
-@@ -1842,7 +1858,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1851,7 +1867,7 @@ static struct phy_driver realtek_drvs[]
                .handle_interrupt = rtl8221b_handle_interrupt,
                .soft_reset     = rtl822x_c45_soft_reset,
                .probe          = rtl822x_probe,
@@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .get_rate_matching = rtl822xb_get_rate_matching,
                .get_features   = rtl822x_c45_get_features,
                .config_aneg    = rtl822x_c45_config_aneg,
-@@ -1872,7 +1888,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1881,7 +1897,7 @@ static struct phy_driver realtek_drvs[]
                .handle_interrupt = rtl8221b_handle_interrupt,
                .soft_reset     = rtl822x_c45_soft_reset,
                .probe          = rtl822x_probe,
index f5621e34ee874e50981558948325a1589ac46b7b..7a52bd552132ba46f5c6528f139eea51b201e3c1 100644 (file)
@@ -24,7 +24,7 @@ Signed-off-by: David Bauer <[email protected]>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -139,7 +139,7 @@ config BROADCOM_PHY
+@@ -151,7 +151,7 @@ config BROADCOM_PHY
        tristate "Broadcom 54XX PHYs"
        select BCM_NET_PHYLIB
        select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING
index 453abe65c64014a5ef0d72473e9549704191381c..efa76572f86e74f5f7c2138c4f81c77007c83af5 100644 (file)
@@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos <[email protected]>
 
 --- a/drivers/net/phy/phy_device.c
 +++ b/drivers/net/phy/phy_device.c
-@@ -1912,6 +1912,9 @@ void phy_detach(struct phy_device *phyde
+@@ -1934,6 +1934,9 @@ void phy_detach(struct phy_device *phyde
                phydev->devlink = NULL;
        }
  
index 1becba6da6f73ab32bbb94524d77ba72fd2a7f42..deb6506186e0bb8dbeb29eac2b4b42fffa64afb0 100644 (file)
@@ -15,7 +15,7 @@ Signed-off-by: Daniel Golle <[email protected]>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1638,6 +1638,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1647,6 +1647,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .name           = "RTL8226 2.5Gbps PHY",
                .match_phy_device = rtl8226_match_phy_device,
@@ -23,7 +23,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
                .read_status    = rtl822x_read_status,
-@@ -1648,6 +1649,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1657,6 +1658,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_match_phy_device,
                .name           = "RTL8226B_RTL8221B 2.5Gbps PHY",
@@ -31,7 +31,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
                .config_init    = rtl822xb_config_init,
-@@ -1660,6 +1662,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1669,6 +1671,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                PHY_ID_MATCH_EXACT(0x001cc838),
                .name           = "RTL8226-CG 2.5Gbps PHY",
@@ -39,7 +39,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
                .read_status    = rtl822x_read_status,
-@@ -1670,6 +1673,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1679,6 +1682,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                PHY_ID_MATCH_EXACT(0x001cc848),
                .name           = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
@@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
                .config_init    = rtl822xb_config_init,
-@@ -1682,6 +1686,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1691,6 +1695,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
                .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
@@ -55,7 +55,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .probe          = rtl822x_probe,
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
-@@ -1695,6 +1700,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1704,6 +1709,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
                .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
@@ -63,7 +63,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .probe          = rtl822x_probe,
                .config_init    = rtl822xb_config_init,
                .get_rate_matching = rtl822xb_get_rate_matching,
-@@ -1706,6 +1712,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1715,6 +1721,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
                .name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
@@ -71,7 +71,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .probe          = rtl822x_probe,
                .get_features   = rtl822x_get_features,
                .config_aneg    = rtl822x_config_aneg,
-@@ -1719,6 +1726,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1728,6 +1735,7 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
                .name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
index 0918794d87e26556b61dc571c0d915c7e7954f10..2886babe57e89c7b08e73505acaf7663baf2361a 100644 (file)
@@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle <[email protected]>
 Signed-off-by: Mieczyslaw Nalewaj <[email protected]>
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1368,10 +1368,32 @@ static int rtl8226_match_phy_device(stru
+@@ -1370,10 +1370,32 @@ static int rtl8226_match_phy_device(stru
  static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
                               bool is_c45)
  {
@@ -49,4 +49,4 @@ Signed-off-by: Mieczyslaw Nalewaj <[email protected]>
 +      }
  }
  
- static int rtl8221b_match_phy_device(struct phy_device *phydev)
+ static int rtl8221b_match_phy_device(struct phy_device *phydev,
index 9afe8baca6614a299408ff637273283cf04fb5eb..29610d2767b3db5a5b4c3bfd94d4914f14263554 100644 (file)
@@ -12,7 +12,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
 
 --- a/drivers/net/phy/realtek/realtek_main.c
 +++ b/drivers/net/phy/realtek/realtek_main.c
-@@ -1580,6 +1580,51 @@ static irqreturn_t rtl9000a_handle_inter
+@@ -1589,6 +1589,51 @@ static irqreturn_t rtl9000a_handle_inter
        return IRQ_HANDLED;
  }
  
@@ -64,7 +64,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
  static struct phy_driver realtek_drvs[] = {
        {
                PHY_ID_MATCH_EXACT(0x00008201),
-@@ -1745,6 +1790,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1754,6 +1799,8 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
                .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
@@ -73,7 +73,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
                .soft_reset     = genphy_soft_reset,
                .probe          = rtl822x_probe,
                .get_features   = rtl822x_get_features,
-@@ -1759,6 +1806,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1768,6 +1815,8 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
                .name           = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
@@ -82,7 +82,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
                .soft_reset     = genphy_soft_reset,
                .probe          = rtl822x_probe,
                .config_init    = rtl822xb_config_init,
-@@ -1771,6 +1820,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1780,6 +1829,8 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
                .name           = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
@@ -91,7 +91,7 @@ Signed-off-by: Jianhui Zhao <[email protected]>
                .soft_reset     = genphy_soft_reset,
                .probe          = rtl822x_probe,
                .get_features   = rtl822x_get_features,
-@@ -1785,6 +1836,8 @@ static struct phy_driver realtek_drvs[]
+@@ -1794,6 +1845,8 @@ static struct phy_driver realtek_drvs[]
        }, {
                .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
                .name           = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
index 1749a74e36144348edc1039471111c31967ac70b..1b6978547db8e2ca637db7ce092f466c72f0e1da 100644 (file)
@@ -38,7 +38,7 @@ Signed-off-by: Daniel Golle <[email protected]>
  static int rtl822xb_get_rate_matching(struct phy_device *phydev,
                                      phy_interface_t iface)
  {
-@@ -1813,7 +1829,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1822,7 +1838,7 @@ static struct phy_driver realtek_drvs[]
                .handle_interrupt = rtl8221b_handle_interrupt,
                .soft_reset     = genphy_soft_reset,
                .probe          = rtl822x_probe,
@@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle <[email protected]>
                .get_rate_matching = rtl822xb_get_rate_matching,
                .get_features   = rtl822x_c45_get_features,
                .config_aneg    = rtl822x_c45_config_aneg,
-@@ -1843,7 +1859,7 @@ static struct phy_driver realtek_drvs[]
+@@ -1852,7 +1868,7 @@ static struct phy_driver realtek_drvs[]
                .handle_interrupt = rtl8221b_handle_interrupt,
                .soft_reset     = genphy_soft_reset,
                .probe          = rtl822x_probe,
index 80b69920e9d46fea20c251d27531e2ca622fb2d3..449b27e617a4f3853f6431c68269bcacd6d8097b 100644 (file)
@@ -24,7 +24,7 @@ Signed-off-by: David Bauer <[email protected]>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -113,7 +113,7 @@ config BROADCOM_PHY
+@@ -125,7 +125,7 @@ config BROADCOM_PHY
        tristate "Broadcom 54XX PHYs"
        select BCM_NET_PHYLIB
        select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING
index 72a1464966d3cd8f6f5c849b4845d8e32d5ba3f2..8f911f6fd8f115dad01d6a44e84206ab38904e24 100644 (file)
@@ -1,6 +1,6 @@
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -431,6 +431,12 @@ config ROCKCHIP_PHY
+@@ -443,6 +443,12 @@ config ROCKCHIP_PHY
        help
          Currently supports the integrated Ethernet PHY.
  
@@ -15,7 +15,7 @@
        select CRC16
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -113,6 +113,7 @@ obj-$(CONFIG_REALTEK_PHY)  += realtek/
+@@ -114,6 +114,7 @@ obj-$(CONFIG_REALTEK_PHY)  += realtek/
  obj-y                         += rtl8261n/
  obj-$(CONFIG_RENESAS_PHY)     += uPD60620.o
  obj-$(CONFIG_ROCKCHIP_PHY)    += rockchip.o
index 079351b7a22ccfd04a858e2fbd63c6353ed67971..4ebaffd1dd1dfae56391ad2ced65682708da3f78 100644 (file)
@@ -14,9 +14,9 @@ Signed-off-by: Robert Marko <[email protected]>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -153,6 +153,11 @@ endif # RTL8366_SMI
- comment "MII PHY device drivers"
+@@ -165,6 +165,11 @@ config AS21XXX_PHY
+         AS21210PB1 that all register with the PHY ID 0x7500 0x7500
+         before the firmware is loaded.
  
 +config AIROHA_EN8801SC_PHY
 +      tristate "Airoha EN8801SC Gigabit PHY"
index b0adf04a5bdc7562320f05b897d3d149d005a7a1..d31215ec27640b95f0b859ef2018568a4ef791b9 100644 (file)
@@ -254,7 +254,7 @@ Christian Marangi (9):
  obj-$(CONFIG_MDIO_BCM_UNIMAC)         += mdio-bcm-unimac.o
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -158,6 +158,11 @@ config AIROHA_EN8801SC_PHY
+@@ -170,6 +170,11 @@ config AIROHA_EN8801SC_PHY
        help
          Currently supports the Airoha EN8801SC PHY.
  
index 632aad0ed29449689394d055ce617565b1ba0e7d..12638978b3e5cd5e17afe0682c0cd3487277d3cd 100644 (file)
@@ -880,7 +880,7 @@ publishing the in-band capabilities from the BCM84881 PHY driver.
         * @get_rate_matching: Get the supported type of rate matching for a
         * particular phy interface. This is used by phy consumers to determine
         * whether to advertise lower-speed modes for that interface. It is
-@@ -1839,6 +1870,9 @@ int phy_config_aneg(struct phy_device *p
+@@ -1840,6 +1871,9 @@ int phy_config_aneg(struct phy_device *p
  int _phy_start_aneg(struct phy_device *phydev);
  int phy_start_aneg(struct phy_device *phydev);
  int phy_aneg_done(struct phy_device *phydev);
index 3b14e9bcfe2ddc72f229c5dac0e4bd52437ecfe2..7d38070091b3910078c5d70f9db1b92a2dcbc441 100644 (file)
@@ -223,12 +223,14 @@ static int rtl821x_match_phy_device(struct phy_device *phydev)
        return PHY_IS_RTL8214FB;
 }
 
-static int rtl8218b_ext_match_phy_device(struct phy_device *phydev)
+static int rtl8218b_ext_match_phy_device(struct phy_device *phydev,
+                                        const struct phy_driver *phydrv)
 {
        return rtl821x_match_phy_device(phydev) == PHY_IS_RTL8218B_E;
 }
 
-static int rtl8214fc_match_phy_device(struct phy_device *phydev)
+static int rtl8214fc_match_phy_device(struct phy_device *phydev,
+                                     const struct phy_driver *phydrv)
 {
        return rtl821x_match_phy_device(phydev) == PHY_IS_RTL8214FC;
 }
index 9b054f96a8c2d38460e08f8a141c43ae159c5177..ca411d69549e7f82d1cf875d2ac9ce1e764e2082 100644 (file)
@@ -21,7 +21,7 @@ Submitted-by: John Crispin <[email protected]>
 
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -1226,6 +1226,8 @@ struct phy_driver {
+@@ -1227,6 +1227,8 @@ struct phy_driver {
         */
        int (*led_polarity_set)(struct phy_device *dev, int index,
                                unsigned long modes);
index 3be218635e82431df83eb2111661ebe4e16e1f74..4d1efd76b78564619b9c32966fa8bf21dc34974e 100644 (file)
@@ -14,7 +14,7 @@ Submitted-by: Birger Koblitz <[email protected]>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -422,6 +422,12 @@ source "drivers/net/phy/realtek/Kconfig"
+@@ -434,6 +434,12 @@ source "drivers/net/phy/realtek/Kconfig"
  
  source "drivers/net/phy/rtl8261n/Kconfig"
  
@@ -29,7 +29,7 @@ Submitted-by: Birger Koblitz <[email protected]>
        help
 --- a/drivers/net/phy/Makefile
 +++ b/drivers/net/phy/Makefile
-@@ -111,6 +111,7 @@ obj-y                              += qcom/
+@@ -112,6 +112,7 @@ obj-y                              += qcom/
  obj-$(CONFIG_QSEMI_PHY)               += qsemi.o
  obj-$(CONFIG_REALTEK_PHY)     += realtek/
  obj-y                         += rtl8261n/
index 55532bb5e520ab3546e127dfb8af5818ba42f2b6..98a6f07de80d9f80ba54021175dbb5f8e79cd4da 100644 (file)
@@ -170,7 +170,7 @@ Signed-off-by: Jakub Kicinski <[email protected]>
        /* This is optional functionality. If not supported, we may get an error
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -1893,6 +1893,7 @@ int genphy_c45_an_config_aneg(struct phy
+@@ -1897,6 +1897,7 @@ int genphy_c45_an_config_aneg(struct phy
  int genphy_c45_an_disable_aneg(struct phy_device *phydev);
  int genphy_c45_read_mdix(struct phy_device *phydev);
  int genphy_c45_pma_read_abilities(struct phy_device *phydev);
index 4113d82326365849e893559fc3413e1ce9ce46df..745d2e8fb5d445b4673b68dfbe1bc68369976379 100644 (file)
@@ -11,7 +11,7 @@ Signed-off-by: haoming.chen <[email protected]>
 
 --- a/drivers/net/phy/Kconfig
 +++ b/drivers/net/phy/Kconfig
-@@ -484,3 +484,8 @@ endif # PHYLIB
+@@ -481,3 +481,8 @@ endif # PHYLIB
  config MICREL_KS8995MA
        tristate "Micrel KS8995MA 5-ports 10/100 managed Ethernet switch"
        depends on SPI