From: Christian Marangi Date: Sat, 5 Jul 2025 11:01:21 +0000 (+0200) Subject: generic: backport support for Aeonsemi AS21xxx PHY X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=08a616b2163ed229f071569d0d009568638fb21f;p=openwrt%2Fstaging%2Fstintel.git generic: backport support for Aeonsemi AS21xxx PHY 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 --- diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk index 9ae3958dfe..f27624d4cc 100644 --- a/package/kernel/linux/modules/netdevices.mk +++ b/package/kernel/linux/modules/netdevices.mk @@ -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 index 0000000000..cda5226904 --- /dev/null +++ b/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch @@ -0,0 +1,303 @@ +From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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) +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Reviewed-by: Benno Lossin # for Rust +Reviewed-by: FUJITA Tomonori +Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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 Adapter { + /// `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 index 0000000000..9e28011b0c --- /dev/null +++ b/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch @@ -0,0 +1,109 @@ +From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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) +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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 index 0000000000..bcf9b1a6e5 --- /dev/null +++ b/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch @@ -0,0 +1,1165 @@ +From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 index 0000000000..b8460a2b5e --- /dev/null +++ b/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch @@ -0,0 +1,273 @@ +From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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) +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Reviewed-by: Benno Lossin # for Rust +Reviewed-by: FUJITA Tomonori +Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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 index 0000000000..1c0b5d836d --- /dev/null +++ b/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch @@ -0,0 +1,109 @@ +From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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) +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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 index 0000000000..cf33b7bfdf --- /dev/null +++ b/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch @@ -0,0 +1,1165 @@ +From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch b/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch index 5b627cf449..ecea0e987d 100644 --- a/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch +++ b/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch @@ -27,9 +27,9 @@ Signed-off-by: Jakub Kicinski --- 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 +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 @@ diff --git a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch index 07287206f6..1ed1008ee5 100644 --- a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch +++ b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch @@ -16,7 +16,7 @@ Signed-off-by: Linus Walleij --- 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 diff --git a/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch b/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch index 54932436c7..4d8742f0e3 100644 --- a/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch +++ b/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch @@ -28,7 +28,7 @@ Signed-off-by: Jakub Kicinski --- 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 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 /** * 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); diff --git a/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch b/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch index 3d8a15bd1e..1b979f8662 100644 --- a/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch +++ b/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch @@ -19,7 +19,7 @@ Signed-off-by: Paolo Abeni --- 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; diff --git a/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch b/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch index b86dbea898..43e2c92ef0 100644 --- a/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch +++ b/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch @@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski --- 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; diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12 index 261166c6c3..dc836ccb10 100644 --- a/target/linux/generic/config-6.12 +++ b/target/linux/generic/config-6.12 @@ -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 diff --git a/target/linux/generic/config-6.6 b/target/linux/generic/config-6.6 index a7d06efed3..be73510dd0 100644 --- a/target/linux/generic/config-6.6 +++ b/target/linux/generic/config-6.6 @@ -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 diff --git a/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch index 4591a42f78..9b1f53af3e 100644 --- a/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch +++ b/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch @@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau + 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) + diff --git a/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch b/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch index 616db25890..cfcc720662 100644 --- a/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch +++ b/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch @@ -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/ diff --git a/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch index 4428ebbb5a..59cc16cb75 100644 --- a/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch +++ b/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch @@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau + 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_ diff --git a/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch b/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch index 946869c303..b142683a28 100644 --- a/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch +++ b/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch @@ -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/ diff --git a/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch index b8d20d8610..ed6c9070b2 100644 --- a/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch +++ b/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos --- 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; } diff --git a/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch index 9bb5737b39..a4cb71b83b 100644 --- a/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch +++ b/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch @@ -15,7 +15,7 @@ Signed-off-by: Daniel Golle --- 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 .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 .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 .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 .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 .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 .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)", diff --git a/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch index 4e07882b1e..186b3ff2b9 100644 --- a/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch @@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle Signed-off-by: Mieczyslaw Nalewaj --- 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 + } } - static int rtl8221b_match_phy_device(struct phy_device *phydev) + static int rtl8221b_match_phy_device(struct phy_device *phydev, diff --git a/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch b/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch index 855ea41c8c..e908af055f 100644 --- a/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch @@ -12,7 +12,7 @@ Signed-off-by: Jianhui Zhao --- 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 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 .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 .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 .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)", diff --git a/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch b/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch index e451d13bd8..d4920b5c97 100644 --- a/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch +++ b/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch @@ -38,7 +38,7 @@ Signed-off-by: Daniel Golle 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 .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, diff --git a/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch b/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch index f5621e34ee..7a52bd5521 100644 --- a/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch +++ b/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch @@ -24,7 +24,7 @@ Signed-off-by: David Bauer --- 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 diff --git a/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch index 453abe65c6..efa76572f8 100644 --- a/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch +++ b/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos --- 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; } diff --git a/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch index 1becba6da6..deb6506186 100644 --- a/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch +++ b/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch @@ -15,7 +15,7 @@ Signed-off-by: Daniel Golle --- 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 .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 .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 .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 .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 .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 .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 .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)", diff --git a/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch index 0918794d87..2886babe57 100644 --- a/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch @@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle Signed-off-by: Mieczyslaw Nalewaj --- 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 + } } - static int rtl8221b_match_phy_device(struct phy_device *phydev) + static int rtl8221b_match_phy_device(struct phy_device *phydev, diff --git a/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch b/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch index 9afe8baca6..29610d2767 100644 --- a/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch @@ -12,7 +12,7 @@ Signed-off-by: Jianhui Zhao --- 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 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 .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 .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 .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)", diff --git a/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch b/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch index 1749a74e36..1b6978547d 100644 --- a/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch +++ b/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch @@ -38,7 +38,7 @@ Signed-off-by: Daniel Golle 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 .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, diff --git a/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch b/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch index 80b69920e9..449b27e617 100644 --- a/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch +++ b/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch @@ -24,7 +24,7 @@ Signed-off-by: David Bauer --- 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 diff --git a/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch b/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch index 72a1464966..8f911f6fd8 100644 --- a/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch +++ b/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch @@ -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 diff --git a/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch b/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch index 079351b7a2..4ebaffd1dd 100644 --- a/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch +++ b/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch @@ -14,9 +14,9 @@ Signed-off-by: Robert Marko --- 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" diff --git a/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch b/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch index b0adf04a5b..d31215ec27 100644 --- a/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch +++ b/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch @@ -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. diff --git a/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch b/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch index 632aad0ed2..12638978b3 100644 --- a/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch +++ b/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch @@ -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); diff --git a/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c index 3b14e9bcfe..7d38070091 100644 --- a/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c +++ b/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c @@ -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; } diff --git a/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch b/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch index 9b054f96a8..ca411d6954 100644 --- a/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch +++ b/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch @@ -21,7 +21,7 @@ Submitted-by: John Crispin --- 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); diff --git a/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch b/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch index 3be218635e..4d1efd76b7 100644 --- a/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch +++ b/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch @@ -14,7 +14,7 @@ Submitted-by: Birger Koblitz --- 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 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/ diff --git a/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch b/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch index 55532bb5e5..98a6f07de8 100644 --- a/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch +++ b/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch @@ -170,7 +170,7 @@ Signed-off-by: Jakub Kicinski /* 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); diff --git a/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch b/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch index 4113d82326..745d2e8fb5 100644 --- a/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch +++ b/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch @@ -11,7 +11,7 @@ Signed-off-by: haoming.chen --- 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